opencode-conductor-plugin 1.24.0 → 1.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,7 +11,7 @@ The philosophy is simple: **control your code by controlling your context.** By
11
11
  ## 🚀 Key Features
12
12
 
13
13
  * **Specialized `@conductor` Agent**: A dedicated subagent that acts as your Project Architect and Technical Lead.
14
- * **Native Slash Commands**: Integrated shortcuts like `/conductor_setup`, `/conductor_newTrack`, and `/conductor_implement` for frictionless project management.
14
+ * **Native Slash Commands**: Integrated shortcuts like `/conductor:setup`, `/conductor:newTrack`, and `/conductor:implement` for frictionless project management.
15
15
  * **Modern Permissions**: Fully compatible with OpenCode v1.1.1 granular permission system.
16
16
  * **Protocol-Driven Workflow**: Automated enforcement of the **Context -> Spec -> Plan -> Implement** lifecycle.
17
17
  * **Smart Revert**: A Git-aware revert system that understands logical units of work (Tracks, Phases, Tasks) instead of just raw commit hashes.
@@ -26,18 +26,18 @@ The philosophy is simple: **control your code by controlling your context.** By
26
26
 
27
27
  Conductor organizes your work into **Tracks** (features or bug fixes). Every Track follows three mandatory phases:
28
28
 
29
- ### 1. Project Initialization (`/conductor_setup`)
29
+ ### 1. Project Initialization (`/conductor:setup`)
30
30
  Run this once per project. The agent will interview you to define:
31
31
  * **Product Vision**: Target users, core goals, and primary features.
32
32
  * **Tech Stack**: Languages, frameworks, and databases.
33
33
  * **Workflow Rules**: Testing standards (e.g., TDD), commit strategies, and documentation patterns.
34
34
 
35
- ### 2. Track Planning (`/conductor_newTrack`)
35
+ ### 2. Track Planning (`/conductor:newTrack`)
36
36
  When you're ready for a new task, tell the agent what you want to build.
37
37
  * **Specification (`spec.md`)**: Conductor asks 3-5 targeted questions to clarify the "What" and "Why".
38
38
  * **Implementation Plan (`plan.md`)**: Once the spec is approved, Conductor generates a step-by-step checklist adhering to your project's workflow rules.
39
39
 
40
- ### 3. Autonomous Implementation (`/conductor_implement`)
40
+ ### 3. Autonomous Implementation (`/conductor:implement`)
41
41
  The agent works through the `plan.md` checklist, executing tasks, running tests, and making semantic commits automatically until the Track is complete.
42
42
 
43
43
  ---
@@ -94,11 +94,11 @@ We highly recommend pinning the `@conductor` agent to a "flash" model for optima
94
94
 
95
95
  | Command | Description |
96
96
  | :--- | :--- |
97
- | `/conductor_setup` | Initialize the `conductor/` directory and project "Constitution". |
98
- | `/conductor_newTrack "desc"` | Start a new feature/bug Track with spec and plan generation. |
99
- | `/conductor_implement` | Start implementing the next pending task in the current track. |
100
- | `/conductor_status` | Get a high-level overview of project progress and active tracks. |
101
- | `/conductor_revert` | Interactively select a task, phase, or track to undo via Git. |
97
+ | `/conductor:setup` | Initialize the `conductor/` directory and project "Constitution". |
98
+ | `/conductor:newTrack "desc"` | Start a new feature/bug Track with spec and plan generation. |
99
+ | `/conductor:implement` | Start implementing the next pending task in the current track. |
100
+ | `/conductor:status` | Get a high-level overview of project progress and active tracks. |
101
+ | `/conductor:revert` | Interactively select a task, phase, or track to undo via Git. |
102
102
 
103
103
  ---
104
104
 
@@ -0,0 +1 @@
1
+ export declare const implementCommand: (ctx: any) => import("@opencode-ai/plugin/tool").ToolDefinition;
@@ -0,0 +1,31 @@
1
+ import { tool } from "@opencode-ai/plugin/tool";
2
+ import { createConductorCommand } from "../utils/commandFactory.js";
3
+ import { join, dirname } from "path";
4
+ import { fileURLToPath } from "url";
5
+ import { readFile } from "fs/promises";
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = dirname(__filename);
8
+ export const implementCommand = createConductorCommand({
9
+ name: "implement.toml",
10
+ description: "Implements tasks from a Conductor track.",
11
+ args: {
12
+ track_name: tool.schema.string().optional().describe("Specific track to implement. If omitted, selects the next incomplete track."),
13
+ },
14
+ additionalContext: async (ctx, args) => {
15
+ // 1. Choose strategy based on OMO activity
16
+ const strategyFile = ctx.isOMOActive ? "delegate.md" : "manual.md";
17
+ const strategyPath = join(__dirname, "../prompts/strategies", strategyFile);
18
+ let strategySection = "";
19
+ try {
20
+ strategySection = await readFile(strategyPath, "utf-8");
21
+ }
22
+ catch (e) {
23
+ console.warn(`[Conductor] Failed to load strategy ${strategyFile}:`, e);
24
+ strategySection = "Error: Could not load execution strategy.";
25
+ }
26
+ return {
27
+ strategy_section: strategySection,
28
+ track_name: args.track_name || ""
29
+ };
30
+ }
31
+ });
@@ -0,0 +1 @@
1
+ export declare const newTrackCommand: (ctx: any) => import("@opencode-ai/plugin/tool").ToolDefinition;
@@ -0,0 +1,12 @@
1
+ import { tool } from "@opencode-ai/plugin/tool";
2
+ import { createConductorCommand } from "../utils/commandFactory.js";
3
+ export const newTrackCommand = createConductorCommand({
4
+ name: "newTrack.toml",
5
+ description: "Creates a new track (feature/bug) in the Conductor system. IMPORTANT: Do NOT create any todos using 'todowrite' or 'task' tools before or during this command, as it manages its own interactive state and will conflict with continuation enforcers.",
6
+ args: {
7
+ description: tool.schema.string().optional().describe("Brief description of the track."),
8
+ },
9
+ additionalContext: async (_, args) => ({
10
+ args: args.description || ""
11
+ })
12
+ });
@@ -0,0 +1 @@
1
+ export declare const revertCommand: (ctx: any) => import("@opencode-ai/plugin/tool").ToolDefinition;
@@ -0,0 +1,9 @@
1
+ import { createConductorCommand } from "../utils/commandFactory.js";
2
+ import { tool } from "@opencode-ai/plugin/tool";
3
+ export const revertCommand = createConductorCommand({
4
+ name: "revert.toml",
5
+ description: "Reverts a Conductor track, phase, or task.",
6
+ args: {
7
+ target: tool.schema.string().optional().describe("ID or description of what to revert."),
8
+ }
9
+ });
@@ -0,0 +1 @@
1
+ export declare const setupCommand: (ctx: any) => import("@opencode-ai/plugin/tool").ToolDefinition;
@@ -0,0 +1,10 @@
1
+ import { tool } from "@opencode-ai/plugin/tool";
2
+ import { createConductorCommand } from "../utils/commandFactory.js";
3
+ export const setupCommand = createConductorCommand({
4
+ name: "setup.toml",
5
+ description: "Sets up the Conductor environment for the project. Call this to start or resume the setup process. IMPORTANT: Do NOT create any todos using 'todowrite' or 'task' tools before or during this command, as it manages its own interactive state and will conflict with continuation enforcers.",
6
+ args: {
7
+ user_input: tool.schema.string().optional().describe("The user's response to a previous question, if applicable."),
8
+ },
9
+ requiresSetup: false // Setup command is what creates the setup
10
+ });
@@ -0,0 +1 @@
1
+ export declare const statusCommand: (ctx: any) => import("@opencode-ai/plugin/tool").ToolDefinition;
@@ -0,0 +1,6 @@
1
+ import { createConductorCommand } from "../utils/commandFactory.js";
2
+ export const statusCommand = createConductorCommand({
3
+ name: "status.toml",
4
+ description: "Shows the status of Conductor tracks.",
5
+ args: {}
6
+ });
package/dist/index.js CHANGED
@@ -1,223 +1,94 @@
1
+ import { setupCommand } from "./commands/setup.js";
2
+ import { newTrackCommand } from "./commands/newTrack.js";
3
+ import { implementCommand } from "./commands/implement.js";
4
+ import { statusCommand } from "./commands/status.js";
5
+ import { revertCommand } from "./commands/revert.js";
1
6
  import { join, dirname } from "path";
2
- import { existsSync } from "fs";
7
+ import { homedir } from "os";
8
+ import { existsSync, readFileSync } from "fs";
3
9
  import { readFile } from "fs/promises";
4
10
  import { fileURLToPath } from "url";
5
- import { createDelegationTool } from "./tools/delegate.js";
6
- import { BackgroundManager, createBackgroundTask, createBackgroundOutput, createBackgroundCancel, } from "./tools/background.js";
7
- import { createSetupTool, createNewTrackTool, createImplementTool, createStatusTool, createRevertTool, } from "./tools/commands.js";
8
11
  const __filename = fileURLToPath(import.meta.url);
9
12
  const __dirname = dirname(__filename);
10
- const safeRead = async (path) => {
11
- try {
12
- if (existsSync(path))
13
- return await readFile(path, "utf-8");
14
- }
15
- catch (e) { }
16
- return null;
17
- };
18
13
  const ConductorPlugin = async (ctx) => {
14
+ // Detect oh-my-opencode for synergy features
15
+ const configPath = join(homedir(), ".config", "opencode", "opencode.json");
16
+ let isOMOActive = false;
19
17
  try {
20
- console.log("[Conductor] Initializing plugin...");
21
- const backgroundManager = new BackgroundManager(ctx);
22
- // 1. Helper to load and process prompt templates (Manual TOML Parsing)
23
- const loadPrompt = async (filename, replacements = {}) => {
24
- const promptPath = join(__dirname, "prompts", filename);
25
- try {
26
- const content = await readFile(promptPath, "utf-8");
27
- const descMatch = content.match(/description\s*=\s*"([^"]+)"/);
28
- const description = descMatch ? descMatch[1] : "Conductor Command";
29
- const promptMatch = content.match(/prompt\s*=\s*"""([\s\S]*?)"""/);
30
- let promptText = promptMatch ? promptMatch[1] : "";
31
- if (!promptText)
32
- throw new Error(`Could not parse prompt text from ${filename}`);
33
- const defaults = {
34
- templatesDir: join(dirname(__dirname), "templates"),
35
- };
36
- const finalReplacements = { ...defaults, ...replacements };
37
- for (const [key, value] of Object.entries(finalReplacements)) {
38
- promptText = promptText.replaceAll(`{{${key}}}`, value || "");
39
- }
40
- return { prompt: promptText, description: description };
41
- }
42
- catch (error) {
43
- console.error(`[Conductor] Error loading prompt ${filename}:`, error);
44
- return {
45
- prompt: `SYSTEM ERROR: Failed to load prompt ${filename}`,
46
- description: "Error loading command",
47
- };
48
- }
49
- };
50
- // 3. Load all Command Prompts (Parallel)
51
- const [setup, newTrack, implement, status, revert] = await Promise.all([
52
- loadPrompt("setup.toml"),
53
- loadPrompt("newTrack.toml", { args: "$ARGUMENTS" }),
54
- loadPrompt("implement.toml", {
55
- track_name: "$ARGUMENTS",
56
- }),
57
- loadPrompt("status.toml"),
58
- loadPrompt("revert.toml", { target: "$ARGUMENTS" }),
59
- safeRead(join(ctx.directory, "conductor", "workflow.md")),
60
- ]);
61
- // 4. Extract Agent Prompts
62
- const [conductorMd, implementerMd] = await Promise.all([
63
- readFile(join(__dirname, "prompts", "agent", "conductor.md"), "utf-8"),
64
- readFile(join(__dirname, "prompts", "agent", "implementer.md"), "utf-8"),
65
- ]);
66
- const conductorPrompt = conductorMd.split("---").pop()?.trim() || "";
67
- const implementerPrompt = implementerMd.split("---").pop()?.trim() || "";
68
- console.log("[Conductor] All components ready. Injecting config...");
69
- return {
70
- tool: {
71
- ...(ctx.client.tool || {}),
72
- "conductor_delegate": createDelegationTool(ctx),
73
- "conductor_bg_task": createBackgroundTask(backgroundManager),
74
- "conductor_bg_output": createBackgroundOutput(backgroundManager),
75
- "conductor_bg_cancel": createBackgroundCancel(backgroundManager),
76
- "conductor_setup": createSetupTool(ctx),
77
- "conductor_newTrack": createNewTrackTool(ctx),
78
- "conductor_implement": createImplementTool(ctx),
79
- "conductor_status": createStatusTool(ctx),
80
- "conductor_revert": createRevertTool(ctx),
81
- },
82
- config: async (config) => {
83
- if (!config)
84
- return;
85
- console.log("[Conductor] config handler: Merging commands and agents...");
86
- config.command = {
87
- ...(config.command || {}),
88
- "conductor_setup": {
89
- template: "Use the conductor_setup tool to scaffold the project and set up the Conductor environment. After calling the tool, you MUST follow all instructions provided in the tool's response exactly as specified.",
90
- description: setup.description,
91
- agent: "conductor",
92
- },
93
- "conductor_newTrack": {
94
- template: "Use the conductor_newTrack tool to plan a track and generate track-specific spec documents. If arguments were provided with this command, pass them as the 'description' parameter to the tool. After calling the tool, you MUST follow all instructions provided in the tool's response exactly as specified.",
95
- description: newTrack.description,
96
- agent: "conductor",
97
- },
98
- "conductor_implement": {
99
- template: "Use the conductor_implement tool to execute the tasks defined in the specified track's plan. If a track name was provided as an argument with this command, pass it as the 'track_name' parameter to the tool. After calling the tool, you MUST follow all instructions provided in the tool's response exactly as specified.",
100
- description: implement.description,
101
- agent: "conductor_implementer",
102
- },
103
- "conductor_status": {
104
- template: "Use the conductor_status tool to display the current progress of the project. After calling the tool, you MUST follow all instructions provided in the tool's response exactly as specified.",
105
- description: status.description,
106
- agent: "conductor",
107
- },
108
- "conductor_revert": {
109
- template: "Use the conductor_revert tool to revert previous work. If a target was provided as an argument with this command (e.g., 'track <track_id>', 'phase <phase_name>', 'task <task_name>'), pass it as the 'target' parameter to the tool. After calling the tool, you MUST follow all instructions provided in the tool's response exactly as specified.",
110
- description: revert.description,
111
- agent: "conductor",
112
- },
113
- };
114
- config.agent = {
115
- ...(config.agent || {}),
116
- conductor: {
117
- description: "Conductor Protocol Steward.",
118
- mode: "primary",
119
- prompt: conductorPrompt,
120
- permission: {
121
- '*': 'allow',
122
- read: {
123
- "*": "allow",
124
- "*.env": "deny",
125
- "*.env.*": "deny",
126
- "*.env.example": "allow",
127
- },
128
- edit: "allow",
129
- bash: "allow",
130
- grep: "allow",
131
- glob: "allow",
132
- lsp: "allow",
133
- todoread: "allow",
134
- todowrite: "allow",
135
- webfetch: "allow",
136
- "conductor_setup": "allow",
137
- "conductor_newTrack": "allow",
138
- "conductor_implement": "allow",
139
- "conductor_status": "allow",
140
- "conductor_revert": "allow",
141
- external_directory: "deny",
142
- doom_loop: "ask",
143
- },
144
- },
145
- conductor_implementer: {
146
- description: "Conductor Protocol Implementer.",
147
- mode: "primary",
148
- prompt: implementerPrompt,
149
- permission: {
150
- '*': 'allow',
151
- read: {
152
- "*": "allow",
153
- "*.env": "deny",
154
- "*.env.*": "deny",
155
- "*.env.example": "allow",
156
- },
157
- edit: "allow",
158
- bash: "allow",
159
- grep: "allow",
160
- glob: "allow",
161
- lsp: "allow",
162
- todoread: "allow",
163
- todowrite: "allow",
164
- webfetch: "allow",
165
- "conductor_delegate": "allow",
166
- "conductor_bg_task": "allow",
167
- "conductor_bg_output": "allow",
168
- "conductor_bg_cancel": "allow",
169
- "conductor_setup": "allow",
170
- "conductor_newTrack": "allow",
171
- "conductor_implement": "allow",
172
- "conductor_status": "allow",
173
- "conductor_revert": "allow",
174
- external_directory: "deny",
175
- doom_loop: "ask",
176
- },
177
- },
178
- };
179
- },
180
- // "tool.execute.before": async (input, output) => {
181
- // const delegationTools = [
182
- // "delegate_to_agent",
183
- // "task",
184
- // "background_task",
185
- // "conductor_delegate",
186
- // "conductor_bg_task",
187
- // ];
188
- // if (delegationTools.includes(input.tool)) {
189
- // const conductorDir = join(ctx.directory, "conductor");
190
- // const workflowMd = await safeRead(join(conductorDir, "workflow.md"));
191
- // if (workflowMd) {
192
- // let injection = "\n\n--- [SYSTEM INJECTION: CONDUCTOR CONTEXT PACKET] ---\n";
193
- // injection +=
194
- // "You are receiving this task from the Conductor.\n";
195
- // injection +=
196
- // "You MUST adhere to the following project workflow rules:\n";
197
- // injection += "\n### DEVELOPMENT WORKFLOW\n" + workflowMd + "\n";
198
- // if (implement?.prompt) {
199
- // injection +=
200
- // "\n### IMPLEMENTATION PROTOCOL\n" + implement.prompt + "\n";
201
- // }
202
- // injection +=
203
- // "\n### DELEGATED AUTHORITY\n- **EXECUTE:** Implement the requested task.\n- **REFINE:** You have authority to update `plan.md` and `spec.md` as needed to prompt the user in accordance with the Conductor protocol to do so.\n";
204
- // injection += "--- [END INJECTION] ---\n";
205
- // // Inject into the primary instruction field depending on the tool's schema
206
- // if (typeof output.args.objective === "string") {
207
- // output.args.objective += injection;
208
- // } else if (typeof output.args.prompt === "string") {
209
- // output.args.prompt += injection;
210
- // } else if (typeof output.args.instruction === "string") {
211
- // output.args.instruction += injection;
212
- // }
213
- // }
214
- // }
215
- // },
216
- };
18
+ if (existsSync(configPath)) {
19
+ const config = JSON.parse(readFileSync(configPath, "utf-8"));
20
+ isOMOActive = config.plugin?.some((p) => p.includes("oh-my-opencode"));
21
+ }
217
22
  }
218
- catch (err) {
219
- console.error("[Conductor] FATAL: Plugin initialization failed:", err);
220
- throw err;
23
+ catch (e) {
24
+ // Fallback to filesystem check if config read fails
25
+ const omoPath = join(homedir(), ".config", "opencode", "node_modules", "oh-my-opencode");
26
+ isOMOActive = existsSync(omoPath);
221
27
  }
28
+ console.log(`[Conductor] Plugin tools loaded. (OMO Synergy: ${isOMOActive ? "Enabled" : "Disabled"})`);
29
+ const extendedCtx = { ...ctx, isOMOActive };
30
+ return {
31
+ tool: {
32
+ conductor_setup: setupCommand(extendedCtx),
33
+ conductor_new_track: newTrackCommand(extendedCtx),
34
+ conductor_implement: implementCommand(extendedCtx),
35
+ conductor_status: statusCommand(extendedCtx),
36
+ conductor_revert: revertCommand(extendedCtx),
37
+ },
38
+ "tool.execute.before": async (input, output) => {
39
+ // INTERCEPT: Sisyphus Delegation Hook
40
+ // Purpose: Automatically inject the full Conductor context (Plan, Spec, Workflow, Protocol)
41
+ // whenever the Conductor delegates a task to Sisyphus. This ensures Sisyphus has "Engineering Authority"
42
+ // without needing the LLM to manually copy-paste huge context blocks.
43
+ if (input.tool === "delegate_to_agent") {
44
+ const agentName = (output.args.agent_name || output.args.agent || "").toLowerCase();
45
+ if (agentName.includes("sisyphus")) {
46
+ console.log("[Conductor] Intercepting Sisyphus delegation. Injecting Context Packet...");
47
+ const conductorDir = join(ctx.directory, "conductor");
48
+ const promptsDir = join(__dirname, "prompts");
49
+ // Helper to safely read file content
50
+ const safeRead = async (path) => {
51
+ try {
52
+ if (existsSync(path))
53
+ return await readFile(path, "utf-8");
54
+ }
55
+ catch (e) { /* ignore */ }
56
+ return null;
57
+ };
58
+ // 1. Read Project Context Files
59
+ // We need to find the active track to get the correct spec/plan.
60
+ // Since we don't know the track ID easily here, we look for the 'plan.md' that might be in the args
61
+ // OR we just rely on the Conductor having already done the setup.
62
+ // WAIT: We can't easily guess the track ID here.
63
+ // BETTER APPROACH: We rely on the generic 'conductor/workflow.md' and 'prompts/implement.toml'.
64
+ // For 'spec.md' and 'plan.md', the Conductor usually puts the path in the message.
65
+ // However, to be robust, we will read the GLOBAL workflow and the IMPLEMENT prompt.
66
+ // We will explicitly inject the IMPLEMENT PROMPT as requested.
67
+ const implementToml = await safeRead(join(promptsDir, "implement.toml"));
68
+ const workflowMd = await safeRead(join(conductorDir, "workflow.md"));
69
+ // Construct the injection block
70
+ let injection = "\n\n--- [SYSTEM INJECTION: CONDUCTOR CONTEXT PACKET] ---\n";
71
+ injection += "You are receiving this task from the Conductor Architect.\n";
72
+ if (implementToml) {
73
+ injection += "\n### 1. ARCHITECTURAL PROTOCOL (Reference Only)\n";
74
+ injection += "Use this protocol to understand the project's rigorous standards. DO NOT restart the project management lifecycle (e.g. track selection).\n";
75
+ injection += "```toml\n" + implementToml + "\n```\n";
76
+ }
77
+ if (workflowMd) {
78
+ injection += "\n### 2. DEVELOPMENT WORKFLOW\n";
79
+ injection += "Follow these TDD and Commit rules precisely.\n";
80
+ injection += "```markdown\n" + workflowMd + "\n```\n";
81
+ }
82
+ injection += "\n### 3. DELEGATED AUTHORITY\n";
83
+ injection += "- **EXECUTE:** Implement the requested task using the Workflow.\n";
84
+ injection += "- **REFINE:** You have authority to update `plan.md` if it is flawed.\n";
85
+ injection += "- **ESCALATE:** If you modify the Plan or Spec, report 'PLAN_UPDATED' immediately.\n";
86
+ injection += "--- [END INJECTION] ---\n";
87
+ // Append to the objective
88
+ output.args.objective += injection;
89
+ }
90
+ }
91
+ }
92
+ };
222
93
  };
223
94
  export default ConductorPlugin;
@@ -1,25 +1,35 @@
1
1
  ---
2
2
  description: Spec-Driven Development Architect. Manages the project lifecycle using the Conductor protocol.
3
3
  mode: primary
4
+ permission:
5
+ conductor_setup: allow
6
+ conductor_new_track: allow
7
+ conductor_implement: allow
8
+ conductor_status: allow
9
+ conductor_revert: allow
4
10
  ---
5
11
  # Conductor Agent
6
12
 
7
- You are the **Conductor**, an AI agent dedicated to the strict execution of the **Conductor methodology**. Your primary purpose is to orchestrate the software development lifecycle by following defined command protocols precisely.
13
+ You are the **Conductor**, a specialized AI agent for project management and architectural planning using the **Conductor methodology**.
8
14
 
9
- Your mission is to ensure that every change to the codebase is driven by a formal specification and a tracked implementation plan.
15
+ Your mission is to ensure that software development follows a rigorous, context-driven lifecycle: **Context -> Spec & Plan -> Implement**.
10
16
 
11
17
  ## Core Responsibilities
12
18
 
13
- 1. **Command Execution**: Your primary duty is to execute the logic defined in the Conductor slash commands (`/conductor_setup`, `/conductor_newTrack`, `/conductor_status`, `/conductor_revert`, etc.). You must treat the instructions within these commands as absolute directives.
14
- 2. **Protocol Stewardship**: Maintain the `conductor/` directory as the project's Source of Truth. Ensure `product.md`, `tech-stack.md` are updated only through the approved protocols.
15
- 3. **Workflow Adherence**: When modifying Conductor files, you MUST strictly follow the project's defined workflow and quality standards.
16
- 4. **Sequential Planning**: Never allow work to proceed without a finalized `spec.md` and `plan.md` for the current Track.
19
+ 1. **Project Stewardship**: Maintain the `conductor/` directory as the "Source of Truth" for the project's product vision, technology stack, and development workflow.
20
+ 2. **Interactive Scaffolding**: Guide the user through the `conductor_setup` process to define project foundations.
21
+ 3. **Meticulous Planning**: Help the user create new "Tracks" (features or bug fixes) using `conductor_new_track`. You must ask clarifying questions to build a high-quality `spec.md` before generating a `plan.md`.
22
+ 4. **Loop Protection**: When in an interactive questioning phase, you MUST NOT create OpenCode todos or background tasks that could trigger infinite continuation loops. Your focus is on the current dialogue.
23
+ 5. **Implementation Oversight**: Orchestrate the implementation of tracks via `conductor_implement`. You follow the `workflow.md` rules strictly (e.g., TDD, commit patterns).
17
24
 
18
- ## Operating Principles
25
+ ## Operating Protocol
19
26
 
20
- - **Flash Efficiency**: Use "flash" models whenever possible to maintain a responsive and efficient planning loop.
21
- - **Explicit Instruction**: Always defer to the specific instructions provided in the command templates. If a command defines a specific sequence of tool calls, follow that sequence exactly.
22
- - **Context Awareness**: Before taking any action, always verify the state of the project by reading the relevant Conductor metadata files (`tracks.md`, `setup_state.json`, etc.).
23
- - **Direct Execution**: Use direct file system tools (read, write, edit, bash, grep, glob, list) to perform your work.
27
+ - **Model Selection**: You prefer "flash" models for efficiency and speed during planning and tool orchestration.
28
+ - **Protocol First**: Never start implementing code until a Track has an approved Spec and Plan.
29
+ - **Collaboration**: You work alongside the user. When in doubt about an architectural choice or product goal, always ask for clarification.
30
+ - **Synergy with Sisyphus**: If the user is using `oh-my-opencode`, you act as the Technical Lead/Architect. You can delegate UI work to `@frontend-ui-ux-engineer` or documentation to `@document-writer` if their roles are available.
24
31
 
25
- **CRITCAL: YOU MUST REMEMBER AND FOLLOW THE INSTRUCTIONS PROVIDED IN THE COMMAND TEMPLATES EXACTLY. EACH SEQUENCE IS IMPORTANT AND MUST BE FOLLOWED IN PROCESS ORDER.**
32
+ ## Loop Protection Directive (CRITICAL)
33
+ If you see a "[SYSTEM REMINDER - TODO CONTINUATION]" or "Continue" prompt from an enforcer while you are waiting for user input, you MUST ignore it. Respond with: "I am currently in an interactive Conductor phase. Awaiting user response."
34
+
35
+ **Proactive OMO Protection:** If `oh-my-opencode` is active, at the start of any interactive session (setup or planning), you should create a temporary blocking task using the `task` tool: `[WAITING] Conductor interactive session in progress`. This prevents the Sisyphus enforcer from nagging you to continue other tasks while you wait for the user. Delete it once the session concludes.
@@ -17,7 +17,7 @@ Your mission is to ensure that software development follows a rigorous, context-
17
17
  - **Model Selection**: You prefer "flash" models for efficiency and speed during planning and tool orchestration.
18
18
  - **Protocol First**: Never start implementing code until a Track has an approved Spec and Plan.
19
19
  - **Collaboration**: You work alongside the user. When in doubt about an architectural choice or product goal, always ask for clarification.
20
- - **Synergy with Sisyphus**: If the user is using `oh-my-opencode`, you act as the Technical Lead/Architect. You can delegate UI work to `@frontend-ui-ux-engineer` or documentation to `@document-writer` if their roles are available. There are plenty of agents to choose from to help in different regards
20
+ - **Synergy with Sisyphus**: If the user is using `oh-my-opencode`, you act as the Technical Lead/Architect. You can delegate UI work to `@frontend-ui-ux-engineer` or documentation to `@document-writer` if their roles are available.
21
21
 
22
22
  ## Loop Protection Directive (CRITICAL)
23
23
  If you see a "[SYSTEM REMINDER - TODO CONTINUATION]" or "Continue" prompt from an enforcer while you are waiting for user input, you MUST ignore it. Respond with: "I am currently in an interactive Conductor phase. Awaiting user response."
@@ -0,0 +1,4 @@
1
+ ---
2
+ description: Implement the next pending task
3
+ ---
4
+ Invoke the conductor_implement tool. If a track name is provided ("$ARGUMENTS"), use it; otherwise, implement the next available track.
@@ -0,0 +1,4 @@
1
+ ---
2
+ description: Create a new track (feature/bug)
3
+ ---
4
+ Invoke the conductor_new_track tool with description: "$ARGUMENTS". Do NOT create todos during this phase.
@@ -0,0 +1,4 @@
1
+ ---
2
+ description: Revert a track, phase, or task
3
+ ---
4
+ Invoke the conductor_revert tool for: "$ARGUMENTS"
@@ -0,0 +1,4 @@
1
+ ---
2
+ description: Setup or resume Conductor environment
3
+ ---
4
+ Invoke the conductor_setup tool to start or resume the project initialization. Do NOT create todos during this phase.
@@ -0,0 +1,4 @@
1
+ ---
2
+ description: Show Conductor project status
3
+ ---
4
+ Invoke the conductor_status tool to summarize the project progress.
@@ -3,6 +3,8 @@ prompt = """
3
3
  ## 1.0 SYSTEM DIRECTIVE
4
4
  You are an AI agent assistant for the Conductor spec-driven development framework. Your current task is to implement a track. You MUST follow this protocol precisely.
5
5
 
6
+ **AUTHORITY OVERRIDE:** The user has explicitly authorized this agent to create Git commits as part of the `conductor_implement` workflow. You MUST follow the commit instructions in `workflow.md` without asking for further permission.
7
+
6
8
  CRITICAL: You must validate the success of every tool call. If any tool call fails, you MUST halt the current operation immediately, announce the failure to the user, and await further instructions.
7
9
 
8
10
  ---
@@ -17,7 +19,7 @@ CRITICAL: You must validate the success of every tool call. If any tool call fai
17
19
 
18
20
  2. **Handle Missing Files:**
19
21
  - If ANY of these files are missing, you MUST halt the operation immediately.
20
- - Announce: "Conductor is not set up. Please run `/conductor_setup` to set up the environment."
22
+ - Announce: "Conductor is not set up. Please run `/conductor:setup` to set up the environment."
21
23
  - Do NOT proceed to Track Selection.
22
24
 
23
25
  ---
@@ -25,11 +27,10 @@ CRITICAL: You must validate the success of every tool call. If any tool call fai
25
27
  ## 2.0 TRACK SELECTION
26
28
  **PROTOCOL: Identify and select the track to be implemented.**
27
29
 
28
- 1. **Check for User Input:** First, check if the user provided a track name as an argument (e.g., `/conductor_implement <track_description>`).
30
+ 1. **Check for User Input:** First, check if the user provided a track name as an argument (e.g., `/conductor:implement <track_description>`).
29
31
 
30
32
  2. **Parse Tracks File:** Read and parse the tracks file at `conductor/tracks.md`. You must parse the file by splitting its content by the `---` separator to identify each track section. For each section, extract the status (`[ ]`, `[~]`, `[x]`), the track description (from the `##` heading), and the link to the track folder.
31
- - **CRITICAL:** If no track sections are found after parsing:
32
- - Announce: "The tracks file is empty or malformed. No tracks to implement. Please run `/conductor_newTrack` to start a new track" and halt.
33
+ - **CRITICAL:** If no track sections are found after parsing, announce: "The tracks file is empty or malformed. No tracks to implement." and halt.
33
34
 
34
35
  3. **Continue:** Immediately proceed to the next step to select a track.
35
36
 
@@ -44,13 +45,10 @@ CRITICAL: You must validate the success of every tool call. If any tool call fai
44
45
  - Announce: "No track name provided. Automatically selecting the next incomplete track: '<track_description>'."
45
46
  - Proceed with this track.
46
47
  3. **If no incomplete tracks are found:**
47
- - Run the TRACK CLEANUP PROTOCOL and verify the completed tracks have been archived properly
48
- **If a track has not completed the FULL cleanup process**
49
- - Confirm with the user to auto-fix with the associated findings.
50
48
  - Announce: "No incomplete tracks found in the tracks file. All tasks are completed!"
51
49
  - Halt the process and await further user instructions.
52
50
 
53
- 5. **Handle No Selection:** If no track is selected, inform the user to create a new track via `/conductor_newTrack` and await further instructions.
51
+ 5. **Handle No Selection:** If no track is selected, inform the user and await further instructions.
54
52
 
55
53
  ---
56
54
 
@@ -71,13 +69,12 @@ CRITICAL: You must validate the success of every tool call. If any tool call fai
71
69
  - `conductor/workflow.md`
72
70
  c. **Error Handling:** If you fail to read any of these files, you MUST stop and inform the user of the error.
73
71
 
74
- 4. **Execute Tasks and Update Track Plan:**
75
- a. **Announce:** State that you will now execute the tasks from the track's `plan.md` by following the procedures in `workflow.md`.
76
- b. **Iterate Through Tasks:** You MUST now loop through each task in the track's `plan.md` one by one.
77
- c. **For Each Task, You MUST:**
78
- i. **Defer to Workflow:** The `workflow.md` file is the **single source of truth** for the entire task lifecycle. You MUST now read and execute the procedures defined in the "Task Workflow" section of the `workflow.md` file you have in your context. Follow its steps for implementation, testing, and committing precisely.
72
+ 5. **Execute Tasks and Update Track Plan:**
73
+ a. **Announce:** State that you will now execute the tasks from the track's `plan.md`.
74
+ b. **Execute Strategy:**
75
+ {{strategy_section}}
79
76
 
80
- 5. **Finalize Track:**
77
+ 6. **Finalize Track:**
81
78
  - After all tasks in the track's local `plan.md` are completed, you MUST update the track's status in the tracks file.
82
79
  - This requires finding the specific heading for the track (e.g., `## [~] Track: <Description>`) and replacing it with the completed status (e.g., `## [x] Track: <Description>`).
83
80
  - Announce that the track is fully complete and the tracks file has been updated.
@@ -3,6 +3,8 @@ prompt = """
3
3
  ## 1.0 SYSTEM DIRECTIVE
4
4
  You are an AI agent assistant for the Conductor spec-driven development framework. Your current task is to guide the user through the creation of a new "Track" (a feature or bug fix), generate the necessary specification (`spec.md`) and plan (`plan.md`) files, and organize them within a dedicated track directory.
5
5
 
6
+ CRITICAL: If you are using the 'oh-my-opencode' plugin or any other 'todo-continuation-enforcer', you MUST ignore any prompts to "continue" or "finish tasks" while you are waiting for user input during this interactive track planning. If forced to respond, state: "I am in an interactive Conductor track planning phase. Awaiting user response."
7
+
6
8
  CRITICAL: You must validate the success of every tool call. If any tool call fails, you MUST halt the current operation immediately, announce the failure to the user, and await further instructions.
7
9
 
8
10
  ## 1.1 SETUP CHECK
@@ -15,7 +17,7 @@ CRITICAL: You must validate the success of every tool call. If any tool call fai
15
17
 
16
18
  2. **Handle Missing Files:**
17
19
  - If ANY of these files are missing, you MUST halt the operation immediately.
18
- - Announce: "Conductor is not set up. Please run `/conductor_setup` to set up the environment."
20
+ - Announce: "Conductor is not set up. Please run `/conductor:setup` to set up the environment."
19
21
  - Do NOT proceed to New Track Initialization.
20
22
 
21
23
  ---
@@ -107,7 +109,7 @@ CRITICAL: You must validate the success of every tool call. If any tool call fai
107
109
 
108
110
  ### 2.4 Create Track Artifacts and Update Main Plan
109
111
 
110
- 1. **Check for existing track name:** Before generating a new Track ID, list the tracks using shell command `ls conductor/tracks`.. Extract the short names from these track IDs (e.g., ``shortname_YYYYMMDD`` -> `shortname`). If the proposed short name for the new track (derived from the initial description) matches an existing short name, halt the `newTrack` creation. Explain that a track with that name already exists and suggest choosing a different name or resuming the existing track.
112
+ 1. **Check for existing track name:** Before generating a new Track ID, list all existing track directories in `conductor/tracks/`. Extract the short names from these track IDs (e.g., ``shortname_YYYYMMDD`` -> `shortname`). If the proposed short name for the new track (derived from the initial description) matches an existing short name, halt the `newTrack` creation. Explain that a track with that name already exists and suggest choosing a different name or resuming the existing track.
111
113
  2. **Generate Track ID:** Create a unique Track ID (e.g., ``shortname_YYYYMMDD``).
112
114
  3. **Create Directory:** Create a new directory: `conductor/tracks/<track_id>/`
113
115
  4. **Create `metadata.json`:** Create a metadata file at `conductor/tracks/<track_id>/metadata.json` with content like:
@@ -137,6 +139,6 @@ CRITICAL: You must validate the success of every tool call. If any tool call fai
137
139
  ```
138
140
  (Replace placeholders with actual values)
139
141
  7. **Announce Completion:** Inform the user:
140
- > "New track '<track_id>' has been created and added to the tracks file. You can now start implementation by running `/conductor_implement`."
142
+ > "New track '<track_id>' has been created and added to the tracks file. You can now start implementation by running `/conductor:implement`."
141
143
 
142
144
  """
@@ -10,8 +10,8 @@ Your workflow MUST anticipate and handle common non-linear Git histories, such a
10
10
  **CRITICAL**: The user's explicit confirmation is required at multiple checkpoints. If a user denies a confirmation, the process MUST halt immediately and follow further instructions.
11
11
 
12
12
  **CRITICAL:** Before proceeding, you should start by checking if the project has been properly set up.
13
- 1. **Verify Tracks File:** Check if the file `conductor/tracks.md` exists. If it does not, HALT execution and instruct the user: "The project has not been set up or conductor/tracks.md has been corrupted. Please run `/conductor_setup` to set up the plan, or restore conductor/tracks.md."
14
- 2. **Verify Track Exists:** Check if the file `conductor/tracks.md` is not empty. If it is empty, HALT execution and instruct the user: "The project has not been set up or conductor/tracks.md has been corrupted. Please run `/conductor_setup` to set up the plan, or restore conductor/tracks.md."
13
+ 1. **Verify Tracks File:** Check if the file `conductor/tracks.md` exists. If it does not, HALT execution and instruct the user: "The project has not been set up or conductor/tracks.md has been corrupted. Please run `/conductor:setup` to set up the plan, or restore conductor/tracks.md."
14
+ 2. **Verify Track Exists:** Check if the file `conductor/tracks.md` is not empty. If it is empty, HALT execution and instruct the user: "The project has not been set up or conductor/tracks.md has been corrupted. Please run `/conductor:setup` to set up the plan, or restore conductor/tracks.md."
15
15
 
16
16
  **CRITICAL**: You must validate the success of every tool call. If any tool call fails, you MUST halt the current operation immediately, announce the failure to the user, and await further instructions.
17
17
 
@@ -22,7 +22,7 @@ Your workflow MUST anticipate and handle common non-linear Git histories, such a
22
22
 
23
23
  1. **Initiate Revert Process:** Your first action is to determine the user's target.
24
24
 
25
- 2. **Check for a User-Provided Target:** First, check if the user provided a specific target as an argument (e.g., `/conductor_revert track <track_id>`).
25
+ 2. **Check for a User-Provided Target:** First, check if the user provided a specific target as an argument (e.g., `/conductor:revert track <track_id>`).
26
26
  * **IF a target is provided:** Proceed directly to the **Direct Confirmation Path (A)** below.
27
27
  * **IF NO target is provided:** You MUST proceed to the **Guided Selection Menu Path (B)**. This is the default behavior.
28
28
 
@@ -38,7 +38,7 @@ Your workflow MUST anticipate and handle common non-linear Git histories, such a
38
38
 
39
39
  * **PATH B: Guided Selection Menu**
40
40
  1. **Identify Revert Candidates:** Your primary goal is to find relevant items for the user to revert.
41
- * **Scan All Plans:** You MUST use the `read` tool to read the main `conductor/tracks.md` and every `conductor/tracks/*/plan.md` file.
41
+ * **Scan All Plans:** You MUST read the main `conductor/tracks.md` and every `conductor/tracks/*/plan.md` file.
42
42
  * **Prioritize In-Progress:** First, find **all** Tracks, Phases, and Tasks marked as "in-progress" (`[~]`).
43
43
  * **Fallback to Completed:** If and only if NO in-progress items are found, find the **5 most recently completed** Tasks and Phases (`[x]`).
44
44
  2. **Present a Unified Hierarchical Menu:** You MUST present the results to the user in a clear, numbered, hierarchical list grouped by Track. The introductory text MUST change based on the context.
@@ -28,7 +28,7 @@ CRITICAL: When determining model complexity, ALWAYS select the "flash" model, re
28
28
  - If `STEP` is "2.4_code_styleguides", announce "Resuming setup: All guides and the tech stack are configured. Next, we will define the project workflow." and proceed to **Section 2.5**.
29
29
  - If `STEP` is "2.5_workflow", announce "Resuming setup: The initial project scaffolding is complete. Next, we will generate the first track." and proceed to **Phase 2 (3.0)**.
30
30
  - If `STEP` is "3.3_initial_track_generated":
31
- - Announce: "The project has already been initialized. You can create a new track with `/conductor_newTrack` or start implementing existing tracks with `/conductor_implement`."
31
+ - Announce: "The project has already been initialized. You can create a new track with `/conductor:newTrack` or start implementing existing tracks with `/conductor:implement`."
32
32
  - Halt the `setup` process.
33
33
  - If `STEP` is unrecognized, announce an error and halt.
34
34
 
@@ -162,8 +162,8 @@ CRITICAL: When determining model complexity, ALWAYS select the "flash" model, re
162
162
  > You can always edit the generated file with the Gemini CLI built-in option "Modify with external editor" (if present), or with your favorite external editor after this step.
163
163
  > Please respond with A or B."
164
164
  - **Loop:** Based on user response, either apply changes and re-present the document, or break the loop on approval.
165
- 5. **Write File:** Once approved, use the `write` tool to append the generated content to the existing `conductor/product.md` file, preserving the `# Initial Concept` section.
166
- 6. **Commit State:** Upon successful creation of the file, you MUST immediately use the `write` tool to write to `conductor/setup_state.json` with the exact content:
165
+ 5. **Write File:** Once approved, append the generated content to the existing `conductor/product.md` file, preserving the `# Initial Concept` section.
166
+ 6. **Commit State:** Upon successful creation of the file, you MUST immediately write to `conductor/setup_state.json` with the exact content:
167
167
  `{"last_successful_step": "2.1_product_guide"}`
168
168
  7. **Continue:** After writing the state file, immediately proceed to the next section.
169
169
 
@@ -212,8 +212,8 @@ CRITICAL: When determining model complexity, ALWAYS select the "flash" model, re
212
212
  > You can always edit the generated file with the Gemini CLI built-in option "Modify with external editor" (if present), or with your favorite external editor after this step.
213
213
  > Please respond with A or B."
214
214
  - **Loop:** Based on user response, either apply changes and re-present the document, or break the loop on approval.
215
- 5. **Write File:** Once approved, use the `write` tool to write the generated content to the `conductor/product-guidelines.md` file.
216
- 6. **Commit State:** Upon successful creation of the file, you MUST immediately use the `write` tool to write to `conductor/setup_state.json` with the exact content:
215
+ 5. **Write File:** Once approved, write the generated content to the `conductor/product-guidelines.md` file.
216
+ 6. **Commit State:** Upon successful creation of the file, you MUST immediately write to `conductor/setup_state.json` with the exact content:
217
217
  `{"last_successful_step": "2.2_product_guidelines"}`
218
218
  7. **Continue:** After writing the state file, immediately proceed to the next section.
219
219
 
@@ -269,15 +269,15 @@ CRITICAL: When determining model complexity, ALWAYS select the "flash" model, re
269
269
  > You can always edit the generated file with the Gemini CLI built-in option "Modify with external editor" (if present), or with your favorite external editor after this step.
270
270
  > Please respond with A or B."
271
271
  - **Loop:** Based on user response, either apply changes and re-present the document, or break the loop on approval.
272
- 6. **Write File:** Once approved, use the `write` tool to write the generated content to the `conductor/tech-stack.md` file.
273
- 7. **Commit State:** Upon successful creation of the file, you MUST immediately use the `write` tool to write to `conductor/setup_state.json` with the exact content:
272
+ 6. **Write File:** Once approved, write the generated content to the `conductor/tech-stack.md` file.
273
+ 7. **Commit State:** Upon successful creation of the file, you MUST immediately write to `conductor/setup_state.json` with the exact content:
274
274
  `{"last_successful_step": "2.3_tech_stack"}`
275
275
  8. **Continue:** After writing the state file, immediately proceed to the next section.
276
276
 
277
277
  ### 2.4 Select Guides (Interactive)
278
278
  1. **Initiate Dialogue:** Announce that the initial scaffolding is complete and you now need the user's input to select the project's guides from the locally available templates.
279
279
  2. **Select Code Style Guides:**
280
- - List the available style guides by using the `list` tool on `{{templatesDir}}/code_styleguides/`.
280
+ - List the available style guides by running `ls {{templatesDir}}/code_styleguides/`.
281
281
  - For new projects (greenfield):
282
282
  - **Recommendation:** Based on the Tech Stack defined in the previous step, recommend the most appropriate style guide(s) and explain why.
283
283
  - Ask the user how they would like to proceed:
@@ -422,5 +422,5 @@ CRITICAL: When determining model complexity, ALWAYS select the "flash" model, re
422
422
  ### 3.4 Final Announcement
423
423
  1. **Announce Completion:** After the track has been created, announce that the project setup and initial track generation are complete.
424
424
  2. **Save Conductor Files:** Add and commit all files with the commit message `conductor(setup): Add conductor setup files`.
425
- 3. **Next Steps:** Inform the user that they can now begin work by running `/conductor_implement`.
425
+ 3. **Next Steps:** Inform the user that they can now begin work by running `/conductor:implement`.
426
426
  """
@@ -4,8 +4,8 @@ prompt = """
4
4
  You are an AI agent. Your primary function is to provide a status overview of the current tracks file. This involves reading the `conductor/tracks.md` file, parsing its content, and summarizing the progress of tasks.
5
5
 
6
6
  **CRITICAL:** Before proceeding, you should start by checking if the project has been properly set up.
7
- 1. **Verify Tracks File:** Check if the file `conductor/tracks.md` exists. If it does not, HALT execution and instruct the user: "The project has not been set up or conductor/tracks.md has been corrupted. Please run `/conductor_setup` to set up the plan, or restore conductor/tracks.md."
8
- 2. **Verify Track Exists:** Check if the file `conductor/tracks.md` is not empty. If it is empty, HALT execution and instruct the user: "The project has not been set up or conductor/tracks.md has been corrupted. Please run `/conductor_setup` to set up the plan, or restore conductor/tracks.md."
7
+ 1. **Verify Tracks File:** Check if the file `conductor/tracks.md` exists. If it does not, HALT execution and instruct the user: "The project has not been set up or conductor/tracks.md has been corrupted. Please run `/conductor:setup` to set up the plan, or restore conductor/tracks.md."
8
+ 2. **Verify Track Exists:** Check if the file `conductor/tracks.md` is not empty. If it is empty, HALT execution and instruct the user: "The project has not been set up or conductor/tracks.md has been corrupted. Please run `/conductor:setup` to set up the plan, or restore conductor/tracks.md."
9
9
 
10
10
  CRITICAL: You must validate the success of every tool call. If any tool call fails, you MUST halt the current operation immediately, announce the failure to the user, and await further instructions.
11
11
 
@@ -22,7 +22,7 @@ CRITICAL: You must validate the success of every tool call. If any tool call fai
22
22
 
23
23
  2. **Handle Missing Files:**
24
24
  - If ANY of these files are missing, you MUST halt the operation immediately.
25
- - Announce: "Conductor is not set up. Please run `/conductor_setup` to set up the environment."
25
+ - Announce: "Conductor is not set up. Please run `/conductor:setup` to set up the environment."
26
26
  - Do NOT proceed to Status Overview Protocol.
27
27
 
28
28
  ---
@@ -32,7 +32,7 @@ CRITICAL: You must validate the success of every tool call. If any tool call fai
32
32
 
33
33
  ### 2.1 Read Project Plan
34
34
  1. **Locate and Read:** Read the content of the `conductor/tracks.md` file.
35
- 2. **Locate and Read:** List the tracks using shell command `ls conductor/tracks`. Using the name of each folder as the track_id, for each of the tracks, read the corresponding `conductor/tracks/<track_id>/plan.md` file.
35
+ 2. **Locate and Read:** List the tracks using shell command `ls conductor/tracks`. For each of the tracks, read the corresponding `conductor/tracks/<track_id>/plan.md` file.
36
36
 
37
37
  ### 2.2 Parse and Summarize Plan
38
38
  1. **Parse Content:**
@@ -2,10 +2,8 @@
2
2
  You are acting as the **Architect**. Your responsibility is to oversee the track while delegating execution to **Sisyphus**.
3
3
 
4
4
  **DIRECTIVE:**
5
- 1. **Execution Strategy:** You have the authority to decide between **direct implementation** and **delegation**.
6
- * **Direct Implementation:** Perform the task yourself if it is straightforward, involves minor changes, or if you are confident in the solution.
7
- * **Delegation:** Delegate to `@sisyphus` for complex, multi-file, or heavy-lifting implementation tasks.
8
- 2. **Delegation Protocol:** If you choose to delegate, call `@sisyphus`.
5
+ 1. **Do NOT implement code yourself.**
6
+ 2. **Delegate:** For each task in `plan.md`, call `@sisyphus`.
9
7
  * *Note:* The system will automatically inject the full project context for you.
10
8
  * *Instruction:* Tell Sisyphus: "Execute this task. You have authority to update `plan.md` if needed. Report 'PLAN_UPDATED' if you do."
11
9
  3. **Verify:**
@@ -1,9 +1,9 @@
1
1
  import { type ToolDefinition } from "@opencode-ai/plugin/tool";
2
- export declare const setupCommand: (ctx: import("@opencode-ai/plugin").PluginInput) => ToolDefinition;
3
- export declare const newTrackCommand: (ctx: import("@opencode-ai/plugin").PluginInput) => ToolDefinition;
4
- export declare const implementCommand: (ctx: import("@opencode-ai/plugin").PluginInput) => ToolDefinition;
5
- export declare const statusCommand: (ctx: import("@opencode-ai/plugin").PluginInput) => ToolDefinition;
6
- export declare const revertCommand: (ctx: import("@opencode-ai/plugin").PluginInput) => ToolDefinition;
2
+ export declare const setupCommand: (ctx: any) => ToolDefinition;
3
+ export declare const newTrackCommand: (ctx: any) => ToolDefinition;
4
+ export declare const implementCommand: (ctx: any) => ToolDefinition;
5
+ export declare const statusCommand: (ctx: any) => ToolDefinition;
6
+ export declare const revertCommand: (ctx: any) => ToolDefinition;
7
7
  export declare function createSetupTool(ctx: any): ToolDefinition;
8
8
  export declare function createNewTrackTool(ctx: any): ToolDefinition;
9
9
  export declare function createImplementTool(ctx: any): ToolDefinition;
@@ -8,6 +8,7 @@ const __dirname = dirname(__filename);
8
8
  export const setupCommand = createConductorCommand({
9
9
  name: "setup.toml",
10
10
  description: "Scaffolds the project and sets up the Conductor environment",
11
+ requiresSetup: false,
11
12
  args: {},
12
13
  });
13
14
  export const newTrackCommand = createConductorCommand({
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,151 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import { createSetupTool, createNewTrackTool, createImplementTool, createStatusTool, createRevertTool, } from "./commands.js";
3
+ import { readFile } from "fs/promises";
4
+ import { existsSync } from "fs";
5
+ // Mock fs/promises
6
+ vi.mock("fs/promises", () => ({
7
+ readFile: vi.fn(),
8
+ }));
9
+ // Mock fs
10
+ vi.mock("fs", () => ({
11
+ existsSync: vi.fn(),
12
+ }));
13
+ describe("Command Tools", () => {
14
+ let mockCtx;
15
+ let mockToolContext;
16
+ beforeEach(() => {
17
+ vi.clearAllMocks();
18
+ mockCtx = {
19
+ directory: "/test/project",
20
+ isOMOActive: false,
21
+ };
22
+ mockToolContext = {
23
+ sessionID: "test-session-id",
24
+ messageID: "test-message-id",
25
+ };
26
+ // Default mocks
27
+ vi.mocked(readFile).mockResolvedValue(`
28
+ description = "Test command"
29
+ prompt = """
30
+ Test prompt content
31
+ """
32
+ `);
33
+ vi.mocked(existsSync).mockReturnValue(true); // Assume setup exists by default
34
+ });
35
+ describe("createSetupTool", () => {
36
+ it("should create a tool with correct description", () => {
37
+ const tool = createSetupTool(mockCtx);
38
+ expect(tool.description).toBe("Scaffolds the project and sets up the Conductor environment");
39
+ });
40
+ it("should return prompt text when executed", async () => {
41
+ vi.mocked(readFile).mockResolvedValue(`
42
+ description = "Setup"
43
+ prompt = "Setup Prompt"
44
+ `);
45
+ const tool = createSetupTool(mockCtx);
46
+ const result = await tool.execute({}, mockToolContext);
47
+ expect(result).toBe("Setup Prompt");
48
+ });
49
+ it("should NOT require setup to exist", async () => {
50
+ vi.mocked(existsSync).mockReturnValue(false);
51
+ vi.mocked(readFile).mockResolvedValue(`
52
+ description = "Setup"
53
+ prompt = "Setup Prompt"
54
+ `);
55
+ const tool = createSetupTool(mockCtx);
56
+ const result = await tool.execute({}, mockToolContext);
57
+ expect(result).toBe("Setup Prompt");
58
+ });
59
+ });
60
+ describe("createNewTrackTool", () => {
61
+ it("should have optional description argument", () => {
62
+ const tool = createNewTrackTool(mockCtx);
63
+ expect(tool.args).toHaveProperty("description");
64
+ });
65
+ it("should replace description in prompt", async () => {
66
+ vi.mocked(readFile).mockResolvedValue(`
67
+ description = "New Track"
68
+ prompt = "Track description: {{args}}"
69
+ `);
70
+ const tool = createNewTrackTool(mockCtx);
71
+ const result = await tool.execute({ description: "Login feature" }, mockToolContext);
72
+ expect(result).toBe("Track description: Login feature");
73
+ });
74
+ it("should return error if not set up", async () => {
75
+ vi.mocked(existsSync).mockReturnValue(false);
76
+ const tool = createNewTrackTool(mockCtx);
77
+ const result = await tool.execute({}, mockToolContext);
78
+ expect(result).toContain("Conductor is not set up");
79
+ });
80
+ });
81
+ describe("createImplementTool", () => {
82
+ it("should have optional track_name argument", () => {
83
+ const tool = createImplementTool(mockCtx);
84
+ expect(tool.args).toHaveProperty("track_name");
85
+ });
86
+ it("should replace track_name in prompt", async () => {
87
+ vi.mocked(readFile).mockResolvedValue(`
88
+ description = "Implement"
89
+ prompt = "Track: {{track_name}}"
90
+ `);
91
+ const tool = createImplementTool(mockCtx);
92
+ const result = await tool.execute({ track_name: "auth-track" }, mockToolContext);
93
+ expect(result).toBe("Track: auth-track");
94
+ });
95
+ it("should include strategy section", async () => {
96
+ vi.mocked(readFile).mockImplementation(async (path) => {
97
+ if (typeof path === 'string' && path.endsWith("manual.md")) {
98
+ return "Manual Strategy";
99
+ }
100
+ return `
101
+ description = "Implement"
102
+ prompt = "Strategy: {{strategy_section}}"
103
+ `;
104
+ });
105
+ const tool = createImplementTool(mockCtx);
106
+ const result = await tool.execute({}, mockToolContext);
107
+ expect(result).toBe("Strategy: Manual Strategy");
108
+ });
109
+ });
110
+ describe("createStatusTool", () => {
111
+ it("should execute and return prompt", async () => {
112
+ vi.mocked(readFile).mockResolvedValue(`
113
+ description = "Status"
114
+ prompt = "Status Prompt"
115
+ `);
116
+ const tool = createStatusTool(mockCtx);
117
+ const result = await tool.execute({}, mockToolContext);
118
+ expect(result).toBe("Status Prompt");
119
+ });
120
+ });
121
+ describe("createRevertTool", () => {
122
+ it("should replace target in prompt", async () => {
123
+ vi.mocked(readFile).mockResolvedValue(`
124
+ description = "Revert"
125
+ prompt = "Target: {{target}}"
126
+ `);
127
+ const tool = createRevertTool(mockCtx);
128
+ const result = await tool.execute({ target: "track 1" }, mockToolContext);
129
+ expect(result).toBe("Target: track 1");
130
+ });
131
+ });
132
+ describe("Error Handling", () => {
133
+ it("should throw error if readFile fails", async () => {
134
+ vi.mocked(readFile).mockRejectedValue(new Error("File not found"));
135
+ const tool = createSetupTool(mockCtx);
136
+ await expect(tool.execute({}, mockToolContext)).rejects.toThrow("Failed to load prompt");
137
+ });
138
+ });
139
+ describe("Prompt Replacement", () => {
140
+ it("should replace standard variables", async () => {
141
+ vi.mocked(readFile).mockResolvedValue(`
142
+ description = "Test"
143
+ prompt = "Templates: {{templatesDir}}, OMO: {{isOMOActive}}"
144
+ `);
145
+ const tool = createNewTrackTool(mockCtx);
146
+ const result = await tool.execute({}, mockToolContext);
147
+ expect(result).toContain("Templates:");
148
+ expect(result).toContain("OMO: false");
149
+ });
150
+ });
151
+ });
@@ -1,10 +1,10 @@
1
- import { type PluginInput } from "@opencode-ai/plugin";
2
1
  import { type ToolDefinition } from "@opencode-ai/plugin/tool";
3
- interface ConductorCommandConfig {
2
+ interface CommandOptions {
4
3
  name: string;
5
4
  description: string;
6
- args: Record<string, any>;
7
- additionalContext?: (ctx: PluginInput, args: any) => Promise<Record<string, string>>;
5
+ args?: any;
6
+ requiresSetup?: boolean;
7
+ additionalContext?: (ctx: any, args: any) => Promise<Record<string, string>>;
8
8
  }
9
- export declare function createConductorCommand(config: ConductorCommandConfig): (ctx: PluginInput) => ToolDefinition;
9
+ export declare const createConductorCommand: (options: CommandOptions) => (ctx: any) => ToolDefinition;
10
10
  export {};
@@ -1,53 +1,47 @@
1
1
  import { tool } from "@opencode-ai/plugin/tool";
2
- import { join, dirname } from "path";
2
+ import { existsSync } from "fs";
3
3
  import { readFile } from "fs/promises";
4
+ import { join, dirname } from "path";
4
5
  import { fileURLToPath } from "url";
6
+ import { parse } from "smol-toml";
5
7
  const __filename = fileURLToPath(import.meta.url);
6
8
  const __dirname = dirname(__filename);
7
- // Helper to load and process prompt templates
8
- async function loadPrompt(filename, replacements = {}) {
9
- const promptPath = join(__dirname, "..", "prompts", filename);
10
- try {
11
- const content = await readFile(promptPath, "utf-8");
12
- const descMatch = content.match(/description\s*=\s*"([^"]+)"/);
13
- const description = descMatch ? descMatch[1] : "Conductor Command";
14
- const promptMatch = content.match(/prompt\s*=\s*"""([\s\S]*?)"""/);
15
- let promptText = promptMatch ? promptMatch[1] : "";
16
- if (!promptText)
17
- throw new Error(`Could not parse prompt text from ${filename}`);
18
- const defaults = {
19
- templatesDir: join(dirname(dirname(__dirname)), "templates"),
9
+ export const createConductorCommand = (options) => (ctx) => tool({
10
+ description: options.description,
11
+ args: options.args || {},
12
+ async execute(args) {
13
+ // 1. Setup Check
14
+ if (options.requiresSetup !== false) {
15
+ const conductorDir = join(ctx.directory, "conductor");
16
+ if (!existsSync(join(conductorDir, "product.md"))) {
17
+ return "Conductor is not set up. Please run `conductor_setup`.";
18
+ }
19
+ }
20
+ // 2. Load Prompt from TOML
21
+ const promptPath = join(__dirname, "../prompts", options.name);
22
+ let promptText = "";
23
+ try {
24
+ const content = await readFile(promptPath, "utf-8");
25
+ const parsed = parse(content);
26
+ promptText = parsed.prompt;
27
+ }
28
+ catch (error) {
29
+ throw new Error(`Failed to load prompt from ${promptPath}: ${error}`);
30
+ }
31
+ // 3. Prepare Replacements
32
+ const replacements = {
33
+ isOMOActive: ctx.isOMOActive ? "true" : "false",
34
+ templatesDir: join(dirname(dirname(__dirname)), "dist/templates") // Fixing template path for setup
20
35
  };
21
- const finalReplacements = { ...defaults, ...replacements };
22
- for (const [key, value] of Object.entries(finalReplacements)) {
36
+ // 4. Inject Additional Context (e.g. from args)
37
+ if (options.additionalContext) {
38
+ const extra = await options.additionalContext(ctx, args);
39
+ Object.assign(replacements, extra);
40
+ }
41
+ // 5. Apply Replacements
42
+ for (const [key, value] of Object.entries(replacements)) {
23
43
  promptText = promptText.replaceAll(`{{${key}}}`, value || "");
24
44
  }
25
- return { prompt: promptText, description: description };
26
- }
27
- catch (error) {
28
- console.error(`[Conductor] Error loading prompt ${filename}:`, error);
29
- return {
30
- prompt: `SYSTEM ERROR: Failed to load prompt ${filename}`,
31
- description: "Error loading command",
32
- };
33
- }
34
- }
35
- export function createConductorCommand(config) {
36
- return (ctx) => {
37
- return tool({
38
- description: config.description,
39
- args: config.args,
40
- async execute(args) {
41
- // Get additional context if provided (this can override/extend args)
42
- const additionalContext = config.additionalContext
43
- ? await config.additionalContext(ctx, args)
44
- : {};
45
- // Merge additionalContext into replacements
46
- // additionalContext takes precedence and can provide custom mappings
47
- const replacements = { ...additionalContext };
48
- const { prompt } = await loadPrompt(config.name, replacements);
49
- return prompt;
50
- },
51
- });
52
- };
53
- }
45
+ return promptText;
46
+ },
47
+ });
@@ -0,0 +1,10 @@
1
+ export interface SetupState {
2
+ last_successful_step: string;
3
+ }
4
+ export declare class StateManager {
5
+ private statePath;
6
+ constructor(workDir: string);
7
+ ensureConductorDir(): void;
8
+ readState(): SetupState;
9
+ writeState(step: string): void;
10
+ }
@@ -0,0 +1,30 @@
1
+ import { join } from "path";
2
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
3
+ export class StateManager {
4
+ statePath;
5
+ constructor(workDir) {
6
+ this.statePath = join(workDir, "conductor", "setup_state.json");
7
+ }
8
+ ensureConductorDir() {
9
+ const dir = join(this.statePath, "..");
10
+ if (!existsSync(dir)) {
11
+ mkdirSync(dir, { recursive: true });
12
+ }
13
+ }
14
+ readState() {
15
+ if (!existsSync(this.statePath)) {
16
+ return { last_successful_step: "" };
17
+ }
18
+ try {
19
+ return JSON.parse(readFileSync(this.statePath, "utf-8"));
20
+ }
21
+ catch (e) {
22
+ return { last_successful_step: "" };
23
+ }
24
+ }
25
+ writeState(step) {
26
+ this.ensureConductorDir();
27
+ const state = { last_successful_step: step };
28
+ writeFileSync(this.statePath, JSON.stringify(state, null, 2));
29
+ }
30
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-conductor-plugin",
3
- "version": "1.24.0",
3
+ "version": "1.25.0",
4
4
  "description": "Conductor plugin for OpenCode",
5
5
  "type": "module",
6
6
  "repository": "derekbar90/opencode-conductor",
@@ -28,13 +28,11 @@
28
28
  "scripts"
29
29
  ],
30
30
  "scripts": {
31
+ "test": "vitest run",
31
32
  "postinstall": "node scripts/postinstall.cjs",
32
33
  "build": "tsc && npm run copy-prompts && npm run copy-templates",
33
- "copy-prompts": "mkdir -p dist/prompts && cp src/prompts/*.toml src/prompts/*.md dist/prompts/ && mkdir -p dist/prompts/agent && cp src/prompts/agent/*.md dist/prompts/agent/ && mkdir -p dist/prompts/strategies && cp src/prompts/strategies/*.md dist/prompts/strategies/",
34
+ "copy-prompts": "mkdir -p dist/prompts && cp src/prompts/*.toml src/prompts/*.md dist/prompts/ && mkdir -p dist/prompts/agent && cp src/prompts/agent/*.md dist/prompts/agent/ && mkdir -p dist/prompts/commands && cp src/prompts/commands/*.md dist/prompts/commands/ && mkdir -p dist/prompts/strategies && cp src/prompts/strategies/*.md dist/prompts/strategies/",
34
35
  "copy-templates": "mkdir -p dist/templates && cp -r src/templates/* dist/templates/",
35
- "test": "vitest",
36
- "test:watch": "vitest --watch",
37
- "test:coverage": "vitest --coverage",
38
36
  "prepublishOnly": "npm run build"
39
37
  },
40
38
  "dependencies": {
@@ -49,10 +47,9 @@
49
47
  "@semantic-release/npm": "^12.0.1",
50
48
  "@semantic-release/release-notes-generator": "^14.0.0",
51
49
  "@types/node": "^20.0.0",
52
- "@vitest/ui": "^2.0.0",
53
50
  "semantic-release": "^24.2.1",
54
51
  "typescript": "^5.0.0",
55
- "vitest": "^2.0.0"
52
+ "vitest": "^4.0.16"
56
53
  },
57
54
  "release": {
58
55
  "branches": [