feat(ultraplan-local): v1.6.0 — /ultraresearch-local deep research command
Add /ultraresearch-local for structured research combining local codebase analysis with external knowledge via parallel agent swarms. Produces research briefs with triangulation, confidence ratings, and source quality assessment. New command: /ultraresearch-local with modes --quick, --local, --external, --fg. New agents: research-orchestrator (opus), docs-researcher, community-researcher, security-researcher, contrarian-researcher, gemini-bridge (all sonnet). New template: research-brief-template.md. Integration: --research flag in /ultraplan-local accepts pre-built research briefs (up to 3), enriches the interview and exploration phases. Planning orchestrator cross-references brief findings during synthesis. Design principle: Context Engineering — right information to right agent at right time. Research briefs are structured artifacts in the pipeline: ultraresearch → brief → ultraplan --research → plan → ultraexecute. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
commit
baa2d0220b
488 changed files with 213221 additions and 0 deletions
301
scripts/skill-gen/expand-categories.sh
Executable file
301
scripts/skill-gen/expand-categories.sh
Executable file
|
|
@ -0,0 +1,301 @@
|
|||
#!/bin/bash
|
||||
# expand-categories.sh — Expand skill categories into full manifest
|
||||
#
|
||||
# Uses claude --print to expand each category in categories.json
|
||||
# into 15-25 individual skill titles, producing manifest.json
|
||||
#
|
||||
# Usage:
|
||||
# ./expand-categories.sh # Expand all categories
|
||||
# ./expand-categories.sh azure-ai-services # Expand single category
|
||||
# ./expand-categories.sh --wave 1 # Expand wave 1 only
|
||||
#
|
||||
# Prerequisites:
|
||||
# - claude CLI installed and authenticated
|
||||
# - jq installed
|
||||
# - categories.json in same directory
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PLUGIN_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
CATEGORIES_FILE="$SCRIPT_DIR/categories.json"
|
||||
MANIFEST_FILE="$SCRIPT_DIR/manifest.json"
|
||||
LOG_DIR="$SCRIPT_DIR/logs"
|
||||
|
||||
# Model for expansion (haiku is sufficient for generating titles)
|
||||
MODEL="${MODEL:-haiku}"
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log() { echo -e "${BLUE}[expand]${NC} $1" >&2; }
|
||||
success() { echo -e "${GREEN}[expand]${NC} $1" >&2; }
|
||||
warn() { echo -e "${YELLOW}[expand]${NC} $1" >&2; }
|
||||
error() { echo -e "${RED}[expand]${NC} $1" >&2; }
|
||||
|
||||
# Check prerequisites
|
||||
check_prereqs() {
|
||||
if ! command -v claude &>/dev/null; then
|
||||
error "claude CLI not found. Install: npm install -g @anthropic-ai/claude-code"
|
||||
exit 1
|
||||
fi
|
||||
if ! command -v jq &>/dev/null; then
|
||||
error "jq not found. Install: brew install jq"
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! -f "$CATEGORIES_FILE" ]]; then
|
||||
error "categories.json not found at $CATEGORIES_FILE"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Get list of existing reference files for context
|
||||
get_existing_refs() {
|
||||
local category_dir="$1"
|
||||
local refs_dir="$PLUGIN_DIR/skills/ms-ai-engineering/references"
|
||||
|
||||
# List all existing reference files
|
||||
find "$refs_dir" -name "*.md" -type f 2>/dev/null | while read -r f; do
|
||||
basename "$f" .md
|
||||
done | sort | tr '\n' ', '
|
||||
}
|
||||
|
||||
# Expand a single category
|
||||
expand_category() {
|
||||
local category_key="$1"
|
||||
|
||||
local name description estimated examples existing_overlap notes
|
||||
name=$(jq -r ".categories[\"$category_key\"].name" "$CATEGORIES_FILE")
|
||||
description=$(jq -r ".categories[\"$category_key\"].description" "$CATEGORIES_FILE")
|
||||
estimated=$(jq -r ".categories[\"$category_key\"].estimated_skills" "$CATEGORIES_FILE")
|
||||
examples=$(jq -r ".categories[\"$category_key\"].examples | join(\", \")" "$CATEGORIES_FILE")
|
||||
existing_overlap=$(jq -r ".categories[\"$category_key\"].existing_overlap | join(\", \")" "$CATEGORIES_FILE")
|
||||
notes=$(jq -r ".categories[\"$category_key\"].notes" "$CATEGORIES_FILE")
|
||||
|
||||
local existing_refs
|
||||
existing_refs=$(get_existing_refs "$category_key")
|
||||
|
||||
log "Expanding: $name ($estimated skills estimated)"
|
||||
|
||||
local prompt
|
||||
prompt="Du er en Microsoft AI Solution Architect som planlegger en kunnskapsbase.
|
||||
|
||||
Kategorien **${name}** trenger individuelle kunnskapsfiler (skills).
|
||||
|
||||
## Kategori-beskrivelse
|
||||
${description}
|
||||
|
||||
## Eksisterende filer i kunnskapsbasen (unngå duplikering)
|
||||
${existing_refs}
|
||||
|
||||
## Eksisterende overlapp å ta hensyn til
|
||||
${existing_overlap}
|
||||
|
||||
## Eksempel-titler (for inspirasjon, ikke begrens deg til disse)
|
||||
${examples}
|
||||
|
||||
## Notater
|
||||
${notes}
|
||||
|
||||
## Oppgave
|
||||
|
||||
Generer en JSON-array med NØYAKTIG ${estimated} skills for denne kategorien.
|
||||
|
||||
Hver skill skal ha:
|
||||
- \`id\`: kebab-case filnavn (uten .md)
|
||||
- \`title\`: Engelsk tittel (kortfattet, beskrivende)
|
||||
- \`description\`: 1-2 setninger på norsk om hva filen dekker
|
||||
- \`subtopics\`: 3-5 viktige undertemaer som array
|
||||
|
||||
Regler:
|
||||
1. Ikke dupliser emner som allerede finnes i eksisterende filer
|
||||
2. Sørg for bred dekning uten overlapp mellom skills
|
||||
3. Titler skal være spesifikke (\"Azure AI Vision OCR and Document Processing\", ikke bare \"Vision\")
|
||||
4. Prioriter mest nyttige emner for enterprise AI-arkitekter i norsk offentlig sektor
|
||||
5. Returner KUN valid JSON-array, ingen annen tekst
|
||||
|
||||
Eksempel-format:
|
||||
[
|
||||
{
|
||||
\"id\": \"example-skill-name\",
|
||||
\"title\": \"Example Skill - Full Descriptive Title\",
|
||||
\"description\": \"Beskrivelse av hva denne kunnskapsfilen dekker.\",
|
||||
\"subtopics\": [\"subtopic-1\", \"subtopic-2\", \"subtopic-3\"]
|
||||
}
|
||||
]"
|
||||
|
||||
local output
|
||||
output=$(claude --print --model "$MODEL" "$prompt" 2>"$LOG_DIR/expand-${category_key}.err")
|
||||
|
||||
# Extract JSON array from response (handles markdown code blocks, plain JSON, etc.)
|
||||
local json_output
|
||||
json_output=$(python3 -c "
|
||||
import sys, json, re
|
||||
text = sys.stdin.read()
|
||||
# Try to find JSON array in code blocks first
|
||||
m = re.search(r'\`\`\`(?:json)?\s*(\[[\s\S]*?\])\s*\`\`\`', text)
|
||||
if m:
|
||||
arr = json.loads(m.group(1))
|
||||
print(json.dumps(arr))
|
||||
sys.exit(0)
|
||||
# Try to find bare JSON array
|
||||
m = re.search(r'(\[[\s\S]*\])', text)
|
||||
if m:
|
||||
try:
|
||||
arr = json.loads(m.group(1))
|
||||
print(json.dumps(arr))
|
||||
sys.exit(0)
|
||||
except: pass
|
||||
# Nothing found
|
||||
sys.exit(1)
|
||||
" <<< "$output" 2>/dev/null)
|
||||
|
||||
# Validate JSON
|
||||
if ! echo "$json_output" | jq . &>/dev/null; then
|
||||
error "Invalid JSON for $category_key. Raw output saved to $LOG_DIR/expand-${category_key}.raw"
|
||||
echo "$output" > "$LOG_DIR/expand-${category_key}.raw"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local count
|
||||
count=$(echo "$json_output" | jq 'length')
|
||||
success "$name: $count skills generated"
|
||||
|
||||
# Return the JSON
|
||||
echo "$json_output"
|
||||
}
|
||||
|
||||
# Build or update manifest
|
||||
build_manifest() {
|
||||
local categories_to_expand=("$@")
|
||||
|
||||
# Initialize manifest if it doesn't exist
|
||||
if [[ ! -f "$MANIFEST_FILE" ]]; then
|
||||
echo '{"version":"1.0","created":"'"$(date +%Y-%m-%d)"'","categories":{}}' | jq . > "$MANIFEST_FILE"
|
||||
fi
|
||||
|
||||
local total=0
|
||||
local failed=0
|
||||
|
||||
for category_key in "${categories_to_expand[@]}"; do
|
||||
local skills_json
|
||||
if skills_json=$(expand_category "$category_key"); then
|
||||
# Add to manifest
|
||||
local dir
|
||||
dir=$(jq -r ".categories[\"$category_key\"].dir" "$CATEGORIES_FILE")
|
||||
local name
|
||||
name=$(jq -r ".categories[\"$category_key\"].name" "$CATEGORIES_FILE")
|
||||
local priority
|
||||
priority=$(jq -r ".categories[\"$category_key\"].priority" "$CATEGORIES_FILE")
|
||||
|
||||
local category_obj
|
||||
category_obj=$(jq -n \
|
||||
--arg name "$name" \
|
||||
--arg dir "$dir" \
|
||||
--arg priority "$priority" \
|
||||
--argjson skills "$skills_json" \
|
||||
'{name: $name, dir: $dir, priority: $priority, skills: $skills}')
|
||||
|
||||
# Merge into manifest
|
||||
jq --arg key "$category_key" --argjson cat "$category_obj" \
|
||||
'.categories[$key] = $cat' "$MANIFEST_FILE" > "$MANIFEST_FILE.tmp" \
|
||||
&& mv "$MANIFEST_FILE.tmp" "$MANIFEST_FILE"
|
||||
|
||||
local count
|
||||
count=$(echo "$skills_json" | jq 'length')
|
||||
total=$((total + count))
|
||||
else
|
||||
failed=$((failed + 1))
|
||||
fi
|
||||
|
||||
# Rate limiting: pause between API calls
|
||||
sleep 2
|
||||
done
|
||||
|
||||
echo "" >&2
|
||||
log "═══════════════════════════════════════"
|
||||
success "Total skills in manifest: $total"
|
||||
[[ $failed -gt 0 ]] && error "Failed categories: $failed"
|
||||
log "Manifest: $MANIFEST_FILE"
|
||||
log "═══════════════════════════════════════"
|
||||
}
|
||||
|
||||
# Parse arguments
|
||||
parse_args() {
|
||||
local wave=""
|
||||
local single_category=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--wave)
|
||||
wave="$2"
|
||||
shift 2
|
||||
;;
|
||||
--model)
|
||||
MODEL="$2"
|
||||
shift 2
|
||||
;;
|
||||
--help|-h)
|
||||
echo "Usage: $0 [category-key] [--wave N] [--model MODEL]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " category-key Expand single category"
|
||||
echo " --wave N Expand all categories in wave N (1 or 2)"
|
||||
echo " --model MODEL Claude model to use (default: haiku)"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " $0 # Expand all categories"
|
||||
echo " $0 azure-ai-services # Expand single category"
|
||||
echo " $0 --wave 1 # Expand HIGH priority only"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
single_category="$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Determine which categories to expand
|
||||
if [[ -n "$single_category" ]]; then
|
||||
echo "$single_category"
|
||||
elif [[ -n "$wave" ]]; then
|
||||
jq -r ".waves[] | select(.wave == $wave) | .categories[]" "$CATEGORIES_FILE"
|
||||
else
|
||||
jq -r '.categories | keys[]' "$CATEGORIES_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
# Main
|
||||
main() {
|
||||
check_prereqs
|
||||
mkdir -p "$LOG_DIR"
|
||||
|
||||
log "Skill Category Expansion"
|
||||
log "Model: $MODEL | Categories file: $CATEGORIES_FILE"
|
||||
echo "" >&2
|
||||
|
||||
local categories=()
|
||||
while IFS= read -r line; do
|
||||
categories+=("$line")
|
||||
done < <(parse_args "$@")
|
||||
|
||||
if [[ ${#categories[@]} -eq 0 ]]; then
|
||||
error "No categories to expand"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Categories to expand: ${#categories[@]}"
|
||||
for cat in "${categories[@]}"; do
|
||||
log " - $cat"
|
||||
done
|
||||
echo "" >&2
|
||||
|
||||
build_manifest "${categories[@]}"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Loading…
Add table
Add a link
Reference in a new issue