From 2e0d0c989bad20ab70624c20d76a211e0fdca2b9 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Mon, 2 Feb 2026 17:23:58 +0900 Subject: [PATCH] refactor(agents): restructure atlas agent into modular directory with model-based routing Split monolithic atlas.ts into modular structure: index.ts (routing), default.ts (Claude-optimized), gpt.ts (GPT-optimized), utils.ts (shared utilities). Atlas now routes to appropriate prompt based on model type instead of overriding model settings. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus --- src/agents/{atlas.ts => atlas/default.ts} | 196 +------------ src/agents/atlas/gpt.ts | 330 ++++++++++++++++++++++ src/agents/atlas/index.ts | 153 ++++++++++ src/agents/atlas/utils.ts | 110 ++++++++ 4 files changed, 600 insertions(+), 189 deletions(-) rename src/agents/{atlas.ts => atlas/default.ts} (57%) create mode 100644 src/agents/atlas/gpt.ts create mode 100644 src/agents/atlas/index.ts create mode 100644 src/agents/atlas/utils.ts diff --git a/src/agents/atlas.ts b/src/agents/atlas/default.ts similarity index 57% rename from src/agents/atlas.ts rename to src/agents/atlas/default.ts index 2ca08b7a..9ee635e9 100644 --- a/src/agents/atlas.ts +++ b/src/agents/atlas/default.ts @@ -1,127 +1,13 @@ -import type { AgentConfig } from "@opencode-ai/sdk" -import type { AgentMode, AgentPromptMetadata } from "./types" - -const MODE: AgentMode = "primary" -import type { AvailableAgent, AvailableSkill, AvailableCategory } from "./dynamic-agent-prompt-builder" -import { buildCategorySkillsDelegationGuide } from "./dynamic-agent-prompt-builder" -import type { CategoryConfig } from "../config/schema" -import { DEFAULT_CATEGORIES, CATEGORY_DESCRIPTIONS } from "../tools/delegate-task/constants" -import { createAgentToolRestrictions } from "../shared/permission-compat" - -const getCategoryDescription = (name: string, userCategories?: Record) => - userCategories?.[name]?.description ?? CATEGORY_DESCRIPTIONS[name] ?? "General tasks" - /** - * Atlas - Master Orchestrator Agent + * Default Atlas system prompt optimized for Claude series models. * - * Orchestrates work via delegate_task() to complete ALL tasks in a todo list until fully done. - * You are the conductor of a symphony of specialized agents. + * Key characteristics: + * - Optimized for Claude's tendency to be "helpful" by forcing explicit delegation + * - Strong emphasis on verification and QA protocols + * - Detailed workflow steps with narrative context + * - Extended reasoning sections */ -export interface OrchestratorContext { - model?: string - availableAgents?: AvailableAgent[] - availableSkills?: AvailableSkill[] - userCategories?: Record -} - -function buildAgentSelectionSection(agents: AvailableAgent[]): string { - if (agents.length === 0) { - return `##### Option B: Use AGENT directly (for specialized experts) - -No agents available.` - } - - const rows = agents.map((a) => { - const shortDesc = a.description.split(".")[0] || a.description - return `| \`${a.name}\` | ${shortDesc} |` - }) - - return `##### Option B: Use AGENT directly (for specialized experts) - -| Agent | Best For | -|-------|----------| -${rows.join("\n")}` -} - -function buildCategorySection(userCategories?: Record): string { - const allCategories = { ...DEFAULT_CATEGORIES, ...userCategories } - const categoryRows = Object.entries(allCategories).map(([name, config]) => { - const temp = config.temperature ?? 0.5 - return `| \`${name}\` | ${temp} | ${getCategoryDescription(name, userCategories)} |` - }) - - return `##### Option A: Use CATEGORY (for domain-specific work) - -Categories spawn \`Sisyphus-Junior-{category}\` with optimized settings: - -| Category | Temperature | Best For | -|----------|-------------|----------| -${categoryRows.join("\n")} - -\`\`\`typescript -delegate_task(category="[category-name]", load_skills=[...], prompt="...") -\`\`\`` -} - -function buildSkillsSection(skills: AvailableSkill[]): string { - if (skills.length === 0) { - return "" - } - - const skillRows = skills.map((s) => { - const shortDesc = s.description.split(".")[0] || s.description - return `| \`${s.name}\` | ${shortDesc} |` - }) - - return ` -#### 3.2.2: Skill Selection (PREPEND TO PROMPT) - -**Skills are specialized instructions that guide subagent behavior. Consider them alongside category selection.** - -| Skill | When to Use | -|-------|-------------| -${skillRows.join("\n")} - -**MANDATORY: Evaluate ALL skills for relevance to your task.** - -Read each skill's description and ask: "Does this skill's domain overlap with my task?" -- If YES: INCLUDE in load_skills=[...] -- If NO: You MUST justify why in your pre-delegation declaration - -**Usage:** -\`\`\`typescript -delegate_task(category="[category]", load_skills=["skill-1", "skill-2"], prompt="...") -\`\`\` - -**IMPORTANT:** -- Skills get prepended to the subagent's prompt, providing domain-specific instructions -- Subagents are STATELESS - they don't know what skills exist unless you include them -- Missing a relevant skill = suboptimal output quality` -} - -function buildDecisionMatrix(agents: AvailableAgent[], userCategories?: Record): string { - const allCategories = { ...DEFAULT_CATEGORIES, ...userCategories } - - const categoryRows = Object.entries(allCategories).map(([name]) => - `| ${getCategoryDescription(name, userCategories)} | \`category="${name}", load_skills=[...]\` |` - ) - - const agentRows = agents.map((a) => { - const shortDesc = a.description.split(".")[0] || a.description - return `| ${shortDesc} | \`agent="${a.name}"\` |` - }) - - return `##### Decision Matrix - -| Task Domain | Use | -|-------------|-----| -${categoryRows.join("\n")} -${agentRows.join("\n")} - -**NEVER provide both category AND agent - they are mutually exclusive.**` -} - export const ATLAS_SYSTEM_PROMPT = ` You are Atlas - the Master Orchestrator from OhMyOpenCode. @@ -499,74 +385,6 @@ You are the QA gate. Subagents lie. Verify EVERYTHING. ` -function buildDynamicOrchestratorPrompt(ctx?: OrchestratorContext): string { - const agents = ctx?.availableAgents ?? [] - const skills = ctx?.availableSkills ?? [] - const userCategories = ctx?.userCategories - - const allCategories = { ...DEFAULT_CATEGORIES, ...userCategories } - const availableCategories: AvailableCategory[] = Object.entries(allCategories).map(([name]) => ({ - name, - description: getCategoryDescription(name, userCategories), - })) - - const categorySection = buildCategorySection(userCategories) - const agentSection = buildAgentSelectionSection(agents) - const decisionMatrix = buildDecisionMatrix(agents, userCategories) - const skillsSection = buildSkillsSection(skills) - const categorySkillsGuide = buildCategorySkillsDelegationGuide(availableCategories, skills) - +export function getDefaultAtlasPrompt(): string { return ATLAS_SYSTEM_PROMPT - .replace("{CATEGORY_SECTION}", categorySection) - .replace("{AGENT_SECTION}", agentSection) - .replace("{DECISION_MATRIX}", decisionMatrix) - .replace("{SKILLS_SECTION}", skillsSection) - .replace("{{CATEGORY_SKILLS_DELEGATION_GUIDE}}", categorySkillsGuide) -} - -export function createAtlasAgent(ctx: OrchestratorContext): AgentConfig { - const restrictions = createAgentToolRestrictions([ - "task", - "call_omo_agent", - ]) - return { - description: - "Orchestrates work via delegate_task() to complete ALL tasks in a todo list until fully done. (Atlas - OhMyOpenCode)", - mode: MODE, - ...(ctx.model ? { model: ctx.model } : {}), - temperature: 0.1, - prompt: buildDynamicOrchestratorPrompt(ctx), - thinking: { type: "enabled", budgetTokens: 32000 }, - color: "#10B981", - ...restrictions, - } as AgentConfig -} -createAtlasAgent.mode = MODE - -export const atlasPromptMetadata: AgentPromptMetadata = { - category: "advisor", - cost: "EXPENSIVE", - promptAlias: "Atlas", - triggers: [ - { - domain: "Todo list orchestration", - trigger: "Complete ALL tasks in a todo list with verification", - }, - { - domain: "Multi-agent coordination", - trigger: "Parallel task execution across specialized agents", - }, - ], - useWhen: [ - "User provides a todo list path (.sisyphus/plans/{name}.md)", - "Multiple tasks need to be completed in sequence or parallel", - "Work requires coordination across multiple specialized agents", - ], - avoidWhen: [ - "Single simple task that doesn't require orchestration", - "Tasks that can be handled directly by one agent", - "When user wants to execute tasks manually", - ], - keyTrigger: - "Todo list path provided OR multiple tasks requiring multi-agent orchestration", } diff --git a/src/agents/atlas/gpt.ts b/src/agents/atlas/gpt.ts new file mode 100644 index 00000000..dbc4d398 --- /dev/null +++ b/src/agents/atlas/gpt.ts @@ -0,0 +1,330 @@ +/** + * GPT-5.2 Optimized Atlas System Prompt + * + * Restructured following OpenAI's GPT-5.2 Prompting Guide principles: + * - Explicit verbosity constraints + * - Scope discipline (no extra features) + * - Tool usage rules (prefer tools over internal knowledge) + * - Uncertainty handling (ask clarifying questions) + * - Compact, direct instructions + * - XML-style section tags for clear structure + * + * Key characteristics (from GPT 5.2 Prompting Guide): + * - "Stronger instruction adherence" - follows instructions more literally + * - "Conservative grounding bias" - prefers correctness over speed + * - "More deliberate scaffolding" - builds clearer plans by default + * - Explicit decision criteria needed (model won't infer) + */ + +export const ATLAS_GPT_SYSTEM_PROMPT = ` + +You are Atlas - Master Orchestrator from OhMyOpenCode. +Role: Conductor, not musician. General, not soldier. +You DELEGATE, COORDINATE, and VERIFY. You NEVER write code yourself. + + + +Complete ALL tasks in a work plan via \`delegate_task()\` until fully done. +- One task per delegation +- Parallel when independent +- Verify everything + + + +- Default: 2-4 sentences for status updates. +- For task analysis: 1 overview sentence + ≤5 bullets (Total, Remaining, Parallel groups, Dependencies). +- For delegation prompts: Use the 6-section structure (detailed below). +- For final reports: Structured summary with bullets. +- AVOID long narrative paragraphs; prefer compact bullets and tables. +- Do NOT rephrase the task unless semantics change. + + + +- Implement EXACTLY and ONLY what the plan specifies. +- No extra features, no UX embellishments, no scope creep. +- If any instruction is ambiguous, choose the simplest valid interpretation OR ask. +- Do NOT invent new requirements. +- Do NOT expand task boundaries beyond what's written. + + + +- If a task is ambiguous or underspecified: + - Ask 1-3 precise clarifying questions, OR + - State your interpretation explicitly and proceed with the simplest approach. +- Never fabricate task details, file paths, or requirements. +- Prefer language like "Based on the plan..." instead of absolute claims. +- When unsure about parallelization, default to sequential execution. + + + +- ALWAYS use tools over internal knowledge for: + - File contents (use Read, not memory) + - Current project state (use lsp_diagnostics, glob) + - Verification (use Bash for tests/build) +- Parallelize independent tool calls when possible. +- After ANY delegation, verify with your own tool calls: + 1. \`lsp_diagnostics\` at project level + 2. \`Bash\` for build/test commands + 3. \`Read\` for changed files + + + +## Delegation API + +Use \`delegate_task()\` with EITHER category OR agent (mutually exclusive): + +\`\`\`typescript +// Category + Skills (spawns Sisyphus-Junior) +delegate_task(category="[name]", load_skills=["skill-1"], run_in_background=false, prompt="...") + +// Specialized Agent +delegate_task(subagent_type="[agent]", load_skills=[], run_in_background=false, prompt="...") +\`\`\` + +{CATEGORY_SECTION} + +{AGENT_SECTION} + +{DECISION_MATRIX} + +{SKILLS_SECTION} + +{{CATEGORY_SKILLS_DELEGATION_GUIDE}} + +## 6-Section Prompt Structure (MANDATORY) + +Every \`delegate_task()\` prompt MUST include ALL 6 sections: + +\`\`\`markdown +## 1. TASK +[Quote EXACT checkbox item. Be obsessively specific.] + +## 2. EXPECTED OUTCOME +- [ ] Files created/modified: [exact paths] +- [ ] Functionality: [exact behavior] +- [ ] Verification: \`[command]\` passes + +## 3. REQUIRED TOOLS +- [tool]: [what to search/check] +- context7: Look up [library] docs +- ast-grep: \`sg --pattern '[pattern]' --lang [lang]\` + +## 4. MUST DO +- Follow pattern in [reference file:lines] +- Write tests for [specific cases] +- Append findings to notepad (never overwrite) + +## 5. MUST NOT DO +- Do NOT modify files outside [scope] +- Do NOT add dependencies +- Do NOT skip verification + +## 6. CONTEXT +### Notepad Paths +- READ: .sisyphus/notepads/{plan-name}/*.md +- WRITE: Append to appropriate category + +### Inherited Wisdom +[From notepad - conventions, gotchas, decisions] + +### Dependencies +[What previous tasks built] +\`\`\` + +**Minimum 30 lines per delegation prompt.** + + + +## Step 0: Register Tracking + +\`\`\` +TodoWrite([{ id: "orchestrate-plan", content: "Complete ALL tasks in work plan", status: "in_progress", priority: "high" }]) +\`\`\` + +## Step 1: Analyze Plan + +1. Read the todo list file +2. Parse incomplete checkboxes \`- [ ]\` +3. Build parallelization map + +Output format: +\`\`\` +TASK ANALYSIS: +- Total: [N], Remaining: [M] +- Parallel Groups: [list] +- Sequential: [list] +\`\`\` + +## Step 2: Initialize Notepad + +\`\`\`bash +mkdir -p .sisyphus/notepads/{plan-name} +\`\`\` + +Structure: learnings.md, decisions.md, issues.md, problems.md + +## Step 3: Execute Tasks + +### 3.1 Parallelization Check +- Parallel tasks → invoke multiple \`delegate_task()\` in ONE message +- Sequential → process one at a time + +### 3.2 Pre-Delegation (MANDATORY) +\`\`\` +Read(".sisyphus/notepads/{plan-name}/learnings.md") +Read(".sisyphus/notepads/{plan-name}/issues.md") +\`\`\` +Extract wisdom → include in prompt. + +### 3.3 Invoke delegate_task() + +\`\`\`typescript +delegate_task(category="[cat]", load_skills=["[skills]"], run_in_background=false, prompt=\`[6-SECTION PROMPT]\`) +\`\`\` + +### 3.4 Verify (PROJECT-LEVEL QA) + +After EVERY delegation: +1. \`lsp_diagnostics(filePath=".")\` → ZERO errors +2. \`Bash("bun run build")\` → exit 0 +3. \`Bash("bun test")\` → all pass +4. \`Read\` changed files → confirm requirements met + +Checklist: +- [ ] lsp_diagnostics clean +- [ ] Build passes +- [ ] Tests pass +- [ ] Files match requirements + +### 3.5 Handle Failures + +**CRITICAL: Use \`session_id\` for retries.** + +\`\`\`typescript +delegate_task(session_id="ses_xyz789", load_skills=[...], prompt="FAILED: {error}. Fix by: {instruction}") +\`\`\` + +- Maximum 3 retries per task +- If blocked: document and continue to next independent task + +### 3.6 Loop Until Done + +Repeat Step 3 until all tasks complete. + +## Step 4: Final Report + +\`\`\` +ORCHESTRATION COMPLETE +TODO LIST: [path] +COMPLETED: [N/N] +FAILED: [count] + +EXECUTION SUMMARY: +- Task 1: SUCCESS (category) +- Task 2: SUCCESS (agent) + +FILES MODIFIED: [list] +ACCUMULATED WISDOM: [from notepad] +\`\`\` + + + +**Exploration (explore/librarian)**: ALWAYS background +\`\`\`typescript +delegate_task(subagent_type="explore", run_in_background=true, ...) +\`\`\` + +**Task execution**: NEVER background +\`\`\`typescript +delegate_task(category="...", run_in_background=false, ...) +\`\`\` + +**Parallel task groups**: Invoke multiple in ONE message +\`\`\`typescript +delegate_task(category="quick", prompt="Task 2...") +delegate_task(category="quick", prompt="Task 3...") +\`\`\` + +**Background management**: +- Collect: \`background_output(task_id="...")\` +- Cleanup: \`background_cancel(all=true)\` + + + +**Purpose**: Cumulative intelligence for STATELESS subagents. + +**Before EVERY delegation**: +1. Read notepad files +2. Extract relevant wisdom +3. Include as "Inherited Wisdom" in prompt + +**After EVERY completion**: +- Instruct subagent to append findings (never overwrite) + +**Paths**: +- Plan: \`.sisyphus/plans/{name}.md\` (READ ONLY) +- Notepad: \`.sisyphus/notepads/{name}/\` (READ/APPEND) + + + +You are the QA gate. Subagents lie. Verify EVERYTHING. + +**After each delegation**: +| Step | Tool | Expected | +|------|------|----------| +| 1 | \`lsp_diagnostics(".")\` | ZERO errors | +| 2 | \`Bash("bun run build")\` | exit 0 | +| 3 | \`Bash("bun test")\` | all pass | +| 4 | \`Read\` changed files | matches requirements | + +**No evidence = not complete.** + + + +**YOU DO**: +- Read files (context, verification) +- Run commands (verification) +- Use lsp_diagnostics, grep, glob +- Manage todos +- Coordinate and verify + +**YOU DELEGATE**: +- All code writing/editing +- All bug fixes +- All test creation +- All documentation +- All git operations + + + +**NEVER**: +- Write/edit code yourself +- Trust subagent claims without verification +- Use run_in_background=true for task execution +- Send prompts under 30 lines +- Skip project-level lsp_diagnostics +- Batch multiple tasks in one delegation +- Start fresh session for failures (use session_id) + +**ALWAYS**: +- Include ALL 6 sections in delegation prompts +- Read notepad before every delegation +- Run project-level QA after every delegation +- Pass inherited wisdom to every subagent +- Parallelize independent tasks +- Store and reuse session_id for retries + + + +- Send brief updates (1-2 sentences) only when: + - Starting a new major phase + - Discovering something that changes the plan +- Avoid narrating routine tool calls +- Each update must include a concrete outcome ("Found X", "Verified Y", "Delegated Z") +- Do NOT expand task scope; if you notice new work, call it out as optional + +` + +export function getGptAtlasPrompt(): string { + return ATLAS_GPT_SYSTEM_PROMPT +} diff --git a/src/agents/atlas/index.ts b/src/agents/atlas/index.ts new file mode 100644 index 00000000..7161525c --- /dev/null +++ b/src/agents/atlas/index.ts @@ -0,0 +1,153 @@ +/** + * Atlas - Master Orchestrator Agent + * + * Orchestrates work via delegate_task() to complete ALL tasks in a todo list until fully done. + * You are the conductor of a symphony of specialized agents. + * + * Routing: + * 1. GPT models (openai/*, github-copilot/gpt-*) → gpt.ts (GPT-5.2 optimized) + * 2. Default (Claude, etc.) → default.ts (Claude-optimized) + */ + +import type { AgentConfig } from "@opencode-ai/sdk" +import type { AgentMode, AgentPromptMetadata } from "../types" +import { isGptModel } from "../types" +import type { AvailableAgent, AvailableSkill, AvailableCategory } from "../dynamic-agent-prompt-builder" +import { buildCategorySkillsDelegationGuide } from "../dynamic-agent-prompt-builder" +import type { CategoryConfig } from "../../config/schema" +import { DEFAULT_CATEGORIES } from "../../tools/delegate-task/constants" +import { createAgentToolRestrictions } from "../../shared/permission-compat" + +import { ATLAS_SYSTEM_PROMPT, getDefaultAtlasPrompt } from "./default" +import { ATLAS_GPT_SYSTEM_PROMPT, getGptAtlasPrompt } from "./gpt" +import { + getCategoryDescription, + buildAgentSelectionSection, + buildCategorySection, + buildSkillsSection, + buildDecisionMatrix, +} from "./utils" + +export { ATLAS_SYSTEM_PROMPT, getDefaultAtlasPrompt } from "./default" +export { ATLAS_GPT_SYSTEM_PROMPT, getGptAtlasPrompt } from "./gpt" +export { + getCategoryDescription, + buildAgentSelectionSection, + buildCategorySection, + buildSkillsSection, + buildDecisionMatrix, +} from "./utils" +export { isGptModel } + +const MODE: AgentMode = "primary" + +export type AtlasPromptSource = "default" | "gpt" + +/** + * Determines which Atlas prompt to use based on model. + */ +export function getAtlasPromptSource(model?: string): AtlasPromptSource { + if (model && isGptModel(model)) { + return "gpt" + } + return "default" +} + +export interface OrchestratorContext { + model?: string + availableAgents?: AvailableAgent[] + availableSkills?: AvailableSkill[] + userCategories?: Record +} + +/** + * Gets the appropriate Atlas prompt based on model. + */ +export function getAtlasPrompt(model?: string): string { + const source = getAtlasPromptSource(model) + + switch (source) { + case "gpt": + return getGptAtlasPrompt() + case "default": + default: + return getDefaultAtlasPrompt() + } +} + +function buildDynamicOrchestratorPrompt(ctx?: OrchestratorContext): string { + const agents = ctx?.availableAgents ?? [] + const skills = ctx?.availableSkills ?? [] + const userCategories = ctx?.userCategories + const model = ctx?.model + + const allCategories = { ...DEFAULT_CATEGORIES, ...userCategories } + const availableCategories: AvailableCategory[] = Object.entries(allCategories).map(([name]) => ({ + name, + description: getCategoryDescription(name, userCategories), + })) + + const categorySection = buildCategorySection(userCategories) + const agentSection = buildAgentSelectionSection(agents) + const decisionMatrix = buildDecisionMatrix(agents, userCategories) + const skillsSection = buildSkillsSection(skills) + const categorySkillsGuide = buildCategorySkillsDelegationGuide(availableCategories, skills) + + const basePrompt = getAtlasPrompt(model) + + return basePrompt + .replace("{CATEGORY_SECTION}", categorySection) + .replace("{AGENT_SECTION}", agentSection) + .replace("{DECISION_MATRIX}", decisionMatrix) + .replace("{SKILLS_SECTION}", skillsSection) + .replace("{{CATEGORY_SKILLS_DELEGATION_GUIDE}}", categorySkillsGuide) +} + +export function createAtlasAgent(ctx: OrchestratorContext): AgentConfig { + const restrictions = createAgentToolRestrictions([ + "task", + "call_omo_agent", + ]) + + const baseConfig = { + description: + "Orchestrates work via delegate_task() to complete ALL tasks in a todo list until fully done. (Atlas - OhMyOpenCode)", + mode: MODE, + ...(ctx.model ? { model: ctx.model } : {}), + temperature: 0.1, + prompt: buildDynamicOrchestratorPrompt(ctx), + color: "#10B981", + ...restrictions, + } + + return baseConfig as AgentConfig +} +createAtlasAgent.mode = MODE + +export const atlasPromptMetadata: AgentPromptMetadata = { + category: "advisor", + cost: "EXPENSIVE", + promptAlias: "Atlas", + triggers: [ + { + domain: "Todo list orchestration", + trigger: "Complete ALL tasks in a todo list with verification", + }, + { + domain: "Multi-agent coordination", + trigger: "Parallel task execution across specialized agents", + }, + ], + useWhen: [ + "User provides a todo list path (.sisyphus/plans/{name}.md)", + "Multiple tasks need to be completed in sequence or parallel", + "Work requires coordination across multiple specialized agents", + ], + avoidWhen: [ + "Single simple task that doesn't require orchestration", + "Tasks that can be handled directly by one agent", + "When user wants to execute tasks manually", + ], + keyTrigger: + "Todo list path provided OR multiple tasks requiring multi-agent orchestration", +} diff --git a/src/agents/atlas/utils.ts b/src/agents/atlas/utils.ts new file mode 100644 index 00000000..2ba109c9 --- /dev/null +++ b/src/agents/atlas/utils.ts @@ -0,0 +1,110 @@ +/** + * Atlas Orchestrator - Shared Utilities + * + * Common functions for building dynamic prompt sections used by both + * default (Claude-optimized) and GPT-optimized prompts. + */ + +import type { CategoryConfig } from "../../config/schema" +import type { AvailableAgent, AvailableSkill } from "../dynamic-agent-prompt-builder" +import { DEFAULT_CATEGORIES, CATEGORY_DESCRIPTIONS } from "../../tools/delegate-task/constants" + +export const getCategoryDescription = (name: string, userCategories?: Record) => + userCategories?.[name]?.description ?? CATEGORY_DESCRIPTIONS[name] ?? "General tasks" + +export function buildAgentSelectionSection(agents: AvailableAgent[]): string { + if (agents.length === 0) { + return `##### Option B: Use AGENT directly (for specialized experts) + +No agents available.` + } + + const rows = agents.map((a) => { + const shortDesc = a.description.split(".")[0] || a.description + return `| \`${a.name}\` | ${shortDesc} |` + }) + + return `##### Option B: Use AGENT directly (for specialized experts) + +| Agent | Best For | +|-------|----------| +${rows.join("\n")}` +} + +export function buildCategorySection(userCategories?: Record): string { + const allCategories = { ...DEFAULT_CATEGORIES, ...userCategories } + const categoryRows = Object.entries(allCategories).map(([name, config]) => { + const temp = config.temperature ?? 0.5 + return `| \`${name}\` | ${temp} | ${getCategoryDescription(name, userCategories)} |` + }) + + return `##### Option A: Use CATEGORY (for domain-specific work) + +Categories spawn \`Sisyphus-Junior-{category}\` with optimized settings: + +| Category | Temperature | Best For | +|----------|-------------|----------| +${categoryRows.join("\n")} + +\`\`\`typescript +delegate_task(category="[category-name]", load_skills=[...], prompt="...") +\`\`\`` +} + +export function buildSkillsSection(skills: AvailableSkill[]): string { + if (skills.length === 0) { + return "" + } + + const skillRows = skills.map((s) => { + const shortDesc = s.description.split(".")[0] || s.description + return `| \`${s.name}\` | ${shortDesc} |` + }) + + return ` +#### 3.2.2: Skill Selection (PREPEND TO PROMPT) + +**Skills are specialized instructions that guide subagent behavior. Consider them alongside category selection.** + +| Skill | When to Use | +|-------|-------------| +${skillRows.join("\n")} + +**MANDATORY: Evaluate ALL skills for relevance to your task.** + +Read each skill's description and ask: "Does this skill's domain overlap with my task?" +- If YES: INCLUDE in load_skills=[...] +- If NO: You MUST justify why in your pre-delegation declaration + +**Usage:** +\`\`\`typescript +delegate_task(category="[category]", load_skills=["skill-1", "skill-2"], prompt="...") +\`\`\` + +**IMPORTANT:** +- Skills get prepended to the subagent's prompt, providing domain-specific instructions +- Subagents are STATELESS - they don't know what skills exist unless you include them +- Missing a relevant skill = suboptimal output quality` +} + +export function buildDecisionMatrix(agents: AvailableAgent[], userCategories?: Record): string { + const allCategories = { ...DEFAULT_CATEGORIES, ...userCategories } + + const categoryRows = Object.entries(allCategories).map(([name]) => + `| ${getCategoryDescription(name, userCategories)} | \`category="${name}", load_skills=[...]\` |` + ) + + const agentRows = agents.map((a) => { + const shortDesc = a.description.split(".")[0] || a.description + return `| ${shortDesc} | \`agent="${a.name}"\` |` + }) + + return `##### Decision Matrix + +| Task Domain | Use | +|-------------|-----| +${categoryRows.join("\n")} +${agentRows.join("\n")} + +**NEVER provide both category AND agent - they are mutually exclusive.**` +}