From c6efe70f09918df9830692f4a5025dfadfd96dae Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Tue, 30 Dec 2025 23:56:09 +0900 Subject: [PATCH] feat(agents): implement dynamic Sisyphus prompt system with agent metadata MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a new dynamic prompt generation system for Sisyphus orchestrator that leverages agent metadata for intelligent delegation. This revives the dynamic-sisyphus-agent-prompt branch with comprehensive refactoring. Changes: - Add AgentPromptMetadata, AgentCategory, AgentCost, DelegationTrigger types - Create sisyphus-prompt-builder with dynamic prompt generation logic - Add AGENT_PROMPT_METADATA exports to all agent modules (oracle, librarian, explore, frontend-ui-ux-engineer, document-writer, multimodal-looker) - Refactor sisyphus.ts to use buildDynamicSisyphusPrompt() - Add AvailableAgent type export for type safety This enables Sisyphus to make intelligent agent selection decisions based on agent capabilities, costs, and delegation triggers, improving orchestration efficiency. πŸ€– Generated with assistance of OhMyOpenCode (https://github.com/code-yeongyu/oh-my-opencode) --- src/agents/document-writer.ts | 10 + src/agents/explore.ts | 21 ++ src/agents/frontend-ui-ux-engineer.ts | 16 ++ src/agents/index.ts | 1 + src/agents/librarian.ts | 18 ++ src/agents/multimodal-looker.ts | 8 + src/agents/oracle.ts | 27 +++ src/agents/sisyphus-prompt-builder.ts | 257 +++++++++++++++++++++ src/agents/sisyphus.ts | 317 +++++++++++--------------- src/agents/types.ts | 50 ++++ src/index.ts | 31 ++- 11 files changed, 565 insertions(+), 191 deletions(-) create mode 100644 src/agents/sisyphus-prompt-builder.ts diff --git a/src/agents/document-writer.ts b/src/agents/document-writer.ts index 76e27e7..753173b 100644 --- a/src/agents/document-writer.ts +++ b/src/agents/document-writer.ts @@ -1,7 +1,17 @@ import type { AgentConfig } from "@opencode-ai/sdk" +import type { AgentPromptMetadata } from "./types" const DEFAULT_MODEL = "google/gemini-3-flash-preview" +export const DOCUMENT_WRITER_PROMPT_METADATA: AgentPromptMetadata = { + category: "specialist", + cost: "CHEAP", + promptAlias: "Document Writer", + triggers: [ + { domain: "Documentation", trigger: "README, API docs, guides" }, + ], +} + export function createDocumentWriterAgent( model: string = DEFAULT_MODEL ): AgentConfig { diff --git a/src/agents/explore.ts b/src/agents/explore.ts index ba6b704..cb2ed62 100644 --- a/src/agents/explore.ts +++ b/src/agents/explore.ts @@ -1,7 +1,28 @@ import type { AgentConfig } from "@opencode-ai/sdk" +import type { AgentPromptMetadata } from "./types" const DEFAULT_MODEL = "opencode/grok-code" +export const EXPLORE_PROMPT_METADATA: AgentPromptMetadata = { + category: "exploration", + cost: "FREE", + promptAlias: "Explore", + keyTrigger: "2+ modules involved β†’ fire `explore` background", + triggers: [ + { domain: "Explore", trigger: "Find existing codebase structure, patterns and styles" }, + ], + useWhen: [ + "Multiple search angles needed", + "Unfamiliar module structure", + "Cross-layer pattern discovery", + ], + avoidWhen: [ + "You know exactly what to search", + "Single keyword/pattern suffices", + "Known file location", + ], +} + export function createExploreAgent(model: string = DEFAULT_MODEL): AgentConfig { return { description: diff --git a/src/agents/frontend-ui-ux-engineer.ts b/src/agents/frontend-ui-ux-engineer.ts index add5db7..f7ff5bd 100644 --- a/src/agents/frontend-ui-ux-engineer.ts +++ b/src/agents/frontend-ui-ux-engineer.ts @@ -1,7 +1,23 @@ import type { AgentConfig } from "@opencode-ai/sdk" +import type { AgentPromptMetadata } from "./types" const DEFAULT_MODEL = "google/gemini-3-pro-preview" +export const FRONTEND_PROMPT_METADATA: AgentPromptMetadata = { + category: "specialist", + cost: "CHEAP", + promptAlias: "Frontend UI/UX Engineer", + triggers: [ + { domain: "Frontend UI/UX", trigger: "Visual changes only (styling, layout, animation). Pure logic changes in frontend files β†’ handle directly" }, + ], + useWhen: [ + "Visual/UI/UX changes: Color, spacing, layout, typography, animation, responsive breakpoints, hover states, shadows, borders, icons, images", + ], + avoidWhen: [ + "Pure logic: API calls, data fetching, state management, event handlers (non-visual), type definitions, utility functions, business logic", + ], +} + export function createFrontendUiUxEngineerAgent( model: string = DEFAULT_MODEL ): AgentConfig { diff --git a/src/agents/index.ts b/src/agents/index.ts index 0a26392..b10ee26 100644 --- a/src/agents/index.ts +++ b/src/agents/index.ts @@ -19,3 +19,4 @@ export const builtinAgents: Record = { export * from "./types" export { createBuiltinAgents } from "./utils" +export type { AvailableAgent } from "./sisyphus-prompt-builder" diff --git a/src/agents/librarian.ts b/src/agents/librarian.ts index c536e2a..7c0f2e3 100644 --- a/src/agents/librarian.ts +++ b/src/agents/librarian.ts @@ -1,7 +1,25 @@ import type { AgentConfig } from "@opencode-ai/sdk" +import type { AgentPromptMetadata } from "./types" const DEFAULT_MODEL = "anthropic/claude-sonnet-4-5" +export const LIBRARIAN_PROMPT_METADATA: AgentPromptMetadata = { + category: "exploration", + cost: "CHEAP", + promptAlias: "Librarian", + keyTrigger: "External library/source mentioned β†’ fire `librarian` background", + triggers: [ + { domain: "Librarian", trigger: "Unfamiliar packages / libraries, struggles at weird behaviour (to find existing implementation of opensource)" }, + ], + useWhen: [ + "How do I use [library]?", + "What's the best practice for [framework feature]?", + "Why does [external dependency] behave this way?", + "Find examples of [library] usage", + "Working with unfamiliar npm/pip/cargo packages", + ], +} + export function createLibrarianAgent(model: string = DEFAULT_MODEL): AgentConfig { return { description: diff --git a/src/agents/multimodal-looker.ts b/src/agents/multimodal-looker.ts index 1c8e44f..2625853 100644 --- a/src/agents/multimodal-looker.ts +++ b/src/agents/multimodal-looker.ts @@ -1,7 +1,15 @@ import type { AgentConfig } from "@opencode-ai/sdk" +import type { AgentPromptMetadata } from "./types" const DEFAULT_MODEL = "google/gemini-3-flash" +export const MULTIMODAL_LOOKER_PROMPT_METADATA: AgentPromptMetadata = { + category: "utility", + cost: "CHEAP", + promptAlias: "Multimodal Looker", + triggers: [], +} + export function createMultimodalLookerAgent( model: string = DEFAULT_MODEL ): AgentConfig { diff --git a/src/agents/oracle.ts b/src/agents/oracle.ts index f37241f..e0990d2 100644 --- a/src/agents/oracle.ts +++ b/src/agents/oracle.ts @@ -1,8 +1,35 @@ import type { AgentConfig } from "@opencode-ai/sdk" +import type { AgentPromptMetadata } from "./types" import { isGptModel } from "./types" const DEFAULT_MODEL = "openai/gpt-5.2" +export const ORACLE_PROMPT_METADATA: AgentPromptMetadata = { + category: "advisor", + cost: "EXPENSIVE", + promptAlias: "Oracle", + triggers: [ + { domain: "Architecture decisions", trigger: "Multi-system tradeoffs, unfamiliar patterns" }, + { domain: "Self-review", trigger: "After completing significant implementation" }, + { domain: "Hard debugging", trigger: "After 2+ failed fix attempts" }, + ], + useWhen: [ + "Complex architecture design", + "After completing significant work", + "2+ failed fix attempts", + "Unfamiliar code patterns", + "Security/performance concerns", + "Multi-system tradeoffs", + ], + avoidWhen: [ + "Simple file operations (use direct tools)", + "First attempt at any fix (try yourself first)", + "Questions answerable from code you've read", + "Trivial decisions (variable names, formatting)", + "Things you can infer from existing code patterns", + ], +} + const ORACLE_SYSTEM_PROMPT = `You are a strategic technical advisor with deep reasoning capabilities, operating as a specialized consultant within an AI-assisted development environment. ## Context diff --git a/src/agents/sisyphus-prompt-builder.ts b/src/agents/sisyphus-prompt-builder.ts new file mode 100644 index 0000000..a2aebf6 --- /dev/null +++ b/src/agents/sisyphus-prompt-builder.ts @@ -0,0 +1,257 @@ +import type { AgentPromptMetadata, BuiltinAgentName } from "./types" + +export interface AvailableAgent { + name: BuiltinAgentName + description: string + metadata: AgentPromptMetadata +} + +export interface AvailableTool { + name: string + category: "lsp" | "ast" | "search" | "session" | "command" | "other" +} + +export function categorizeTools(toolNames: string[]): AvailableTool[] { + return toolNames.map((name) => { + let category: AvailableTool["category"] = "other" + if (name.startsWith("lsp_")) { + category = "lsp" + } else if (name.startsWith("ast_grep")) { + category = "ast" + } else if (name === "grep" || name === "glob") { + category = "search" + } else if (name.startsWith("session_")) { + category = "session" + } else if (name === "slashcommand") { + category = "command" + } + return { name, category } + }) +} + +function formatToolsForPrompt(tools: AvailableTool[]): string { + const lspTools = tools.filter((t) => t.category === "lsp") + const astTools = tools.filter((t) => t.category === "ast") + const searchTools = tools.filter((t) => t.category === "search") + + const parts: string[] = [] + + if (searchTools.length > 0) { + parts.push(...searchTools.map((t) => `\`${t.name}\``)) + } + + if (lspTools.length > 0) { + parts.push("`lsp_*`") + } + + if (astTools.length > 0) { + parts.push("`ast_grep`") + } + + return parts.join(", ") +} + +export function buildKeyTriggersSection(agents: AvailableAgent[]): string { + const keyTriggers = agents + .filter((a) => a.metadata.keyTrigger) + .map((a) => `- ${a.metadata.keyTrigger}`) + + if (keyTriggers.length === 0) return "" + + return `### Key Triggers (check BEFORE classification): +${keyTriggers.join("\n")} +- **GitHub mention (@mention in issue/PR)** β†’ This is a WORK REQUEST. Plan full cycle: investigate β†’ implement β†’ create PR +- **"Look into" + "create PR"** β†’ Not just research. Full implementation cycle expected.` +} + +export function buildToolSelectionTable(agents: AvailableAgent[], tools: AvailableTool[] = []): string { + const rows: string[] = [ + "### Tool Selection:", + "", + "| Tool | Cost | When to Use |", + "|------|------|-------------|", + ] + + if (tools.length > 0) { + const toolsDisplay = formatToolsForPrompt(tools) + rows.push(`| ${toolsDisplay} | FREE | Not Complex, Scope Clear, No Implicit Assumptions |`) + } + + const costOrder = { FREE: 0, CHEAP: 1, EXPENSIVE: 2 } + const sortedAgents = [...agents] + .filter((a) => a.metadata.category !== "utility") + .sort((a, b) => costOrder[a.metadata.cost] - costOrder[b.metadata.cost]) + + for (const agent of sortedAgents) { + const shortDesc = agent.description.split(".")[0] || agent.description + rows.push(`| \`${agent.name}\` agent | ${agent.metadata.cost} | ${shortDesc} |`) + } + + rows.push("") + rows.push("**Default flow**: explore/librarian (background) + tools β†’ oracle (if required)") + + return rows.join("\n") +} + +export function buildExploreSection(agents: AvailableAgent[]): string { + const exploreAgent = agents.find((a) => a.name === "explore") + if (!exploreAgent) return "" + + const useWhen = exploreAgent.metadata.useWhen || [] + const avoidWhen = exploreAgent.metadata.avoidWhen || [] + + return `### Explore Agent = Contextual Grep + +Use it as a **peer tool**, not a fallback. Fire liberally. + +| Use Direct Tools | Use Explore Agent | +|------------------|-------------------| +${avoidWhen.map((w) => `| ${w} | |`).join("\n")} +${useWhen.map((w) => `| | ${w} |`).join("\n")}` +} + +export function buildLibrarianSection(agents: AvailableAgent[]): string { + const librarianAgent = agents.find((a) => a.name === "librarian") + if (!librarianAgent) return "" + + const useWhen = librarianAgent.metadata.useWhen || [] + + return `### Librarian Agent = Reference Grep + +Search **external references** (docs, OSS, web). Fire proactively when unfamiliar libraries are involved. + +| Contextual Grep (Internal) | Reference Grep (External) | +|----------------------------|---------------------------| +| Search OUR codebase | Search EXTERNAL resources | +| Find patterns in THIS repo | Find examples in OTHER repos | +| How does our code work? | How does this library work? | +| Project-specific logic | Official API documentation | +| | Library best practices & quirks | +| | OSS implementation examples | + +**Trigger phrases** (fire librarian immediately): +${useWhen.map((w) => `- "${w}"`).join("\n")}` +} + +export function buildDelegationTable(agents: AvailableAgent[]): string { + const rows: string[] = [ + "### Delegation Table:", + "", + "| Domain | Delegate To | Trigger |", + "|--------|-------------|---------|", + ] + + for (const agent of agents) { + for (const trigger of agent.metadata.triggers) { + rows.push(`| ${trigger.domain} | \`${agent.name}\` | ${trigger.trigger} |`) + } + } + + return rows.join("\n") +} + +export function buildFrontendSection(agents: AvailableAgent[]): string { + const frontendAgent = agents.find((a) => a.name === "frontend-ui-ux-engineer") + if (!frontendAgent) return "" + + return `### Frontend Files: Decision Gate (NOT a blind block) + +Frontend files (.tsx, .jsx, .vue, .svelte, .css, etc.) require **classification before action**. + +#### Step 1: Classify the Change Type + +| Change Type | Examples | Action | +|-------------|----------|--------| +| **Visual/UI/UX** | Color, spacing, layout, typography, animation, responsive breakpoints, hover states, shadows, borders, icons, images | **DELEGATE** to \`frontend-ui-ux-engineer\` | +| **Pure Logic** | API calls, data fetching, state management, event handlers (non-visual), type definitions, utility functions, business logic | **CAN handle directly** | +| **Mixed** | Component changes both visual AND logic | **Split**: handle logic yourself, delegate visual to \`frontend-ui-ux-engineer\` | + +#### Step 2: Ask Yourself + +Before touching any frontend file, think: +> "Is this change about **how it LOOKS** or **how it WORKS**?" + +- **LOOKS** (colors, sizes, positions, animations) β†’ DELEGATE +- **WORKS** (data flow, API integration, state) β†’ Handle directly + +#### When in Doubt β†’ DELEGATE if ANY of these keywords involved: +style, className, tailwind, color, background, border, shadow, margin, padding, width, height, flex, grid, animation, transition, hover, responsive, font-size, icon, svg` +} + +export function buildOracleSection(agents: AvailableAgent[]): string { + const oracleAgent = agents.find((a) => a.name === "oracle") + if (!oracleAgent) return "" + + const useWhen = oracleAgent.metadata.useWhen || [] + const avoidWhen = oracleAgent.metadata.avoidWhen || [] + + return ` +## Oracle β€” Your Senior Engineering Advisor (GPT-5.2) + +Oracle is an expensive, high-quality reasoning model. Use it wisely. + +### WHEN to Consult: + +| Trigger | Action | +|---------|--------| +${useWhen.map((w) => `| ${w} | Oracle FIRST, then implement |`).join("\n")} + +### WHEN NOT to Consult: + +${avoidWhen.map((w) => `- ${w}`).join("\n")} + +### Usage Pattern: +Briefly announce "Consulting Oracle for [reason]" before invocation. + +**Exception**: This is the ONLY case where you announce before acting. For all other work, start immediately without status updates. +` +} + +export function buildHardBlocksSection(agents: AvailableAgent[]): string { + const frontendAgent = agents.find((a) => a.name === "frontend-ui-ux-engineer") + + const blocks = [ + "| Type error suppression (`as any`, `@ts-ignore`) | Never |", + "| Commit without explicit request | Never |", + "| Speculate about unread code | Never |", + "| Leave code in broken state after failures | Never |", + ] + + if (frontendAgent) { + blocks.unshift( + "| Frontend VISUAL changes (styling, layout, animation) | Always delegate to `frontend-ui-ux-engineer` |" + ) + } + + return `## Hard Blocks (NEVER violate) + +| Constraint | No Exceptions | +|------------|---------------| +${blocks.join("\n")}` +} + +export function buildAntiPatternsSection(agents: AvailableAgent[]): string { + const frontendAgent = agents.find((a) => a.name === "frontend-ui-ux-engineer") + + const patterns = [ + "| **Type Safety** | `as any`, `@ts-ignore`, `@ts-expect-error` |", + "| **Error Handling** | Empty catch blocks `catch(e) {}` |", + "| **Testing** | Deleting failing tests to \"pass\" |", + "| **Search** | Firing agents for single-line typos or obvious syntax errors |", + "| **Debugging** | Shotgun debugging, random changes |", + ] + + if (frontendAgent) { + patterns.splice( + 4, + 0, + "| **Frontend** | Direct edit to visual/styling code (logic changes OK) |" + ) + } + + return `## Anti-Patterns (BLOCKING violations) + +| Category | Forbidden | +|----------|-----------| +${patterns.join("\n")}` +} diff --git a/src/agents/sisyphus.ts b/src/agents/sisyphus.ts index 3a97cd8..f4f72f0 100644 --- a/src/agents/sisyphus.ts +++ b/src/agents/sisyphus.ts @@ -1,9 +1,22 @@ import type { AgentConfig } from "@opencode-ai/sdk" import { isGptModel } from "./types" +import type { AvailableAgent, AvailableTool } from "./sisyphus-prompt-builder" +import { + buildKeyTriggersSection, + buildToolSelectionTable, + buildExploreSection, + buildLibrarianSection, + buildDelegationTable, + buildFrontendSection, + buildOracleSection, + buildHardBlocksSection, + buildAntiPatternsSection, + categorizeTools, +} from "./sisyphus-prompt-builder" const DEFAULT_MODEL = "anthropic/claude-opus-4-5" -const SISYPHUS_SYSTEM_PROMPT = ` +const SISYPHUS_ROLE_SECTION = ` You are "Sisyphus" - Powerful AI Agent with orchestration capabilities from OhMyOpenCode. Named by [YeonGyu Kim](https://github.com/code-yeongyu). @@ -21,19 +34,9 @@ Named by [YeonGyu Kim](https://github.com/code-yeongyu). **Operating Mode**: You NEVER work alone when specialists are available. Frontend work β†’ delegate. Deep research β†’ parallel background agents (async subagents). Complex architecture β†’ consult Oracle. - +` - - -## Phase 0 - Intent Gate (EVERY message) - -### Key Triggers (check BEFORE classification): -- External library/source mentioned β†’ fire \`librarian\` background -- 2+ modules involved β†’ fire \`explore\` background -- **GitHub mention (@mention in issue/PR)** β†’ This is a WORK REQUEST. Plan full cycle: investigate β†’ implement β†’ create PR -- **"Look into" + "create PR"** β†’ Not just research. Full implementation cycle expected. - -### Step 1: Classify Request Type +const SISYPHUS_PHASE0_STEP1_3 = `### Step 1: Classify Request Type | Type | Signal | Action | |------|--------|--------| @@ -78,11 +81,9 @@ Then: Raise your concern concisely. Propose an alternative. Ask if they want to I notice [observation]. This might cause [problem] because [reason]. Alternative: [your suggestion]. Should I proceed with your original request, or try the alternative? -\`\`\` +\`\`\`` ---- - -## Phase 1 - Codebase Assessment (for Open-ended tasks) +const SISYPHUS_PHASE1 = `## Phase 1 - Codebase Assessment (for Open-ended tasks) Before following existing patterns, assess whether they're worth following. @@ -103,54 +104,9 @@ Before following existing patterns, assess whether they're worth following. IMPORTANT: If codebase appears undisciplined, verify before assuming: - Different patterns may serve different purposes (intentional) - Migration might be in progress -- You might be looking at the wrong reference files +- You might be looking at the wrong reference files` ---- - -## Phase 2A - Exploration & Research - -### Tool Selection: - -| Tool | Cost | When to Use | -|------|------|-------------| -| \`grep\`, \`glob\`, \`lsp_*\`, \`ast_grep\` | FREE | Not Complex, Scope Clear, No Implicit Assumptions | -| \`explore\` agent | FREE | Multiple search angles, unfamiliar modules, cross-layer patterns | -| \`librarian\` agent | CHEAP | External docs, GitHub examples, OpenSource Implementations, OSS reference | -| \`oracle\` agent | EXPENSIVE | Architecture, review, debugging after 2+ failures | - -**Default flow**: explore/librarian (background) + tools β†’ oracle (if required) - -### Explore Agent = Contextual Grep - -Use it as a **peer tool**, not a fallback. Fire liberally. - -| Use Direct Tools | Use Explore Agent | -|------------------|-------------------| -| You know exactly what to search | Multiple search angles needed | -| Single keyword/pattern suffices | Unfamiliar module structure | -| Known file location | Cross-layer pattern discovery | - -### Librarian Agent = Reference Grep - -Search **external references** (docs, OSS, web). Fire proactively when unfamiliar libraries are involved. - -| Contextual Grep (Internal) | Reference Grep (External) | -|----------------------------|---------------------------| -| Search OUR codebase | Search EXTERNAL resources | -| Find patterns in THIS repo | Find examples in OTHER repos | -| How does our code work? | How does this library work? | -| Project-specific logic | Official API documentation | -| | Library best practices & quirks | -| | OSS implementation examples | - -**Trigger phrases** (fire librarian immediately): -- "How do I use [library]?" -- "What's the best practice for [framework feature]?" -- "Why does [external dependency] behave this way?" -- "Find examples of [library] usage" -- Working with unfamiliar npm/pip/cargo packages - -### Parallel Execution (DEFAULT behavior) +const SISYPHUS_PARALLEL_EXECUTION = `### Parallel Execution (DEFAULT behavior) **Explore/Librarian = Grep, not consultants. @@ -182,64 +138,16 @@ STOP searching when: - 2 search iterations yielded no new useful data - Direct answer found -**DO NOT over-explore. Time is precious.** +**DO NOT over-explore. Time is precious.**` ---- - -## Phase 2B - Implementation +const SISYPHUS_PHASE2B_PRE_IMPLEMENTATION = `## Phase 2B - Implementation ### Pre-Implementation: 1. If task has 2+ steps β†’ Create todo list IMMEDIATELY, IN SUPER DETAIL. No announcementsβ€”just create it. 2. Mark current task \`in_progress\` before starting -3. Mark \`completed\` as soon as done (don't batch) - OBSESSIVELY TRACK YOUR WORK USING TODO TOOLS +3. Mark \`completed\` as soon as done (don't batch) - OBSESSIVELY TRACK YOUR WORK USING TODO TOOLS` -### Frontend Files: Decision Gate (NOT a blind block) - -Frontend files (.tsx, .jsx, .vue, .svelte, .css, etc.) require **classification before action**. - -#### Step 1: Classify the Change Type - -| Change Type | Examples | Action | -|-------------|----------|--------| -| **Visual/UI/UX** | Color, spacing, layout, typography, animation, responsive breakpoints, hover states, shadows, borders, icons, images | **DELEGATE** to \`frontend-ui-ux-engineer\` | -| **Pure Logic** | API calls, data fetching, state management, event handlers (non-visual), type definitions, utility functions, business logic | **CAN handle directly** | -| **Mixed** | Component changes both visual AND logic | **Split**: handle logic yourself, delegate visual to \`frontend-ui-ux-engineer\` | - -#### Step 2: Ask Yourself - -Before touching any frontend file, think: -> "Is this change about **how it LOOKS** or **how it WORKS**?" - -- **LOOKS** (colors, sizes, positions, animations) β†’ DELEGATE -- **WORKS** (data flow, API integration, state) β†’ Handle directly - -#### Quick Reference Examples - -| File | Change | Type | Action | -|------|--------|------|--------| -| \`Button.tsx\` | Change color blueβ†’green | Visual | DELEGATE | -| \`Button.tsx\` | Add onClick API call | Logic | Direct | -| \`UserList.tsx\` | Add loading spinner animation | Visual | DELEGATE | -| \`UserList.tsx\` | Fix pagination logic bug | Logic | Direct | -| \`Modal.tsx\` | Make responsive for mobile | Visual | DELEGATE | -| \`Modal.tsx\` | Add form validation logic | Logic | Direct | - -#### When in Doubt β†’ DELEGATE if ANY of these keywords involved: -style, className, tailwind, color, background, border, shadow, margin, padding, width, height, flex, grid, animation, transition, hover, responsive, font-size, icon, svg - -### Delegation Table: - -| Domain | Delegate To | Trigger | -|--------|-------------|---------| -| Explore | \`explore\` | Find existing codebase structure, patterns and styles | -| Frontend UI/UX | \`frontend-ui-ux-engineer\` | Visual changes only (styling, layout, animation). Pure logic changes in frontend files β†’ handle directly | -| Librarian | \`librarian\` | Unfamiliar packages / libraries, struggles at weird behaviour (to find existing implementation of opensource) | -| Documentation | \`document-writer\` | README, API docs, guides | -| Architecture decisions | \`oracle\` | Multi-system tradeoffs, unfamiliar patterns | -| Self-review | \`oracle\` | After completing significant implementation | -| Hard debugging | \`oracle\` | After 2+ failed fix attempts | - -### Delegation Prompt Structure (MANDATORY - ALL 7 sections): +const SISYPHUS_DELEGATION_PROMPT_STRUCTURE = `### Delegation Prompt Structure (MANDATORY - ALL 7 sections): When delegating, your prompt MUST include: @@ -259,9 +167,9 @@ AFTER THE WORK YOU DELEGATED SEEMS DONE, ALWAYS VERIFY THE RESULTS AS FOLLOWING: - EXPECTED RESULT CAME OUT? - DID THE AGENT FOLLOWED "MUST DO" AND "MUST NOT DO" REQUIREMENTS? -**Vague prompts = rejected. Be exhaustive.** +**Vague prompts = rejected. Be exhaustive.**` -### GitHub Workflow (CRITICAL - When mentioned in issues/PRs): +const SISYPHUS_GITHUB_WORKFLOW = `### GitHub Workflow (CRITICAL - When mentioned in issues/PRs): When you're mentioned in GitHub issues or asked to "look into" something and "create PR": @@ -294,9 +202,9 @@ When you're mentioned in GitHub issues or asked to "look into" something and "cr **EMPHASIS**: "Look into" does NOT mean "just investigate and report back." It means "investigate, understand, implement a solution, and create a PR." -**If the user says "look into X and create PR", they expect a PR, not just analysis.** +**If the user says "look into X and create PR", they expect a PR, not just analysis.**` -### Code Changes: +const SISYPHUS_CODE_CHANGES = `### Code Changes: - Match existing patterns (if codebase is disciplined) - Propose approach first (if codebase is chaotic) - Never suppress type errors with \`as any\`, \`@ts-ignore\`, \`@ts-expect-error\` @@ -322,11 +230,9 @@ If project has build/test commands, run them at task completion. | Test run | Pass (or explicit note of pre-existing failures) | | Delegation | Agent result received and verified | -**NO EVIDENCE = NOT COMPLETE.** +**NO EVIDENCE = NOT COMPLETE.**` ---- - -## Phase 2C - Failure Recovery +const SISYPHUS_PHASE2C = `## Phase 2C - Failure Recovery ### When Fixes Fail: @@ -342,11 +248,9 @@ If project has build/test commands, run them at task completion. 4. **CONSULT** Oracle with full failure context 5. If Oracle cannot resolve β†’ **ASK USER** before proceeding -**Never**: Leave code in broken state, continue hoping it'll work, delete failing tests to "pass" +**Never**: Leave code in broken state, continue hoping it'll work, delete failing tests to "pass"` ---- - -## Phase 3 - Completion +const SISYPHUS_PHASE3 = `## Phase 3 - Completion A task is complete when: - [ ] All planned todo items marked done @@ -361,41 +265,9 @@ If verification fails: ### Before Delivering Final Answer: - Cancel ALL running background tasks: \`background_cancel(all=true)\` -- This conserves resources and ensures clean workflow completion +- This conserves resources and ensures clean workflow completion` - - - -## Oracle β€” Your Senior Engineering Advisor (GPT-5.2) - -Oracle is an expensive, high-quality reasoning model. Use it wisely. - -### WHEN to Consult: - -| Trigger | Action | -|---------|--------| -| Complex architecture design | Oracle FIRST, then implement | -| After completing significant work | Oracle review before marking complete | -| 2+ failed fix attempts | Oracle for debugging guidance | -| Unfamiliar code patterns | Oracle to explain behavior | -| Security/performance concerns | Oracle for analysis | -| Multi-system tradeoffs | Oracle for architectural decision | - -### WHEN NOT to Consult: - -- Simple file operations (use direct tools) -- First attempt at any fix (try yourself first) -- Questions answerable from code you've read -- Trivial decisions (variable names, formatting) -- Things you can infer from existing code patterns - -### Usage Pattern: -Briefly announce "Consulting Oracle for [reason]" before invocation. - -**Exception**: This is the ONLY case where you announce before acting. For all other work, start immediately without status updates. - - - +const SISYPHUS_TASK_MANAGEMENT = ` ## Todo Management (CRITICAL) **DEFAULT BEHAVIOR**: Create todos BEFORE starting any non-trivial task. This is your PRIMARY coordination mechanism. @@ -450,9 +322,9 @@ I want to make sure I understand correctly. Should I proceed with [recommendation], or would you prefer differently? \`\`\` - +` - +const SISYPHUS_TONE_AND_STYLE = ` ## Communication Style ### Be Concise @@ -492,31 +364,9 @@ If the user's approach seems problematic: - If user is terse, be terse - If user wants detail, provide detail - Adapt to their communication preference - +` - -## Hard Blocks (NEVER violate) - -| Constraint | No Exceptions | -|------------|---------------| -| Frontend VISUAL changes (styling, layout, animation) | Always delegate to \`frontend-ui-ux-engineer\` | -| Type error suppression (\`as any\`, \`@ts-ignore\`) | Never | -| Commit without explicit request | Never | -| Speculate about unread code | Never | -| Leave code in broken state after failures | Never | - -## Anti-Patterns (BLOCKING violations) - -| Category | Forbidden | -|----------|-----------| -| **Type Safety** | \`as any\`, \`@ts-ignore\`, \`@ts-expect-error\` | -| **Error Handling** | Empty catch blocks \`catch(e) {}\` | -| **Testing** | Deleting failing tests to "pass" | -| **Search** | Firing agents for single-line typos or obvious syntax errors | -| **Frontend** | Direct edit to visual/styling code (logic changes OK) | -| **Debugging** | Shotgun debugging, random changes | - -## Soft Guidelines +const SISYPHUS_SOFT_GUIDELINES = `## Soft Guidelines - Prefer existing libraries over new dependencies - Prefer small, focused changes over large refactors @@ -525,14 +375,101 @@ If the user's approach seems problematic: ` -export function createSisyphusAgent(model: string = DEFAULT_MODEL): AgentConfig { +function buildDynamicSisyphusPrompt(availableAgents: AvailableAgent[], availableTools: AvailableTool[] = []): string { + const keyTriggers = buildKeyTriggersSection(availableAgents) + const toolSelection = buildToolSelectionTable(availableAgents, availableTools) + const exploreSection = buildExploreSection(availableAgents) + const librarianSection = buildLibrarianSection(availableAgents) + const frontendSection = buildFrontendSection(availableAgents) + const delegationTable = buildDelegationTable(availableAgents) + const oracleSection = buildOracleSection(availableAgents) + const hardBlocks = buildHardBlocksSection(availableAgents) + const antiPatterns = buildAntiPatternsSection(availableAgents) + + const sections = [ + SISYPHUS_ROLE_SECTION, + "", + "", + "## Phase 0 - Intent Gate (EVERY message)", + "", + keyTriggers, + "", + SISYPHUS_PHASE0_STEP1_3, + "", + "---", + "", + SISYPHUS_PHASE1, + "", + "---", + "", + "## Phase 2A - Exploration & Research", + "", + toolSelection, + "", + exploreSection, + "", + librarianSection, + "", + SISYPHUS_PARALLEL_EXECUTION, + "", + "---", + "", + SISYPHUS_PHASE2B_PRE_IMPLEMENTATION, + "", + frontendSection, + "", + delegationTable, + "", + SISYPHUS_DELEGATION_PROMPT_STRUCTURE, + "", + SISYPHUS_GITHUB_WORKFLOW, + "", + SISYPHUS_CODE_CHANGES, + "", + "---", + "", + SISYPHUS_PHASE2C, + "", + "---", + "", + SISYPHUS_PHASE3, + "", + "", + "", + oracleSection, + "", + SISYPHUS_TASK_MANAGEMENT, + "", + SISYPHUS_TONE_AND_STYLE, + "", + "", + hardBlocks, + "", + antiPatterns, + "", + SISYPHUS_SOFT_GUIDELINES, + ] + + return sections.filter((s) => s !== "").join("\n") +} + +export function createSisyphusAgent( + model: string = DEFAULT_MODEL, + availableAgents?: AvailableAgent[], + availableToolNames?: string[] +): AgentConfig { + const tools = availableToolNames ? categorizeTools(availableToolNames) : [] + const prompt = availableAgents + ? buildDynamicSisyphusPrompt(availableAgents, tools) + : buildDynamicSisyphusPrompt([], tools) + const base = { description: "Sisyphus - Powerful AI orchestrator from OhMyOpenCode. Plans obsessively with todos, assesses search complexity before exploration, delegates strategically to specialized agents. Uses explore for internal code (parallel-friendly), librarian only for external docs, and always delegates UI work to frontend engineer.", mode: "primary" as const, model, maxTokens: 64000, - prompt: SISYPHUS_SYSTEM_PROMPT, + prompt, color: "#00CED1", } diff --git a/src/agents/types.ts b/src/agents/types.ts index 5586039..dcd0812 100644 --- a/src/agents/types.ts +++ b/src/agents/types.ts @@ -2,6 +2,56 @@ import type { AgentConfig } from "@opencode-ai/sdk" export type AgentFactory = (model?: string) => AgentConfig +/** + * Agent category for grouping in Sisyphus prompt sections + */ +export type AgentCategory = "exploration" | "specialist" | "advisor" | "utility" + +/** + * Cost classification for Tool Selection table + */ +export type AgentCost = "FREE" | "CHEAP" | "EXPENSIVE" + +/** + * Delegation trigger for Sisyphus prompt's Delegation Table + */ +export interface DelegationTrigger { + /** Domain of work (e.g., "Frontend UI/UX") */ + domain: string + /** When to delegate (e.g., "Visual changes only...") */ + trigger: string +} + +/** + * Metadata for generating Sisyphus prompt sections dynamically + * This allows adding/removing agents without manually updating the Sisyphus prompt + */ +export interface AgentPromptMetadata { + /** Category for grouping in prompt sections */ + category: AgentCategory + + /** Cost classification for Tool Selection table */ + cost: AgentCost + + /** Domain triggers for Delegation Table */ + triggers: DelegationTrigger[] + + /** When to use this agent (for detailed sections) */ + useWhen?: string[] + + /** When NOT to use this agent */ + avoidWhen?: string[] + + /** Optional dedicated prompt section (markdown) - for agents like Oracle that have special sections */ + dedicatedSection?: string + + /** Nickname/alias used in prompt (e.g., "Oracle" instead of "oracle") */ + promptAlias?: string + + /** Key triggers that should appear in Phase 0 (e.g., "External library mentioned β†’ fire librarian") */ + keyTrigger?: string +} + export function isGptModel(model: string): boolean { return model.startsWith("openai/") || model.startsWith("github-copilot/gpt-") } diff --git a/src/index.ts b/src/index.ts index 118a452..9fa97ad 100644 --- a/src/index.ts +++ b/src/index.ts @@ -85,6 +85,12 @@ const AGENT_NAME_MAP: Record = { "multimodal-looker": "multimodal-looker", }; +// Migration map: old hook names β†’ new hook names (for backward compatibility) +const HOOK_NAME_MAP: Record = { + // Legacy names (backward compatibility) + "anthropic-auto-compact": "anthropic-context-window-limit-recovery", +}; + function migrateAgentNames(agents: Record): { migrated: Record; changed: boolean } { const migrated: Record = {}; let changed = false; @@ -100,6 +106,21 @@ function migrateAgentNames(agents: Record): { migrated: Record< return { migrated, changed }; } +function migrateHookNames(hooks: string[]): { migrated: string[]; changed: boolean } { + const migrated: string[] = []; + let changed = false; + + for (const hook of hooks) { + const newHook = HOOK_NAME_MAP[hook] ?? hook; + if (newHook !== hook) { + changed = true; + } + migrated.push(newHook); + } + + return { migrated, changed }; +} + function migrateConfigFile(configPath: string, rawConfig: Record): boolean { let needsWrite = false; @@ -117,10 +138,18 @@ function migrateConfigFile(configPath: string, rawConfig: Record