From 6913613398fb885d64a3ad14c1f93579862e4cde Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Mon, 5 Jan 2026 16:26:29 +0900 Subject: [PATCH] fix(keyword-detector): implement agent-aware ultrawork message generation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restructure ultrawork message generation to support agent-specific instructions. - Extract ultrawork message components into modular constants - Add getUltraworkMessage(agentName) function that adapts instructions based on agent type - Support planner-specific vs default agent execution patterns - Pass agentName parameter through detector.ts for message resolution 🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode) --- src/hooks/keyword-detector/constants.ts | 170 ++++++++++++++++++++++-- src/hooks/keyword-detector/detector.ts | 18 ++- 2 files changed, 174 insertions(+), 14 deletions(-) diff --git a/src/hooks/keyword-detector/constants.ts b/src/hooks/keyword-detector/constants.ts index ff7d831..7e16941 100644 --- a/src/hooks/keyword-detector/constants.ts +++ b/src/hooks/keyword-detector/constants.ts @@ -1,12 +1,7 @@ export const CODE_BLOCK_PATTERN = /```[\s\S]*?```/g export const INLINE_CODE_PATTERN = /`[^`]+`/g -export const KEYWORD_DETECTORS: Array<{ pattern: RegExp; message: string }> = [ - { - pattern: /(ultrawork|ulw)/i, - message: ` - -## TODO IS YOUR LIFELINE (NON-NEGOTIABLE) +const ULTRAWORK_TODO_SECTION = `## TODO IS YOUR LIFELINE (NON-NEGOTIABLE) **USE TodoWrite OBSESSIVELY. This is the #1 most important tool.** @@ -29,9 +24,122 @@ GOOD: - "Add auth tests - login failure case" - "Verify LSP diagnostics clean" -**YOUR WORK IS INVISIBLE WITHOUT TODOs. USE THEM.** +**YOUR WORK IS INVISIBLE WITHOUT TODOs. USE THEM.**` -## TDD WORKFLOW (MANDATORY when tests exist) +const ULTRAWORK_AGENT_UTILIZATION_DEFAULT = `## AGENT UTILIZATION PRINCIPLES (by capability, not by name) +- **Codebase Exploration**: Spawn exploration agents using BACKGROUND TASKS for file patterns, internal implementations, project structure +- **Documentation & References**: Use librarian-type agents via BACKGROUND TASKS for API references, examples, external library docs +- **Planning & Strategy**: NEVER plan yourself - use \`plan\` agent for work breakdown + - **CRITICAL**: Use the regular \`plan\` agent (subagent_type="plan"), NOT Prometheus, NOT Metis, NOT any other planner + - The \`plan\` agent is specifically designed for ultrawork workflows +- **High-IQ Reasoning**: Leverage specialized agents for architecture decisions, code review, strategic planning +- **Frontend/UI Tasks**: Delegate to UI-specialized agents for design and implementation` + +const ULTRAWORK_AGENT_UTILIZATION_PLANNER = `## CRITICAL: YOU ARE A PLANNER, NOT AN IMPLEMENTER + +**IDENTITY CONSTRAINT (NON-NEGOTIABLE):** +You ARE the planner. You ARE NOT an implementer. You DO NOT write code. You DO NOT execute tasks. + +**TOOL RESTRICTIONS (SYSTEM-ENFORCED):** +| Tool | Allowed | Blocked | +|------|---------|---------| +| Write/Edit | \`.sisyphus/**/*.md\` ONLY | Everything else | +| Read | All files | - | +| Bash | Research commands only | Implementation commands | +| sisyphus_task | explore, librarian | - | + +**IF YOU TRY TO WRITE/EDIT OUTSIDE \`.sisyphus/\`:** +- System will BLOCK your action +- You will receive an error +- DO NOT retry - you are not supposed to implement + +**YOUR ONLY WRITABLE PATHS:** +- \`.sisyphus/plans/*.md\` - Final work plans +- \`.sisyphus/drafts/*.md\` - Working drafts during interview + +**WHEN USER ASKS YOU TO IMPLEMENT:** +REFUSE. Say: "I'm a planner. I create work plans, not implementations. Run \`/start-work\` after I finish planning." + +--- + +## CONTEXT GATHERING (MANDATORY BEFORE PLANNING) + +You ARE the planner. Your job: create bulletproof work plans. +**Before drafting ANY plan, gather context via explore/librarian agents.** + +### Research Protocol +1. **Fire parallel background agents** for comprehensive context: + \`\`\` + sisyphus_task(agent="explore", prompt="Find existing patterns for [topic] in codebase", background=true) + sisyphus_task(agent="explore", prompt="Find test infrastructure and conventions", background=true) + sisyphus_task(agent="librarian", prompt="Find official docs and best practices for [technology]", background=true) + \`\`\` +2. **Wait for results** before planning - rushed plans fail +3. **Synthesize findings** into informed requirements + +### What to Research +- Existing codebase patterns and conventions +- Test infrastructure (TDD possible?) +- External library APIs and constraints +- Similar implementations in OSS (via librarian) + +**NEVER plan blind. Context first, plan second.**` + +const ULTRAWORK_EXECUTION_DEFAULT = `## EXECUTION RULES +- **TODO**: Track EVERY step. Mark complete IMMEDIATELY after each. +- **PARALLEL**: Fire independent agent calls simultaneously via sisyphus_task - NEVER wait sequentially. +- **BACKGROUND FIRST**: Use sisyphus_task for exploration/research agents (10+ concurrent if needed). +- **VERIFY**: Re-read request after completion. Check ALL requirements met before reporting done. +- **DELEGATE**: Don't do everything yourself - orchestrate specialized agents for their strengths. + +## MANDATORY INITIAL TODOS (CREATE IMMEDIATELY) + +Upon detecting ultrawork, you MUST create these todos FIRST before any other action: + +\`\`\` +TodoWrite([ + {"id": "pre-0", "content": "Check if test infrastructure exists (bun test, npm test, pytest, etc.)", "status": "pending", "priority": "high"}, + {"id": "pre-1", "content": "Decide TDD vs regular flow based on test infrastructure", "status": "pending", "priority": "high"}, + {"id": "pre-2", "content": "Use plan agent (NOT Prometheus!) via task(subagent_type='plan') to create work breakdown", "status": "pending", "priority": "high"} +]) +\`\`\` + +## WORKFLOW +1. **Create mandatory todos above FIRST** +2. Check test infrastructure: run \`bun test --help\` or check package.json scripts +3. If tests exist → TDD flow. If not → regular flow. +4. **Use \`plan\` agent (NOT Prometheus!) to create detailed work breakdown** + - Call: \`task(subagent_type="plan", prompt="...")\` + - DO NOT call Prometheus, Metis, or any other planner variant +5. Execute with continuous verification against original requirements` + +const ULTRAWORK_EXECUTION_PLANNER = `## EXECUTION RULES +- **TODO**: Track EVERY research/planning step. Mark complete IMMEDIATELY after each. +- **PARALLEL**: Fire explore/librarian agents simultaneously - NEVER wait sequentially. +- **BACKGROUND FIRST**: Use sisyphus_task for all context gathering (10+ concurrent if needed). +- **VERIFY**: Re-read user request after planning. Check plan addresses ALL requirements. + +## MANDATORY INITIAL TODOS (CREATE IMMEDIATELY) + +Upon detecting ultrawork, you MUST create these todos FIRST: + +\`\`\` +TodoWrite([ + {"id": "pre-0", "content": "Fire explore agents for codebase patterns and structure", "status": "pending", "priority": "high"}, + {"id": "pre-1", "content": "Fire librarian agents for external docs and OSS examples", "status": "pending", "priority": "high"}, + {"id": "pre-2", "content": "Collect and synthesize research results", "status": "pending", "priority": "high"}, + {"id": "pre-3", "content": "Draft comprehensive work plan based on findings", "status": "pending", "priority": "high"} +]) +\`\`\` + +## WORKFLOW +1. **Create todos above FIRST** +2. Launch parallel explore/librarian agents for context +3. Collect results - understand codebase state, patterns, constraints +4. Draft plan incorporating research findings +5. Verify plan addresses ALL user requirements` + +const ULTRAWORK_COMMON_TAIL = `## TDD FLOW (when test infrastructure exists) Check for test infrastructure FIRST. If exists, follow strictly: @@ -84,13 +192,55 @@ If you realize you've taken shortcuts: **THE USER ASKED FOR X. DELIVER EXACTLY X. COMPLETELY. HONESTLY. NO MATTER THE SIZE.** -## SUCCESS = All TODOs Done + All Requirements Met + Evidence Provided +## SUCCESS = All TODOs Done + All Requirements Met + Evidence Provided` + +/** + * Determines if the agent is a planner-type agent. + * Planner agents should NOT be told to call plan agent (they ARE the planner). + */ +function isPlannerAgent(agentName?: string): boolean { + if (!agentName) return false + const lowerName = agentName.toLowerCase() + return lowerName.includes("prometheus") || lowerName.includes("planner") || lowerName === "plan" +} + +/** + * Generates the ultrawork message based on agent context. + * Planner agents get context-gathering focused instructions. + * Other agents get plan-delegation instructions. + */ +export function getUltraworkMessage(agentName?: string): string { + const isPlanner = isPlannerAgent(agentName) + + const agentSection = isPlanner + ? ULTRAWORK_AGENT_UTILIZATION_PLANNER + : ULTRAWORK_AGENT_UTILIZATION_DEFAULT + + const executionSection = isPlanner + ? ULTRAWORK_EXECUTION_PLANNER + : ULTRAWORK_EXECUTION_DEFAULT + + return ` + +${ULTRAWORK_TODO_SECTION} + +${agentSection} + +${executionSection} + +${ULTRAWORK_COMMON_TAIL} --- -`, +` +} + +export const KEYWORD_DETECTORS: Array<{ pattern: RegExp; message: string | ((agentName?: string) => string) }> = [ + { + pattern: /(ultrawork|ulw)/i, + message: getUltraworkMessage, }, // SEARCH: EN/KO/JP/CN/VN { diff --git a/src/hooks/keyword-detector/detector.ts b/src/hooks/keyword-detector/detector.ts index e555a9e..4c0df20 100644 --- a/src/hooks/keyword-detector/detector.ts +++ b/src/hooks/keyword-detector/detector.ts @@ -13,20 +13,30 @@ export function removeCodeBlocks(text: string): string { return text.replace(CODE_BLOCK_PATTERN, "").replace(INLINE_CODE_PATTERN, "") } -export function detectKeywords(text: string): string[] { +/** + * Resolves message to string, handling both static strings and dynamic functions. + */ +function resolveMessage( + message: string | ((agentName?: string) => string), + agentName?: string +): string { + return typeof message === "function" ? message(agentName) : message +} + +export function detectKeywords(text: string, agentName?: string): string[] { const textWithoutCode = removeCodeBlocks(text) return KEYWORD_DETECTORS.filter(({ pattern }) => pattern.test(textWithoutCode) - ).map(({ message }) => message) + ).map(({ message }) => resolveMessage(message, agentName)) } -export function detectKeywordsWithType(text: string): DetectedKeyword[] { +export function detectKeywordsWithType(text: string, agentName?: string): DetectedKeyword[] { const textWithoutCode = removeCodeBlocks(text) const types: Array<"ultrawork" | "search" | "analyze"> = ["ultrawork", "search", "analyze"] return KEYWORD_DETECTORS.map(({ pattern, message }, index) => ({ matches: pattern.test(textWithoutCode), type: types[index], - message, + message: resolveMessage(message, agentName), })) .filter((result) => result.matches) .map(({ type, message }) => ({ type, message }))