opencode-conductor-plugin 1.21.1 → 1.22.1

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/dist/index.js CHANGED
@@ -4,6 +4,7 @@ import { readFile } from "fs/promises";
4
4
  import { fileURLToPath } from "url";
5
5
  import { createDelegationTool } from "./tools/delegate.js";
6
6
  import { BackgroundManager, createBackgroundTask, createBackgroundOutput, createBackgroundCancel, } from "./tools/background.js";
7
+ import { createSetupTool, createNewTrackTool, createImplementTool, createStatusTool, createRevertTool, } from "./tools/commands.js";
7
8
  const __filename = fileURLToPath(import.meta.url);
8
9
  const __dirname = dirname(__filename);
9
10
  const safeRead = async (path) => {
@@ -72,6 +73,11 @@ const ConductorPlugin = async (ctx) => {
72
73
  "conductor_bg_task": createBackgroundTask(backgroundManager),
73
74
  "conductor_bg_output": createBackgroundOutput(backgroundManager),
74
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),
75
81
  },
76
82
  config: async (config) => {
77
83
  if (!config)
@@ -80,27 +86,27 @@ const ConductorPlugin = async (ctx) => {
80
86
  config.command = {
81
87
  ...(config.command || {}),
82
88
  "conductor_setup": {
83
- template: setup.prompt,
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.",
84
90
  description: setup.description,
85
91
  agent: "conductor",
86
92
  },
87
93
  "conductor_newTrack": {
88
- template: newTrack.prompt,
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.",
89
95
  description: newTrack.description,
90
96
  agent: "conductor",
91
97
  },
92
98
  "conductor_implement": {
93
- template: implement.prompt,
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.",
94
100
  description: implement.description,
95
101
  agent: "conductor_implementer",
96
102
  },
97
103
  "conductor_status": {
98
- template: status.prompt,
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.",
99
105
  description: status.description,
100
106
  agent: "conductor",
101
107
  },
102
108
  "conductor_revert": {
103
- template: revert.prompt,
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.",
104
110
  description: revert.description,
105
111
  agent: "conductor",
106
112
  },
@@ -127,6 +133,11 @@ const ConductorPlugin = async (ctx) => {
127
133
  todoread: "allow",
128
134
  todowrite: "allow",
129
135
  webfetch: "allow",
136
+ "conductor_setup": "allow",
137
+ "conductor_newTrack": "allow",
138
+ "conductor_implement": "allow",
139
+ "conductor_status": "allow",
140
+ "conductor_revert": "allow",
130
141
  external_directory: "deny",
131
142
  doom_loop: "ask",
132
143
  },
@@ -155,6 +166,11 @@ const ConductorPlugin = async (ctx) => {
155
166
  "conductor_bg_task": "allow",
156
167
  "conductor_bg_output": "allow",
157
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",
158
174
  external_directory: "deny",
159
175
  doom_loop: "ask",
160
176
  },
@@ -0,0 +1,7 @@
1
+ import { type PluginInput } from "@opencode-ai/plugin";
2
+ import { type ToolDefinition } from "@opencode-ai/plugin/tool";
3
+ export declare function createSetupTool(ctx: PluginInput): ToolDefinition;
4
+ export declare function createNewTrackTool(ctx: PluginInput): ToolDefinition;
5
+ export declare function createImplementTool(ctx: PluginInput): ToolDefinition;
6
+ export declare function createStatusTool(ctx: PluginInput): ToolDefinition;
7
+ export declare function createRevertTool(ctx: PluginInput): ToolDefinition;
@@ -0,0 +1,132 @@
1
+ import { tool } from "@opencode-ai/plugin/tool";
2
+ import { join, dirname } from "path";
3
+ import { readFile } from "fs/promises";
4
+ import { fileURLToPath } from "url";
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ 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"),
20
+ };
21
+ const finalReplacements = { ...defaults, ...replacements };
22
+ for (const [key, value] of Object.entries(finalReplacements)) {
23
+ promptText = promptText.replaceAll(`{{${key}}}`, value || "");
24
+ }
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
+ // Helper to execute a command prompt in a sub-session
36
+ async function executeCommand(ctx, toolContext, promptText, agent, description) {
37
+ // Create a sub-session linked to the current one
38
+ const createResult = await ctx.client.session.create({
39
+ body: {
40
+ parentID: toolContext.sessionID,
41
+ title: description,
42
+ },
43
+ });
44
+ if (createResult.error)
45
+ return `Error: ${createResult.error}`;
46
+ const sessionID = createResult.data.id;
47
+ // Send the prompt to the agent
48
+ await ctx.client.session.prompt({
49
+ path: { id: sessionID },
50
+ body: {
51
+ agent: agent,
52
+ parts: [{ type: "text", text: promptText }],
53
+ },
54
+ });
55
+ // Fetch and return the assistant's response
56
+ const messagesResult = await ctx.client.session.messages({
57
+ path: { id: sessionID },
58
+ });
59
+ const lastMessage = messagesResult.data
60
+ ?.filter((m) => m.info.role === "assistant")
61
+ .pop();
62
+ const responseText = lastMessage?.parts
63
+ ?.filter((p) => p.type === "text")
64
+ .map((p) => p.text)
65
+ .join("\n") || "No response.";
66
+ return `${responseText}\n\n<task_metadata>\nsession_id: ${sessionID}\n</task_metadata>`;
67
+ }
68
+ export function createSetupTool(ctx) {
69
+ return tool({
70
+ description: "Scaffolds the project and sets up the Conductor environment",
71
+ args: {},
72
+ async execute(args, toolContext) {
73
+ const { prompt, description } = await loadPrompt("setup.toml");
74
+ return await executeCommand(ctx, toolContext, prompt, "conductor", description);
75
+ },
76
+ });
77
+ }
78
+ export function createNewTrackTool(ctx) {
79
+ return tool({
80
+ description: "Plans a track, generates track-specific spec documents and updates the tracks file",
81
+ args: {
82
+ description: tool.schema.string().optional().describe("Brief description of the track (feature, bug fix, chore, etc.)"),
83
+ },
84
+ async execute(args, toolContext) {
85
+ const trackDescription = args.description || "";
86
+ const { prompt, description } = await loadPrompt("newTrack.toml", {
87
+ args: trackDescription,
88
+ });
89
+ return await executeCommand(ctx, toolContext, prompt, "conductor", description);
90
+ },
91
+ });
92
+ }
93
+ export function createImplementTool(ctx) {
94
+ return tool({
95
+ description: "Executes the tasks defined in the specified track's plan",
96
+ args: {
97
+ track_name: tool.schema.string().optional().describe("Name or description of the track to implement"),
98
+ },
99
+ async execute(args, toolContext) {
100
+ const trackName = args.track_name || "";
101
+ const { prompt, description } = await loadPrompt("implement.toml", {
102
+ track_name: trackName,
103
+ });
104
+ return await executeCommand(ctx, toolContext, prompt, "conductor_implementer", description);
105
+ },
106
+ });
107
+ }
108
+ export function createStatusTool(ctx) {
109
+ return tool({
110
+ description: "Displays the current progress of the project",
111
+ args: {},
112
+ async execute(args, toolContext) {
113
+ const { prompt, description } = await loadPrompt("status.toml");
114
+ return await executeCommand(ctx, toolContext, prompt, "conductor", description);
115
+ },
116
+ });
117
+ }
118
+ export function createRevertTool(ctx) {
119
+ return tool({
120
+ description: "Reverts previous work",
121
+ args: {
122
+ target: tool.schema.string().optional().describe("Target to revert (e.g., 'track <track_id>', 'phase <phase_name>', 'task <task_name>')"),
123
+ },
124
+ async execute(args, toolContext) {
125
+ const target = args.target || "";
126
+ const { prompt, description } = await loadPrompt("revert.toml", {
127
+ target: target,
128
+ });
129
+ return await executeCommand(ctx, toolContext, prompt, "conductor", description);
130
+ },
131
+ });
132
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-conductor-plugin",
3
- "version": "1.21.1",
3
+ "version": "1.22.1",
4
4
  "description": "Conductor plugin for OpenCode",
5
5
  "type": "module",
6
6
  "repository": "derekbar90/opencode-conductor",