From 571dfe23a4f2fd349db87a1ea27e3e1ce4e887b8 Mon Sep 17 00:00:00 2001 From: Ryan Dielhenn Date: Sat, 28 Mar 2026 09:34:58 -0700 Subject: [PATCH 1/6] add bool value: tdd to sisyphus_agent config --- src/config/schema/sisyphus-agent.ts | 2 ++ src/plugin/tool-registry.ts | 1 + src/tools/delegate-task/background-task.ts | 3 +- src/tools/delegate-task/executor-types.ts | 3 +- src/tools/delegate-task/prompt-builder.ts | 18 +++++++++--- src/tools/delegate-task/sync-continuation.ts | 5 ++-- src/tools/delegate-task/sync-prompt-sender.ts | 5 +++- src/tools/delegate-task/sync-task.ts | 1 + src/tools/delegate-task/tools.test.ts | 29 +++++++++++++++++++ src/tools/delegate-task/types.ts | 3 +- .../delegate-task/unstable-agent-task.ts | 5 ++-- 11 files changed, 63 insertions(+), 12 deletions(-) diff --git a/src/config/schema/sisyphus-agent.ts b/src/config/schema/sisyphus-agent.ts index 76ee2c373..0fbedc4bd 100644 --- a/src/config/schema/sisyphus-agent.ts +++ b/src/config/schema/sisyphus-agent.ts @@ -5,6 +5,8 @@ export const SisyphusAgentConfigSchema = z.object({ default_builder_enabled: z.boolean().optional(), planner_enabled: z.boolean().optional(), replace_plan: z.boolean().optional(), + /** Enable TDD-oriented planning for plan agent prompts (default: true) */ + tdd: z.boolean().default(true), }) export type SisyphusAgentConfig = z.infer diff --git a/src/plugin/tool-registry.ts b/src/plugin/tool-registry.ts index f13546977..2ce9f1d4e 100644 --- a/src/plugin/tool-registry.ts +++ b/src/plugin/tool-registry.ts @@ -75,6 +75,7 @@ export function createToolRegistry(args: { disabledSkills: skillContext.disabledSkills, availableCategories, availableSkills: skillContext.availableSkills, + sisyphusAgentConfig: pluginConfig.sisyphus_agent, syncPollTimeoutMs: pluginConfig.background_task?.syncPollTimeoutMs, onSyncSessionCreated: async (event) => { log("[index] onSyncSessionCreated callback", { diff --git a/src/tools/delegate-task/background-task.ts b/src/tools/delegate-task/background-task.ts index bc96423eb..be9b1f5b3 100644 --- a/src/tools/delegate-task/background-task.ts +++ b/src/tools/delegate-task/background-task.ts @@ -23,7 +23,8 @@ export async function executeBackgroundTask( const { manager } = executorCtx try { - const effectivePrompt = buildTaskPrompt(args.prompt, agentToUse) + const tddEnabled = executorCtx.sisyphusAgentConfig?.tdd + const effectivePrompt = buildTaskPrompt(args.prompt, agentToUse, tddEnabled) const task = await manager.launch({ description: args.description, prompt: effectivePrompt, diff --git a/src/tools/delegate-task/executor-types.ts b/src/tools/delegate-task/executor-types.ts index ad8c01879..bfa7fc70b 100644 --- a/src/tools/delegate-task/executor-types.ts +++ b/src/tools/delegate-task/executor-types.ts @@ -1,5 +1,5 @@ import type { BackgroundManager } from "../../features/background-agent" -import type { CategoriesConfig, GitMasterConfig, BrowserAutomationProvider, AgentOverrides } from "../../config/schema" +import type { CategoriesConfig, GitMasterConfig, BrowserAutomationProvider, AgentOverrides, SisyphusAgentConfig } from "../../config/schema" import type { OpencodeClient } from "./types" export interface ExecutorContext { @@ -11,6 +11,7 @@ export interface ExecutorContext { sisyphusJuniorModel?: string browserProvider?: BrowserAutomationProvider agentOverrides?: AgentOverrides + sisyphusAgentConfig?: SisyphusAgentConfig onSyncSessionCreated?: (event: { sessionID: string; parentID: string; title: string }) => Promise syncPollTimeoutMs?: number } diff --git a/src/tools/delegate-task/prompt-builder.ts b/src/tools/delegate-task/prompt-builder.ts index ddae1ce59..1672eea74 100644 --- a/src/tools/delegate-task/prompt-builder.ts +++ b/src/tools/delegate-task/prompt-builder.ts @@ -3,15 +3,24 @@ import { buildPlanAgentSystemPrepend, isPlanAgent } from "./constants" import { buildSystemContentWithTokenLimit } from "./token-limiter" const FREE_OR_LOCAL_PROMPT_TOKEN_LIMIT = 24000 -const PLAN_AGENT_PROMPT_APPEND = ` +const PLAN_AGENT_PROMPT_BASE = ` Additional requirements for this planning request: - Answer in English. - Write the plan in English. - Plan well for ultrawork execution. -- Use TDD-oriented planning. - Include a clear atomic commit strategy.` +const TDD_LINE = "- Use TDD-oriented planning." + +function buildPlanAgentPromptAppend(tddEnabled: boolean): string { + if (tddEnabled) { + return `${PLAN_AGENT_PROMPT_BASE} +${TDD_LINE}` + } + return PLAN_AGENT_PROMPT_BASE +} + function usesFreeOrLocalModel(model: { providerID: string; modelID: string; variant?: string } | undefined): boolean { if (!model) { return false @@ -61,10 +70,11 @@ export function buildSystemContent(input: BuildSystemContentInput): string | und ) } -export function buildTaskPrompt(prompt: string, agentName: string | undefined): string { +export function buildTaskPrompt(prompt: string, agentName: string | undefined, tddEnabled?: boolean): string { if (!isPlanAgent(agentName)) { return prompt } - return `${prompt}${PLAN_AGENT_PROMPT_APPEND}` + const effectiveTdd = tddEnabled ?? true + return `${prompt}${buildPlanAgentPromptAppend(effectiveTdd)}` } diff --git a/src/tools/delegate-task/sync-continuation.ts b/src/tools/delegate-task/sync-continuation.ts index 82618a289..5abe635c1 100644 --- a/src/tools/delegate-task/sync-continuation.ts +++ b/src/tools/delegate-task/sync-continuation.ts @@ -19,7 +19,7 @@ export async function executeSyncContinuation( executorCtx: ExecutorContext, deps: SyncContinuationDeps = syncContinuationDeps ): Promise { - const { client, syncPollTimeoutMs } = executorCtx + const { client, syncPollTimeoutMs, sisyphusAgentConfig } = executorCtx const toastManager = getTaskToastManager() const taskId = `resume_sync_${args.session_id!.slice(0, 8)}` const startTime = new Date() @@ -83,7 +83,8 @@ export async function executeSyncContinuation( } const allowTask = isPlanFamily(resumeAgent) - const effectivePrompt = buildTaskPrompt(args.prompt, resumeAgent) + const tddEnabled = sisyphusAgentConfig?.tdd + const effectivePrompt = buildTaskPrompt(args.prompt, resumeAgent, tddEnabled) const tools = { task: allowTask, call_omo_agent: true, diff --git a/src/tools/delegate-task/sync-prompt-sender.ts b/src/tools/delegate-task/sync-prompt-sender.ts index 502bb6dec..1140344d4 100644 --- a/src/tools/delegate-task/sync-prompt-sender.ts +++ b/src/tools/delegate-task/sync-prompt-sender.ts @@ -1,4 +1,5 @@ import type { DelegateTaskArgs, OpencodeClient, DelegatedModelConfig } from "./types" +import type { SisyphusAgentConfig } from "../../config/schema" import { isPlanFamily } from "./constants" import { buildTaskPrompt } from "./prompt-builder" import { @@ -41,11 +42,13 @@ export async function sendSyncPrompt( categoryModel: DelegatedModelConfig | undefined toastManager: { removeTask: (id: string) => void } | null | undefined taskId: string | undefined + sisyphusAgentConfig?: SisyphusAgentConfig }, deps: SendSyncPromptDeps = sendSyncPromptDeps ): Promise { const allowTask = isPlanFamily(input.agentToUse) - const effectivePrompt = buildTaskPrompt(input.args.prompt, input.agentToUse) + const tddEnabled = input.sisyphusAgentConfig?.tdd + const effectivePrompt = buildTaskPrompt(input.args.prompt, input.agentToUse, tddEnabled) const tools = { task: allowTask, call_omo_agent: true, diff --git a/src/tools/delegate-task/sync-task.ts b/src/tools/delegate-task/sync-task.ts index 554794188..b87001543 100644 --- a/src/tools/delegate-task/sync-task.ts +++ b/src/tools/delegate-task/sync-task.ts @@ -126,6 +126,7 @@ export async function executeSyncTask( categoryModel, toastManager, taskId, + sisyphusAgentConfig: executorCtx.sisyphusAgentConfig, }) if (promptError) { return promptError diff --git a/src/tools/delegate-task/tools.test.ts b/src/tools/delegate-task/tools.test.ts index b56096493..3fca12a90 100644 --- a/src/tools/delegate-task/tools.test.ts +++ b/src/tools/delegate-task/tools.test.ts @@ -3129,6 +3129,35 @@ describe("sisyphus-task", () => { // then expect(result).toBe(prompt) }) + + test("excludes TDD line when tddEnabled is false", () => { + // given + const { buildTaskPrompt } = require("./tools") + const prompt = "Create a work plan for this feature" + + // when + const result = buildTaskPrompt(prompt, "plan", false) + + // then + expect(result).toContain(prompt) + expect(result).toContain("Answer in English.") + expect(result).toContain("Write the plan in English.") + expect(result).toContain("Plan well for ultrawork execution.") + expect(result).toContain("Include a clear atomic commit strategy.") + expect(result).not.toContain("Use TDD-oriented planning.") + }) + + test("includes TDD line when tddEnabled is true", () => { + // given + const { buildTaskPrompt } = require("./tools") + const prompt = "Create a work plan for this feature" + + // when + const result = buildTaskPrompt(prompt, "plan", true) + + // then + expect(result).toContain("Use TDD-oriented planning.") + }) }) describe("modelInfo detection via resolveCategoryConfig", () => { diff --git a/src/tools/delegate-task/types.ts b/src/tools/delegate-task/types.ts index fd1c2dd43..bbb0e8e21 100644 --- a/src/tools/delegate-task/types.ts +++ b/src/tools/delegate-task/types.ts @@ -1,6 +1,6 @@ import type { PluginInput } from "@opencode-ai/plugin" import type { BackgroundManager } from "../../features/background-agent" -import type { CategoriesConfig, GitMasterConfig, BrowserAutomationProvider, AgentOverrides } from "../../config/schema" +import type { CategoriesConfig, GitMasterConfig, BrowserAutomationProvider, AgentOverrides, SisyphusAgentConfig } from "../../config/schema" import type { AvailableCategory, AvailableSkill, @@ -67,6 +67,7 @@ export interface DelegateTaskToolOptions { availableCategories?: AvailableCategory[] availableSkills?: AvailableSkill[] agentOverrides?: AgentOverrides + sisyphusAgentConfig?: SisyphusAgentConfig onSyncSessionCreated?: (event: SyncSessionCreatedEvent) => Promise syncPollTimeoutMs?: number } diff --git a/src/tools/delegate-task/unstable-agent-task.ts b/src/tools/delegate-task/unstable-agent-task.ts index ba0ec6152..57f517e18 100644 --- a/src/tools/delegate-task/unstable-agent-task.ts +++ b/src/tools/delegate-task/unstable-agent-task.ts @@ -20,12 +20,13 @@ export async function executeUnstableAgentTask( systemContent: string | undefined, actualModel: string | undefined ): Promise { - const { manager, client, syncPollTimeoutMs } = executorCtx + const { manager, client, syncPollTimeoutMs, sisyphusAgentConfig } = executorCtx let cleanupReason: string | undefined let launchedTaskID: string | undefined try { - const effectivePrompt = buildTaskPrompt(args.prompt, agentToUse) + const tddEnabled = sisyphusAgentConfig?.tdd + const effectivePrompt = buildTaskPrompt(args.prompt, agentToUse, tddEnabled) const task = await manager.launch({ description: args.description, prompt: effectivePrompt, From 2b6a02ee971f37927c3b14b6af8acff750739045 Mon Sep 17 00:00:00 2001 From: Ryan Dielhenn Date: Sat, 28 Mar 2026 09:37:13 -0700 Subject: [PATCH 2/6] nit --- src/config/schema/sisyphus-agent.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/config/schema/sisyphus-agent.ts b/src/config/schema/sisyphus-agent.ts index 0fbedc4bd..6b15b1188 100644 --- a/src/config/schema/sisyphus-agent.ts +++ b/src/config/schema/sisyphus-agent.ts @@ -5,7 +5,6 @@ export const SisyphusAgentConfigSchema = z.object({ default_builder_enabled: z.boolean().optional(), planner_enabled: z.boolean().optional(), replace_plan: z.boolean().optional(), - /** Enable TDD-oriented planning for plan agent prompts (default: true) */ tdd: z.boolean().default(true), }) From a8b3b67e8fb3116d550b0bc6775b5cb26facdc02 Mon Sep 17 00:00:00 2001 From: Ryan Dielhenn Date: Sat, 28 Mar 2026 10:16:54 -0700 Subject: [PATCH 3/6] nit --- assets/oh-my-opencode.schema.json | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/assets/oh-my-opencode.schema.json b/assets/oh-my-opencode.schema.json index e9c7a9e88..7ab3a9d01 100644 --- a/assets/oh-my-opencode.schema.json +++ b/assets/oh-my-opencode.schema.json @@ -4261,8 +4261,15 @@ }, "replace_plan": { "type": "boolean" + }, + "tdd": { + "default": true, + "type": "boolean" } }, + "required": [ + "tdd" + ], "additionalProperties": false }, "comment_checker": { @@ -4885,6 +4892,11 @@ "additionalProperties": false }, "git_master": { + "default": { + "commit_footer": true, + "include_co_authored_by": true, + "git_env_prefix": "GIT_MASTER=1" + }, "type": "object", "properties": { "commit_footer": { @@ -5035,5 +5047,8 @@ } } }, + "required": [ + "git_master" + ], "additionalProperties": false } \ No newline at end of file From e9e0aef84713585635fda95958477ac2867aaddd Mon Sep 17 00:00:00 2001 From: Ryan Dielhenn Date: Sat, 28 Mar 2026 11:10:26 -0700 Subject: [PATCH 4/6] use optional instead of required for tdd config for backwards compatibility --- assets/oh-my-opencode.schema.json | 3 --- src/config/schema/sisyphus-agent.ts | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/assets/oh-my-opencode.schema.json b/assets/oh-my-opencode.schema.json index 7ab3a9d01..3cdf6b6c4 100644 --- a/assets/oh-my-opencode.schema.json +++ b/assets/oh-my-opencode.schema.json @@ -4267,9 +4267,6 @@ "type": "boolean" } }, - "required": [ - "tdd" - ], "additionalProperties": false }, "comment_checker": { diff --git a/src/config/schema/sisyphus-agent.ts b/src/config/schema/sisyphus-agent.ts index 6b15b1188..be08dda87 100644 --- a/src/config/schema/sisyphus-agent.ts +++ b/src/config/schema/sisyphus-agent.ts @@ -5,7 +5,7 @@ export const SisyphusAgentConfigSchema = z.object({ default_builder_enabled: z.boolean().optional(), planner_enabled: z.boolean().optional(), replace_plan: z.boolean().optional(), - tdd: z.boolean().default(true), + tdd: z.boolean().default(true).optional(), }) export type SisyphusAgentConfig = z.infer From 1d13250654e408770517986ad2d1176f1ab414a8 Mon Sep 17 00:00:00 2001 From: Ryan Dielhenn Date: Sun, 29 Mar 2026 05:56:44 -0700 Subject: [PATCH 5/6] address bot review --- assets/oh-my-opencode.schema.json | 3 --- src/config/schema/oh-my-opencode-config.ts | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/assets/oh-my-opencode.schema.json b/assets/oh-my-opencode.schema.json index 3cdf6b6c4..3a1d1d4a1 100644 --- a/assets/oh-my-opencode.schema.json +++ b/assets/oh-my-opencode.schema.json @@ -5044,8 +5044,5 @@ } } }, - "required": [ - "git_master" - ], "additionalProperties": false } \ No newline at end of file diff --git a/src/config/schema/oh-my-opencode-config.ts b/src/config/schema/oh-my-opencode-config.ts index 5db7b0559..348affdf4 100644 --- a/src/config/schema/oh-my-opencode-config.ts +++ b/src/config/schema/oh-my-opencode-config.ts @@ -64,7 +64,7 @@ export const OhMyOpenCodeConfigSchema = z.object({ commit_footer: true, include_co_authored_by: true, git_env_prefix: "GIT_MASTER=1", - }), + }).optional(), browser_automation_engine: BrowserAutomationConfigSchema.optional(), websearch: WebsearchConfigSchema.optional(), tmux: TmuxConfigSchema.optional(), From f9e487f55a49677128c241847d4e05e41bb4ac0c Mon Sep 17 00:00:00 2001 From: Ryan Dielhenn Date: Sun, 29 Mar 2026 06:01:31 -0700 Subject: [PATCH 6/6] address bot review --- src/config/schema/oh-my-opencode-config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/schema/oh-my-opencode-config.ts b/src/config/schema/oh-my-opencode-config.ts index 348affdf4..5db7b0559 100644 --- a/src/config/schema/oh-my-opencode-config.ts +++ b/src/config/schema/oh-my-opencode-config.ts @@ -64,7 +64,7 @@ export const OhMyOpenCodeConfigSchema = z.object({ commit_footer: true, include_co_authored_by: true, git_env_prefix: "GIT_MASTER=1", - }).optional(), + }), browser_automation_engine: BrowserAutomationConfigSchema.optional(), websearch: WebsearchConfigSchema.optional(), tmux: TmuxConfigSchema.optional(),