opencode-conductor-plugin 1.25.0 → 1.26.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/dist/commands/implement.d.ts +1 -1
- package/dist/commands/newTrack.d.ts +1 -1
- package/dist/commands/revert.d.ts +1 -1
- package/dist/commands/setup.d.ts +1 -1
- package/dist/commands/status.d.ts +1 -1
- package/dist/tools/commands.d.ts +5 -5
- package/dist/tools/commands.js +5 -6
- package/dist/tools/commands.test.js +44 -50
- package/dist/utils/commandFactory.d.ts +5 -4
- package/dist/utils/commandFactory.js +47 -39
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const implementCommand: (ctx:
|
|
1
|
+
export declare const implementCommand: (ctx: import("@opencode-ai/plugin").PluginInput) => import("@opencode-ai/plugin/tool").ToolDefinition;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const newTrackCommand: (ctx:
|
|
1
|
+
export declare const newTrackCommand: (ctx: import("@opencode-ai/plugin").PluginInput) => import("@opencode-ai/plugin/tool").ToolDefinition;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const revertCommand: (ctx:
|
|
1
|
+
export declare const revertCommand: (ctx: import("@opencode-ai/plugin").PluginInput) => import("@opencode-ai/plugin/tool").ToolDefinition;
|
package/dist/commands/setup.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const setupCommand: (ctx:
|
|
1
|
+
export declare const setupCommand: (ctx: import("@opencode-ai/plugin").PluginInput) => import("@opencode-ai/plugin/tool").ToolDefinition;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const statusCommand: (ctx:
|
|
1
|
+
export declare const statusCommand: (ctx: import("@opencode-ai/plugin").PluginInput) => import("@opencode-ai/plugin/tool").ToolDefinition;
|
package/dist/tools/commands.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { type ToolDefinition } from "@opencode-ai/plugin/tool";
|
|
2
|
-
export declare const setupCommand: (ctx:
|
|
3
|
-
export declare const newTrackCommand: (ctx:
|
|
4
|
-
export declare const implementCommand: (ctx:
|
|
5
|
-
export declare const statusCommand: (ctx:
|
|
6
|
-
export declare const revertCommand: (ctx:
|
|
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;
|
|
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;
|
package/dist/tools/commands.js
CHANGED
|
@@ -7,13 +7,12 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
7
7
|
const __dirname = dirname(__filename);
|
|
8
8
|
export const setupCommand = createConductorCommand({
|
|
9
9
|
name: "setup.toml",
|
|
10
|
-
description: "
|
|
11
|
-
requiresSetup: false,
|
|
10
|
+
description: "Directives lookup tool for scaffolding the project and setting up the Conductor environment",
|
|
12
11
|
args: {},
|
|
13
12
|
});
|
|
14
13
|
export const newTrackCommand = createConductorCommand({
|
|
15
14
|
name: "newTrack.toml",
|
|
16
|
-
description: "
|
|
15
|
+
description: "Directives lookup tool for planning a track, generating track-specific spec documents and updating the tracks file",
|
|
17
16
|
args: {
|
|
18
17
|
description: tool.schema.string().optional().describe("Brief description of the track (feature, bug fix, chore, etc.)"),
|
|
19
18
|
},
|
|
@@ -25,7 +24,7 @@ export const newTrackCommand = createConductorCommand({
|
|
|
25
24
|
});
|
|
26
25
|
export const implementCommand = createConductorCommand({
|
|
27
26
|
name: "implement.toml",
|
|
28
|
-
description: "
|
|
27
|
+
description: "Directives lookup tool for executing the tasks defined in the specified track's plan",
|
|
29
28
|
args: {
|
|
30
29
|
track_name: tool.schema.string().optional().describe("Name or description of the track to implement"),
|
|
31
30
|
},
|
|
@@ -49,12 +48,12 @@ export const implementCommand = createConductorCommand({
|
|
|
49
48
|
});
|
|
50
49
|
export const statusCommand = createConductorCommand({
|
|
51
50
|
name: "status.toml",
|
|
52
|
-
description: "
|
|
51
|
+
description: "Directives lookup tool for displaying the current progress of the project",
|
|
53
52
|
args: {},
|
|
54
53
|
});
|
|
55
54
|
export const revertCommand = createConductorCommand({
|
|
56
55
|
name: "revert.toml",
|
|
57
|
-
description: "
|
|
56
|
+
description: "Directives lookup tool for reverting previous work",
|
|
58
57
|
args: {
|
|
59
58
|
target: tool.schema.string().optional().describe("Target to revert (e.g., 'track <track_id>', 'phase <phase_name>', 'task <task_name>')"),
|
|
60
59
|
},
|
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
2
|
import { createSetupTool, createNewTrackTool, createImplementTool, createStatusTool, createRevertTool, } from "./commands.js";
|
|
3
3
|
import { readFile } from "fs/promises";
|
|
4
|
-
import { existsSync } from "fs";
|
|
5
4
|
// Mock fs/promises
|
|
6
5
|
vi.mock("fs/promises", () => ({
|
|
7
6
|
readFile: vi.fn(),
|
|
8
7
|
}));
|
|
9
|
-
// Mock fs
|
|
10
|
-
vi.mock("fs", () => ({
|
|
11
|
-
existsSync: vi.fn(),
|
|
12
|
-
}));
|
|
13
8
|
describe("Command Tools", () => {
|
|
14
9
|
let mockCtx;
|
|
15
10
|
let mockToolContext;
|
|
@@ -20,132 +15,131 @@ describe("Command Tools", () => {
|
|
|
20
15
|
isOMOActive: false,
|
|
21
16
|
};
|
|
22
17
|
mockToolContext = {
|
|
23
|
-
sessionID: "test-session
|
|
24
|
-
messageID: "test-message
|
|
18
|
+
sessionID: "test-session",
|
|
19
|
+
messageID: "test-message",
|
|
25
20
|
};
|
|
26
|
-
// Default mocks
|
|
21
|
+
// Default mocks - must use triple quotes for the regex in commandFactory
|
|
27
22
|
vi.mocked(readFile).mockResolvedValue(`
|
|
28
23
|
description = "Test command"
|
|
29
24
|
prompt = """
|
|
30
25
|
Test prompt content
|
|
31
26
|
"""
|
|
32
27
|
`);
|
|
33
|
-
vi.mocked(existsSync).mockReturnValue(true); // Assume setup exists by default
|
|
34
28
|
});
|
|
35
29
|
describe("createSetupTool", () => {
|
|
36
30
|
it("should create a tool with correct description", () => {
|
|
37
31
|
const tool = createSetupTool(mockCtx);
|
|
38
|
-
expect(tool.description).toBe("
|
|
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");
|
|
32
|
+
expect(tool.description).toBe("Directives lookup tool for scaffolding the project and setting up the Conductor environment");
|
|
48
33
|
});
|
|
49
|
-
it("should
|
|
50
|
-
vi.mocked(existsSync).mockReturnValue(false);
|
|
34
|
+
it("should return directives JSON string when executed", async () => {
|
|
51
35
|
vi.mocked(readFile).mockResolvedValue(`
|
|
52
36
|
description = "Setup"
|
|
53
|
-
prompt = "Setup Prompt"
|
|
37
|
+
prompt = """Setup Prompt"""
|
|
54
38
|
`);
|
|
55
39
|
const tool = createSetupTool(mockCtx);
|
|
56
40
|
const result = await tool.execute({}, mockToolContext);
|
|
57
|
-
expect(result).
|
|
41
|
+
expect(JSON.parse(result)).toEqual({ directives: "Setup Prompt" });
|
|
58
42
|
});
|
|
59
43
|
});
|
|
60
44
|
describe("createNewTrackTool", () => {
|
|
45
|
+
it("should create a tool with correct description", () => {
|
|
46
|
+
const tool = createNewTrackTool(mockCtx);
|
|
47
|
+
expect(tool.description).toBe("Directives lookup tool for planning a track, generating track-specific spec documents and updating the tracks file");
|
|
48
|
+
});
|
|
61
49
|
it("should have optional description argument", () => {
|
|
62
50
|
const tool = createNewTrackTool(mockCtx);
|
|
63
51
|
expect(tool.args).toHaveProperty("description");
|
|
64
52
|
});
|
|
65
|
-
it("should replace description in
|
|
53
|
+
it("should replace description in directives", async () => {
|
|
66
54
|
vi.mocked(readFile).mockResolvedValue(`
|
|
67
55
|
description = "New Track"
|
|
68
|
-
prompt = "Track description: {{args}}"
|
|
56
|
+
prompt = """Track description: {{args}}"""
|
|
69
57
|
`);
|
|
70
58
|
const tool = createNewTrackTool(mockCtx);
|
|
71
59
|
const result = await tool.execute({ description: "Login feature" }, mockToolContext);
|
|
72
|
-
expect(result).
|
|
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");
|
|
60
|
+
expect(JSON.parse(result)).toEqual({ directives: "Track description: Login feature" });
|
|
79
61
|
});
|
|
80
62
|
});
|
|
81
63
|
describe("createImplementTool", () => {
|
|
64
|
+
it("should create a tool with correct description", () => {
|
|
65
|
+
const tool = createImplementTool(mockCtx);
|
|
66
|
+
expect(tool.description).toBe("Directives lookup tool for executing the tasks defined in the specified track's plan");
|
|
67
|
+
});
|
|
82
68
|
it("should have optional track_name argument", () => {
|
|
83
69
|
const tool = createImplementTool(mockCtx);
|
|
84
70
|
expect(tool.args).toHaveProperty("track_name");
|
|
85
71
|
});
|
|
86
|
-
it("should replace track_name in
|
|
72
|
+
it("should replace track_name in directives", async () => {
|
|
87
73
|
vi.mocked(readFile).mockResolvedValue(`
|
|
88
74
|
description = "Implement"
|
|
89
|
-
prompt = "Track: {{track_name}}"
|
|
75
|
+
prompt = """Track: {{track_name}}"""
|
|
90
76
|
`);
|
|
91
77
|
const tool = createImplementTool(mockCtx);
|
|
92
78
|
const result = await tool.execute({ track_name: "auth-track" }, mockToolContext);
|
|
93
|
-
expect(result).
|
|
79
|
+
expect(JSON.parse(result)).toEqual({ directives: "Track: auth-track" });
|
|
94
80
|
});
|
|
95
|
-
it("should include strategy section", async () => {
|
|
81
|
+
it("should include strategy section in directives", async () => {
|
|
96
82
|
vi.mocked(readFile).mockImplementation(async (path) => {
|
|
97
83
|
if (typeof path === 'string' && path.endsWith("manual.md")) {
|
|
98
84
|
return "Manual Strategy";
|
|
99
85
|
}
|
|
100
86
|
return `
|
|
101
87
|
description = "Implement"
|
|
102
|
-
prompt = "Strategy: {{strategy_section}}"
|
|
88
|
+
prompt = """Strategy: {{strategy_section}}"""
|
|
103
89
|
`;
|
|
104
90
|
});
|
|
105
91
|
const tool = createImplementTool(mockCtx);
|
|
106
92
|
const result = await tool.execute({}, mockToolContext);
|
|
107
|
-
expect(result).
|
|
93
|
+
expect(JSON.parse(result)).toEqual({ directives: "Strategy: Manual Strategy" });
|
|
108
94
|
});
|
|
109
95
|
});
|
|
110
96
|
describe("createStatusTool", () => {
|
|
111
|
-
it("should
|
|
97
|
+
it("should create a tool with correct description", () => {
|
|
98
|
+
const tool = createStatusTool(mockCtx);
|
|
99
|
+
expect(tool.description).toBe("Directives lookup tool for displaying the current progress of the project");
|
|
100
|
+
});
|
|
101
|
+
it("should execute and return directives", async () => {
|
|
112
102
|
vi.mocked(readFile).mockResolvedValue(`
|
|
113
103
|
description = "Status"
|
|
114
|
-
prompt = "Status Prompt"
|
|
104
|
+
prompt = """Status Prompt"""
|
|
115
105
|
`);
|
|
116
106
|
const tool = createStatusTool(mockCtx);
|
|
117
107
|
const result = await tool.execute({}, mockToolContext);
|
|
118
|
-
expect(result).
|
|
108
|
+
expect(JSON.parse(result)).toEqual({ directives: "Status Prompt" });
|
|
119
109
|
});
|
|
120
110
|
});
|
|
121
111
|
describe("createRevertTool", () => {
|
|
122
|
-
it("should
|
|
112
|
+
it("should create a tool with correct description", () => {
|
|
113
|
+
const tool = createRevertTool(mockCtx);
|
|
114
|
+
expect(tool.description).toBe("Directives lookup tool for reverting previous work");
|
|
115
|
+
});
|
|
116
|
+
it("should replace target in directives", async () => {
|
|
123
117
|
vi.mocked(readFile).mockResolvedValue(`
|
|
124
118
|
description = "Revert"
|
|
125
|
-
prompt = "Target: {{target}}"
|
|
119
|
+
prompt = """Target: {{target}}"""
|
|
126
120
|
`);
|
|
127
121
|
const tool = createRevertTool(mockCtx);
|
|
128
122
|
const result = await tool.execute({ target: "track 1" }, mockToolContext);
|
|
129
|
-
expect(result).
|
|
123
|
+
expect(JSON.parse(result)).toEqual({ directives: "Target: track 1" });
|
|
130
124
|
});
|
|
131
125
|
});
|
|
132
126
|
describe("Error Handling", () => {
|
|
133
|
-
it("should
|
|
127
|
+
it("should return error in directives if readFile fails", async () => {
|
|
134
128
|
vi.mocked(readFile).mockRejectedValue(new Error("File not found"));
|
|
135
129
|
const tool = createSetupTool(mockCtx);
|
|
136
|
-
await
|
|
130
|
+
const result = await tool.execute({}, mockToolContext);
|
|
131
|
+
expect(JSON.parse(result).directives).toContain("SYSTEM ERROR: Failed to load prompt");
|
|
137
132
|
});
|
|
138
133
|
});
|
|
139
134
|
describe("Prompt Replacement", () => {
|
|
140
|
-
it("should replace standard variables", async () => {
|
|
135
|
+
it("should replace standard variables in directives", async () => {
|
|
141
136
|
vi.mocked(readFile).mockResolvedValue(`
|
|
142
137
|
description = "Test"
|
|
143
|
-
prompt = "Templates: {{templatesDir}}
|
|
138
|
+
prompt = """Templates: {{templatesDir}}"""
|
|
144
139
|
`);
|
|
145
140
|
const tool = createNewTrackTool(mockCtx);
|
|
146
141
|
const result = await tool.execute({}, mockToolContext);
|
|
147
|
-
expect(result).toContain("Templates:");
|
|
148
|
-
expect(result).toContain("OMO: false");
|
|
142
|
+
expect(JSON.parse(result).directives).toContain("Templates:");
|
|
149
143
|
});
|
|
150
144
|
});
|
|
151
145
|
});
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { type PluginInput } from "@opencode-ai/plugin";
|
|
1
2
|
import { type ToolDefinition } from "@opencode-ai/plugin/tool";
|
|
2
|
-
interface
|
|
3
|
+
interface ConductorCommandConfig {
|
|
3
4
|
name: string;
|
|
4
5
|
description: string;
|
|
5
|
-
args
|
|
6
|
+
args: Record<string, any>;
|
|
6
7
|
requiresSetup?: boolean;
|
|
7
|
-
additionalContext?: (ctx:
|
|
8
|
+
additionalContext?: (ctx: PluginInput, args: any) => Promise<Record<string, string>>;
|
|
8
9
|
}
|
|
9
|
-
export declare
|
|
10
|
+
export declare function createConductorCommand(config: ConductorCommandConfig): (ctx: PluginInput) => ToolDefinition;
|
|
10
11
|
export {};
|
|
@@ -1,47 +1,55 @@
|
|
|
1
1
|
import { tool } from "@opencode-ai/plugin/tool";
|
|
2
|
-
import { existsSync } from "fs";
|
|
3
|
-
import { readFile } from "fs/promises";
|
|
4
2
|
import { join, dirname } from "path";
|
|
3
|
+
import { readFile } from "fs/promises";
|
|
5
4
|
import { fileURLToPath } from "url";
|
|
6
|
-
import { parse } from "smol-toml";
|
|
7
5
|
const __filename = fileURLToPath(import.meta.url);
|
|
8
6
|
const __dirname = dirname(__filename);
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
|
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"),
|
|
35
20
|
};
|
|
36
|
-
|
|
37
|
-
|
|
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)) {
|
|
21
|
+
const finalReplacements = { ...defaults, ...replacements };
|
|
22
|
+
for (const [key, value] of Object.entries(finalReplacements)) {
|
|
43
23
|
promptText = promptText.replaceAll(`{{${key}}}`, value || "");
|
|
44
24
|
}
|
|
45
|
-
return promptText;
|
|
46
|
-
}
|
|
47
|
-
|
|
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, context) {
|
|
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 JSON.stringify({
|
|
50
|
+
directives: prompt
|
|
51
|
+
});
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
}
|