refactor: rename sisyphus_task to delegate_task

- Rename directories: sisyphus-task → delegate-task
- Rename types: SisyphusTaskArgs → DelegateTaskArgs, etc.
- Rename functions: createSisyphusTask → createDelegateTask
- Rename constants: SISYPHUS_TASK_* → DELEGATE_TASK_*
- Update tool name: sisyphus_task → delegate_task
- Update all prompts, docs, and tests
This commit is contained in:
justsisyphus
2026-01-16 17:34:40 +09:00
parent 6008388a4e
commit 188bbef018
46 changed files with 289 additions and 303 deletions
+6 -6
View File
@@ -24,7 +24,7 @@ export const TARGET_TOOLS = new Set([
export const AGENT_TOOLS = new Set([
"task",
"call_omo_agent",
"sisyphus_task",
"delegate_task",
]);
export const REMINDER_MESSAGE = `
@@ -32,13 +32,13 @@ export const REMINDER_MESSAGE = `
You called a search/fetch tool directly without leveraging specialized agents.
RECOMMENDED: Use sisyphus_task with explore/librarian agents for better results:
RECOMMENDED: Use delegate_task with explore/librarian agents for better results:
\`\`\`
// Parallel exploration - fire multiple agents simultaneously
sisyphus_task(agent="explore", prompt="Find all files matching pattern X")
sisyphus_task(agent="explore", prompt="Search for implementation of Y")
sisyphus_task(agent="librarian", prompt="Lookup documentation for Z")
delegate_task(agent="explore", prompt="Find all files matching pattern X")
delegate_task(agent="explore", prompt="Search for implementation of Y")
delegate_task(agent="librarian", prompt="Lookup documentation for Z")
// Then continue your work while they run in background
// System will notify you when each completes
@@ -50,5 +50,5 @@ WHY:
- Specialized agents have domain expertise
- Reduces context window usage in main session
ALWAYS prefer: Multiple parallel sisyphus_task calls > Direct tool calls
ALWAYS prefer: Multiple parallel delegate_task calls > Direct tool calls
`;
+2 -16
View File
@@ -145,13 +145,7 @@ export function createClaudeCodeHooksHook(
const hookContent = result.messages.join("\n\n")
log(`[claude-code-hooks] Injecting ${result.messages.length} hook messages`, { sessionID: input.sessionID, contentLength: hookContent.length, isFirstMessage })
if (isFirstMessage) {
const idx = output.parts.findIndex((p) => p.type === "text" && p.text)
if (idx >= 0) {
output.parts[idx].text = `${hookContent}\n\n${output.parts[idx].text ?? ""}`
log("UserPromptSubmit hooks prepended to first message parts directly", { sessionID: input.sessionID })
}
} else if (contextCollector) {
if (contextCollector) {
log("[DEBUG] Registering hook content to contextCollector", {
sessionID: input.sessionID,
contentLength: hookContent.length,
@@ -168,14 +162,6 @@ export function createClaudeCodeHooksHook(
sessionID: input.sessionID,
contentLength: hookContent.length,
})
} else {
const idx = output.parts.findIndex((p) => p.type === "text" && p.text)
if (idx >= 0) {
output.parts[idx].text = `${hookContent}\n\n${output.parts[idx].text ?? ""}`
log("Hook content prepended to message (fallback)", {
sessionID: input.sessionID,
})
}
}
}
}
@@ -257,7 +243,7 @@ export function createClaudeCodeHooksHook(
const cachedInput = getToolInput(input.sessionID, input.tool, input.callID) || {}
// Use metadata if available and non-empty, otherwise wrap output.output in a structured object
// This ensures plugin tools (call_omo_agent, sisyphus_task, task) that return strings
// This ensures plugin tools (call_omo_agent, delegate_task, task) that return strings
// get their results properly recorded in transcripts instead of empty {}
const metadata = output.metadata as Record<string, unknown> | undefined
const hasMetadata = metadata && typeof metadata === "object" && Object.keys(metadata).length > 0
@@ -1,18 +1,18 @@
import { describe, expect, it } from "bun:test"
import {
SISYPHUS_TASK_ERROR_PATTERNS,
detectSisyphusTaskError,
DELEGATE_TASK_ERROR_PATTERNS,
detectDelegateTaskError,
buildRetryGuidance,
} from "./index"
describe("sisyphus-task-retry", () => {
describe("SISYPHUS_TASK_ERROR_PATTERNS", () => {
describe("DELEGATE_TASK_ERROR_PATTERNS", () => {
// #given error patterns are defined
// #then should include all known sisyphus_task error types
// #then should include all known delegate_task error types
it("should contain all known error patterns", () => {
expect(SISYPHUS_TASK_ERROR_PATTERNS.length).toBeGreaterThan(5)
expect(DELEGATE_TASK_ERROR_PATTERNS.length).toBeGreaterThan(5)
const patternTexts = SISYPHUS_TASK_ERROR_PATTERNS.map(p => p.pattern)
const patternTexts = DELEGATE_TASK_ERROR_PATTERNS.map(p => p.pattern)
expect(patternTexts).toContain("run_in_background")
expect(patternTexts).toContain("skills")
expect(patternTexts).toContain("category OR subagent_type")
@@ -21,14 +21,14 @@ describe("sisyphus-task-retry", () => {
})
})
describe("detectSisyphusTaskError", () => {
describe("detectDelegateTaskError", () => {
// #given tool output with run_in_background error
// #when detecting error
// #then should return matching error info
it("should detect run_in_background missing error", () => {
const output = "❌ Invalid arguments: 'run_in_background' parameter is REQUIRED. Use run_in_background=false for task delegation."
const result = detectSisyphusTaskError(output)
const result = detectDelegateTaskError(output)
expect(result).not.toBeNull()
expect(result?.errorType).toBe("missing_run_in_background")
@@ -37,7 +37,7 @@ describe("sisyphus-task-retry", () => {
it("should detect skills missing error", () => {
const output = "❌ Invalid arguments: 'skills' parameter is REQUIRED. Use skills=[] if no skills needed."
const result = detectSisyphusTaskError(output)
const result = detectDelegateTaskError(output)
expect(result).not.toBeNull()
expect(result?.errorType).toBe("missing_skills")
@@ -46,7 +46,7 @@ describe("sisyphus-task-retry", () => {
it("should detect category/subagent mutual exclusion error", () => {
const output = "❌ Invalid arguments: Provide EITHER category OR subagent_type, not both."
const result = detectSisyphusTaskError(output)
const result = detectDelegateTaskError(output)
expect(result).not.toBeNull()
expect(result?.errorType).toBe("mutual_exclusion")
@@ -55,7 +55,7 @@ describe("sisyphus-task-retry", () => {
it("should detect unknown category error", () => {
const output = '❌ Unknown category: "invalid-cat". Available: visual-engineering, ultrabrain, quick'
const result = detectSisyphusTaskError(output)
const result = detectDelegateTaskError(output)
expect(result).not.toBeNull()
expect(result?.errorType).toBe("unknown_category")
@@ -64,7 +64,7 @@ describe("sisyphus-task-retry", () => {
it("should detect unknown agent error", () => {
const output = '❌ Unknown agent: "fake-agent". Available agents: explore, librarian, oracle'
const result = detectSisyphusTaskError(output)
const result = detectDelegateTaskError(output)
expect(result).not.toBeNull()
expect(result?.errorType).toBe("unknown_agent")
@@ -73,7 +73,7 @@ describe("sisyphus-task-retry", () => {
it("should return null for successful output", () => {
const output = "Background task launched.\n\nTask ID: bg_12345\nSession ID: ses_abc"
const result = detectSisyphusTaskError(output)
const result = detectDelegateTaskError(output)
expect(result).toBeNull()
})
@@ -1,12 +1,12 @@
import type { PluginInput } from "@opencode-ai/plugin"
export interface SisyphusTaskErrorPattern {
export interface DelegateTaskErrorPattern {
pattern: string
errorType: string
fixHint: string
}
export const SISYPHUS_TASK_ERROR_PATTERNS: SisyphusTaskErrorPattern[] = [
export const DELEGATE_TASK_ERROR_PATTERNS: DelegateTaskErrorPattern[] = [
{
pattern: "run_in_background",
errorType: "missing_run_in_background",
@@ -45,7 +45,7 @@ export const SISYPHUS_TASK_ERROR_PATTERNS: SisyphusTaskErrorPattern[] = [
{
pattern: "Cannot call primary agent",
errorType: "primary_agent",
fixHint: "Primary agents cannot be called via sisyphus_task. Use a subagent like 'explore', 'oracle', or 'librarian'",
fixHint: "Primary agents cannot be called via delegate_task. Use a subagent like 'explore', 'oracle', or 'librarian'",
},
{
pattern: "Skills not found",
@@ -59,10 +59,10 @@ export interface DetectedError {
originalOutput: string
}
export function detectSisyphusTaskError(output: string): DetectedError | null {
export function detectDelegateTaskError(output: string): DetectedError | null {
if (!output.includes("❌")) return null
for (const errorPattern of SISYPHUS_TASK_ERROR_PATTERNS) {
for (const errorPattern of DELEGATE_TASK_ERROR_PATTERNS) {
if (output.includes(errorPattern.pattern)) {
return {
errorType: errorPattern.errorType,
@@ -80,16 +80,16 @@ function extractAvailableList(output: string): string | null {
}
export function buildRetryGuidance(errorInfo: DetectedError): string {
const pattern = SISYPHUS_TASK_ERROR_PATTERNS.find(
const pattern = DELEGATE_TASK_ERROR_PATTERNS.find(
(p) => p.errorType === errorInfo.errorType
)
if (!pattern) {
return `[sisyphus_task ERROR] Fix the error and retry with correct parameters.`
return `[delegate_task ERROR] Fix the error and retry with correct parameters.`
}
let guidance = `
[sisyphus_task CALL FAILED - IMMEDIATE RETRY REQUIRED]
[delegate_task CALL FAILED - IMMEDIATE RETRY REQUIRED]
**Error Type**: ${errorInfo.errorType}
**Fix**: ${pattern.fixHint}
@@ -101,11 +101,11 @@ export function buildRetryGuidance(errorInfo: DetectedError): string {
}
guidance += `
**Action**: Retry sisyphus_task NOW with corrected parameters.
**Action**: Retry delegate_task NOW with corrected parameters.
Example of CORRECT call:
\`\`\`
sisyphus_task(
delegate_task(
description="Task description",
prompt="Detailed prompt...",
category="general", // OR subagent_type="explore"
@@ -118,15 +118,15 @@ sisyphus_task(
return guidance
}
export function createSisyphusTaskRetryHook(_ctx: PluginInput) {
export function createDelegateTaskRetryHook(_ctx: PluginInput) {
return {
"tool.execute.after": async (
input: { tool: string; sessionID: string; callID: string },
output: { title: string; output: string; metadata: unknown }
) => {
if (input.tool.toLowerCase() !== "sisyphus_task") return
if (input.tool.toLowerCase() !== "delegate_task") return
const errorInfo = detectSisyphusTaskError(output.output)
const errorInfo = detectDelegateTaskError(output.output)
if (errorInfo) {
const guidance = buildRetryGuidance(errorInfo)
output.output += `\n${guidance}`
+1 -1
View File
@@ -30,4 +30,4 @@ export { createPrometheusMdOnlyHook } from "./prometheus-md-only";
export { createTaskResumeInfoHook } from "./task-resume-info";
export { createStartWorkHook } from "./start-work";
export { createSisyphusOrchestratorHook } from "./sisyphus-orchestrator";
export { createSisyphusTaskRetryHook } from "./sisyphus-task-retry";
export { createDelegateTaskRetryHook } from "./delegate-task-retry";
+7 -7
View File
@@ -12,7 +12,7 @@ You ARE the planner. You ARE NOT an implementer. You DO NOT write code. You DO N
| Write/Edit | \`.sisyphus/**/*.md\` ONLY | Everything else |
| Read | All files | - |
| Bash | Research commands only | Implementation commands |
| sisyphus_task | explore, librarian | - |
| delegate_task | explore, librarian | - |
**IF YOU TRY TO WRITE/EDIT OUTSIDE \`.sisyphus/\`:**
- System will BLOCK your action
@@ -36,9 +36,9 @@ You ARE the planner. Your job: create bulletproof work plans.
### 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)
delegate_task(agent="explore", prompt="Find existing patterns for [topic] in codebase", background=true)
delegate_task(agent="explore", prompt="Find test infrastructure and conventions", background=true)
delegate_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
@@ -101,14 +101,14 @@ TELL THE USER WHAT AGENTS YOU WILL LEVERAGE NOW TO SATISFY USER'S REQUEST.
## EXECUTION RULES
- **TODO**: Track EVERY step. Mark complete IMMEDIATELY after each.
- **PARALLEL**: Fire independent agent calls simultaneously via sisyphus_task(background=true) - NEVER wait sequentially.
- **BACKGROUND FIRST**: Use sisyphus_task for exploration/research agents (10+ concurrent if needed).
- **PARALLEL**: Fire independent agent calls simultaneously via delegate_task(background=true) - NEVER wait sequentially.
- **BACKGROUND FIRST**: Use delegate_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.
## WORKFLOW
1. Analyze the request and identify required capabilities
2. Spawn exploration/librarian agents via sisyphus_task(background=true) in PARALLEL (10+ if needed)
2. Spawn exploration/librarian agents via delegate_task(background=true) in PARALLEL (10+ if needed)
3. Always Use Plan agent with gathered context to create detailed work breakdown
4. Execute with continuous verification against original requirements
+5 -5
View File
@@ -154,11 +154,11 @@ describe("prometheus-md-only", () => {
).resolves.toBeUndefined()
})
test("should inject read-only warning when Prometheus calls sisyphus_task", async () => {
test("should inject read-only warning when Prometheus calls delegate_task", async () => {
// #given
const hook = createPrometheusMdOnlyHook(createMockPluginInput())
const input = {
tool: "sisyphus_task",
tool: "delegate_task",
sessionID: TEST_SESSION_ID,
callID: "call-1",
}
@@ -216,7 +216,7 @@ describe("prometheus-md-only", () => {
// #given
const hook = createPrometheusMdOnlyHook(createMockPluginInput())
const input = {
tool: "sisyphus_task",
tool: "delegate_task",
sessionID: TEST_SESSION_ID,
callID: "call-1",
}
@@ -257,11 +257,11 @@ describe("prometheus-md-only", () => {
).resolves.toBeUndefined()
})
test("should not inject warning for non-Prometheus agents calling sisyphus_task", async () => {
test("should not inject warning for non-Prometheus agents calling delegate_task", async () => {
// #given
const hook = createPrometheusMdOnlyHook(createMockPluginInput())
const input = {
tool: "sisyphus_task",
tool: "delegate_task",
sessionID: TEST_SESSION_ID,
callID: "call-1",
}
+1 -1
View File
@@ -61,7 +61,7 @@ function getMessageDir(sessionID: string): string | null {
return null
}
const TASK_TOOLS = ["sisyphus_task", "task", "call_omo_agent"]
const TASK_TOOLS = ["delegate_task", "task", "call_omo_agent"]
function getAgentFromMessageFiles(sessionID: string): string | undefined {
const messageDir = getMessageDir(sessionID)
+14 -14
View File
@@ -66,8 +66,8 @@ describe("sisyphus-orchestrator hook", () => {
})
describe("tool.execute.after handler", () => {
test("should ignore non-sisyphus_task tools", async () => {
// #given - hook and non-sisyphus_task tool
test("should ignore non-delegate_task tools", async () => {
// #given - hook and non-delegate_task tool
const hook = createSisyphusOrchestratorHook(createMockPluginInput())
const output = {
title: "Test Tool",
@@ -110,7 +110,7 @@ describe("sisyphus-orchestrator hook", () => {
// #when
await hook["tool.execute.after"](
{ tool: "sisyphus_task", sessionID },
{ tool: "delegate_task", sessionID },
output
)
@@ -134,14 +134,14 @@ describe("sisyphus-orchestrator hook", () => {
// #when
await hook["tool.execute.after"](
{ tool: "sisyphus_task", sessionID },
{ tool: "delegate_task", sessionID },
output
)
// #then - standalone verification reminder appended
expect(output.output).toContain("Task completed successfully")
expect(output.output).toContain("MANDATORY:")
expect(output.output).toContain("sisyphus_task(resume=")
expect(output.output).toContain("delegate_task(resume=")
cleanupMessageStorage(sessionID)
})
@@ -171,7 +171,7 @@ describe("sisyphus-orchestrator hook", () => {
// #when
await hook["tool.execute.after"](
{ tool: "sisyphus_task", sessionID },
{ tool: "delegate_task", sessionID },
output
)
@@ -180,7 +180,7 @@ describe("sisyphus-orchestrator hook", () => {
expect(output.output).toContain("SUBAGENT WORK COMPLETED")
expect(output.output).toContain("test-plan")
expect(output.output).toContain("LIE")
expect(output.output).toContain("sisyphus_task(resume=")
expect(output.output).toContain("delegate_task(resume=")
cleanupMessageStorage(sessionID)
})
@@ -210,7 +210,7 @@ describe("sisyphus-orchestrator hook", () => {
// #when
await hook["tool.execute.after"](
{ tool: "sisyphus_task", sessionID },
{ tool: "delegate_task", sessionID },
output
)
@@ -247,7 +247,7 @@ describe("sisyphus-orchestrator hook", () => {
// #when
await hook["tool.execute.after"](
{ tool: "sisyphus_task", sessionID },
{ tool: "delegate_task", sessionID },
output
)
@@ -283,7 +283,7 @@ describe("sisyphus-orchestrator hook", () => {
// #when
await hook["tool.execute.after"](
{ tool: "sisyphus_task", sessionID },
{ tool: "delegate_task", sessionID },
output
)
@@ -320,7 +320,7 @@ describe("sisyphus-orchestrator hook", () => {
// #when
await hook["tool.execute.after"](
{ tool: "sisyphus_task", sessionID },
{ tool: "delegate_task", sessionID },
output
)
@@ -357,12 +357,12 @@ describe("sisyphus-orchestrator hook", () => {
// #when
await hook["tool.execute.after"](
{ tool: "sisyphus_task", sessionID },
{ tool: "delegate_task", sessionID },
output
)
// #then - should include resume instructions and verification
expect(output.output).toContain("sisyphus_task(resume=")
expect(output.output).toContain("delegate_task(resume=")
expect(output.output).toContain("[x]")
expect(output.output).toContain("MANDATORY:")
@@ -398,7 +398,7 @@ describe("sisyphus-orchestrator hook", () => {
// #then
expect(output.output).toContain("DELEGATION REQUIRED")
expect(output.output).toContain("ORCHESTRATOR, not an IMPLEMENTER")
expect(output.output).toContain("sisyphus_task")
expect(output.output).toContain("delegate_task")
})
test("should append delegation reminder when orchestrator edits outside .sisyphus/", async () => {
+10 -10
View File
@@ -36,7 +36,7 @@ You just performed direct file modifications outside \`.sisyphus/\`.
**You are an ORCHESTRATOR, not an IMPLEMENTER.**
As an orchestrator, you should:
- **DELEGATE** implementation work to subagents via \`sisyphus_task\`
- **DELEGATE** implementation work to subagents via \`delegate_task\`
- **VERIFY** the work done by subagents
- **COORDINATE** multiple tasks and ensure completion
@@ -46,7 +46,7 @@ You should NOT:
- Implement features yourself
**If you need to make changes:**
1. Use \`sisyphus_task\` to delegate to an appropriate subagent
1. Use \`delegate_task\` to delegate to an appropriate subagent
2. Provide clear instructions in the prompt
3. Verify the subagent's work after completion
@@ -120,7 +120,7 @@ You (orchestrator-sisyphus) are attempting to directly modify a file outside \`.
🚫 **THIS IS FORBIDDEN** (except for VERIFICATION purposes)
As an ORCHESTRATOR, you MUST:
1. **DELEGATE** all implementation work via \`sisyphus_task\`
1. **DELEGATE** all implementation work via \`delegate_task\`
2. **VERIFY** the work done by subagents (reading files is OK)
3. **COORDINATE** - you orchestrate, you don't implement
@@ -138,11 +138,11 @@ As an ORCHESTRATOR, you MUST:
**IF THIS IS FOR VERIFICATION:**
Proceed if you are verifying subagent work by making a small fix.
But for any substantial changes, USE \`sisyphus_task\`.
But for any substantial changes, USE \`delegate_task\`.
**CORRECT APPROACH:**
\`\`\`
sisyphus_task(
delegate_task(
category="...",
prompt="[specific single task with clear acceptance criteria]"
)
@@ -185,7 +185,7 @@ function buildVerificationReminder(sessionId: string): string {
**If ANY verification fails, use this immediately:**
\`\`\`
sisyphus_task(resume="${sessionId}", prompt="fix: [describe the specific failure]")
delegate_task(resume="${sessionId}", prompt="fix: [describe the specific failure]")
\`\`\``
}
@@ -656,12 +656,12 @@ export function createSisyphusOrchestratorHook(
return
}
// Check sisyphus_task - inject single-task directive
if (input.tool === "sisyphus_task") {
// Check delegate_task - inject single-task directive
if (input.tool === "delegate_task") {
const prompt = output.args.prompt as string | undefined
if (prompt && !prompt.includes(SYSTEM_DIRECTIVE_PREFIX)) {
output.args.prompt = prompt + `\n<system-reminder>${SINGLE_TASK_DIRECTIVE}</system-reminder>`
log(`[${HOOK_NAME}] Injected single-task directive to sisyphus_task`, {
log(`[${HOOK_NAME}] Injected single-task directive to delegate_task`, {
sessionID: input.sessionID,
})
}
@@ -695,7 +695,7 @@ export function createSisyphusOrchestratorHook(
return
}
if (input.tool !== "sisyphus_task") {
if (input.tool !== "delegate_task") {
return
}
+2 -2
View File
@@ -1,4 +1,4 @@
const TARGET_TOOLS = ["task", "Task", "call_omo_agent", "sisyphus_task"]
const TARGET_TOOLS = ["task", "Task", "call_omo_agent", "delegate_task"]
const SESSION_ID_PATTERNS = [
/Session ID: (ses_[a-zA-Z0-9_-]+)/,
@@ -27,7 +27,7 @@ export function createTaskResumeInfoHook() {
const sessionId = extractSessionId(output.output)
if (!sessionId) return
output.output = output.output.trimEnd() + `\n\nto resume: sisyphus_task(resume="${sessionId}", prompt="...")`
output.output = output.output.trimEnd() + `\n\nto resume: delegate_task(resume="${sessionId}", prompt="...")`
}
return {