opencode-conductor-cdd-plugin 1.0.0-beta.13

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.
Files changed (91) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +163 -0
  3. package/README.test.md +51 -0
  4. package/dist/commands/implement.d.ts +1 -0
  5. package/dist/commands/implement.js +30 -0
  6. package/dist/index.d.ts +2 -0
  7. package/dist/index.js +108 -0
  8. package/dist/index.test.d.ts +1 -0
  9. package/dist/index.test.js +122 -0
  10. package/dist/prompts/agent/cdd.md +41 -0
  11. package/dist/prompts/agent/implementer.md +22 -0
  12. package/dist/prompts/agent.md +23 -0
  13. package/dist/prompts/cdd/implement.json +4 -0
  14. package/dist/prompts/cdd/newTrack.json +4 -0
  15. package/dist/prompts/cdd/revert.json +4 -0
  16. package/dist/prompts/cdd/setup.json +4 -0
  17. package/dist/prompts/cdd/setup.test.d.ts +1 -0
  18. package/dist/prompts/cdd/setup.test.js +132 -0
  19. package/dist/prompts/cdd/setup.test.ts +168 -0
  20. package/dist/prompts/cdd/status.json +4 -0
  21. package/dist/prompts/strategies/delegate.md +11 -0
  22. package/dist/prompts/strategies/manual.md +9 -0
  23. package/dist/templates/code_styleguides/c.md +28 -0
  24. package/dist/templates/code_styleguides/cpp.md +46 -0
  25. package/dist/templates/code_styleguides/csharp.md +115 -0
  26. package/dist/templates/code_styleguides/dart.md +238 -0
  27. package/dist/templates/code_styleguides/general.md +23 -0
  28. package/dist/templates/code_styleguides/go.md +48 -0
  29. package/dist/templates/code_styleguides/html-css.md +49 -0
  30. package/dist/templates/code_styleguides/java.md +39 -0
  31. package/dist/templates/code_styleguides/javascript.md +51 -0
  32. package/dist/templates/code_styleguides/julia.md +27 -0
  33. package/dist/templates/code_styleguides/kotlin.md +41 -0
  34. package/dist/templates/code_styleguides/php.md +37 -0
  35. package/dist/templates/code_styleguides/python.md +37 -0
  36. package/dist/templates/code_styleguides/react.md +37 -0
  37. package/dist/templates/code_styleguides/ruby.md +39 -0
  38. package/dist/templates/code_styleguides/rust.md +44 -0
  39. package/dist/templates/code_styleguides/shell.md +35 -0
  40. package/dist/templates/code_styleguides/solidity.md +60 -0
  41. package/dist/templates/code_styleguides/sql.md +39 -0
  42. package/dist/templates/code_styleguides/swift.md +36 -0
  43. package/dist/templates/code_styleguides/typescript.md +43 -0
  44. package/dist/templates/code_styleguides/vue.md +38 -0
  45. package/dist/templates/code_styleguides/zig.md +27 -0
  46. package/dist/templates/workflow.md +336 -0
  47. package/dist/tools/background.d.ts +54 -0
  48. package/dist/tools/background.js +198 -0
  49. package/dist/tools/commands.d.ts +11 -0
  50. package/dist/tools/commands.js +80 -0
  51. package/dist/tools/commands.test.d.ts +1 -0
  52. package/dist/tools/commands.test.js +142 -0
  53. package/dist/tools/delegate.d.ts +3 -0
  54. package/dist/tools/delegate.js +45 -0
  55. package/dist/utils/autogenerateFlow.d.ts +65 -0
  56. package/dist/utils/autogenerateFlow.js +391 -0
  57. package/dist/utils/autogenerateFlow.test.d.ts +1 -0
  58. package/dist/utils/autogenerateFlow.test.js +610 -0
  59. package/dist/utils/bootstrap.d.ts +1 -0
  60. package/dist/utils/bootstrap.js +46 -0
  61. package/dist/utils/commandFactory.d.ts +11 -0
  62. package/dist/utils/commandFactory.js +69 -0
  63. package/dist/utils/commitMessages.d.ts +35 -0
  64. package/dist/utils/commitMessages.js +33 -0
  65. package/dist/utils/commitMessages.test.d.ts +1 -0
  66. package/dist/utils/commitMessages.test.js +79 -0
  67. package/dist/utils/configDetection.d.ts +7 -0
  68. package/dist/utils/configDetection.js +49 -0
  69. package/dist/utils/configDetection.test.d.ts +1 -0
  70. package/dist/utils/configDetection.test.js +119 -0
  71. package/dist/utils/contentGeneration.d.ts +10 -0
  72. package/dist/utils/contentGeneration.js +141 -0
  73. package/dist/utils/contentGeneration.test.d.ts +1 -0
  74. package/dist/utils/contentGeneration.test.js +147 -0
  75. package/dist/utils/contextAnalysis.d.ts +100 -0
  76. package/dist/utils/contextAnalysis.js +308 -0
  77. package/dist/utils/contextAnalysis.test.d.ts +1 -0
  78. package/dist/utils/contextAnalysis.test.js +307 -0
  79. package/dist/utils/gitNotes.d.ts +23 -0
  80. package/dist/utils/gitNotes.js +53 -0
  81. package/dist/utils/gitNotes.test.d.ts +1 -0
  82. package/dist/utils/gitNotes.test.js +105 -0
  83. package/dist/utils/ignoreMatcher.d.ts +9 -0
  84. package/dist/utils/ignoreMatcher.js +77 -0
  85. package/dist/utils/ignoreMatcher.test.d.ts +1 -0
  86. package/dist/utils/ignoreMatcher.test.js +126 -0
  87. package/dist/utils/stateManager.d.ts +10 -0
  88. package/dist/utils/stateManager.js +30 -0
  89. package/package.json +90 -0
  90. package/scripts/convert-legacy.cjs +17 -0
  91. package/scripts/postinstall.cjs +38 -0
@@ -0,0 +1,198 @@
1
+ import { tool } from "@opencode-ai/plugin/tool";
2
+ const BACKGROUND_TASK_DESCRIPTION = "Launch a specialized agent in the background to perform research or implementation tasks.";
3
+ const BACKGROUND_OUTPUT_DESCRIPTION = "Retrieve the results or status of a background task.";
4
+ export class BackgroundManager {
5
+ tasks;
6
+ client;
7
+ pollingInterval;
8
+ constructor(ctx) {
9
+ this.tasks = new Map();
10
+ this.client = ctx.client;
11
+ }
12
+ async launch(input) {
13
+ if (!input.agent || input.agent.trim() === "") {
14
+ throw new Error("Agent parameter is required");
15
+ }
16
+ const createResult = await this.client.session.create({
17
+ body: {
18
+ parentID: input.parentSessionID,
19
+ title: `Background: ${input.description}`,
20
+ },
21
+ });
22
+ if (createResult.error) {
23
+ throw new Error(`Failed to create background session: ${createResult.error}`);
24
+ }
25
+ const sessionID = createResult.data.id;
26
+ const task = {
27
+ id: `bg_${Math.random().toString(36).substring(2, 10)}`,
28
+ sessionID,
29
+ parentSessionID: input.parentSessionID,
30
+ parentMessageID: input.parentMessageID,
31
+ description: input.description,
32
+ prompt: input.prompt,
33
+ agent: input.agent,
34
+ status: "running",
35
+ startedAt: new Date(),
36
+ progress: {
37
+ toolCalls: 0,
38
+ lastUpdate: new Date(),
39
+ },
40
+ };
41
+ this.tasks.set(task.id, task);
42
+ this.startPolling();
43
+ this.client.session.promptAsync({
44
+ path: { id: sessionID },
45
+ body: {
46
+ agent: input.agent,
47
+ tools: {
48
+ "cdd_bg_task": false,
49
+ "cdd_delegate": false,
50
+ },
51
+ parts: [{ type: "text", text: input.prompt }],
52
+ },
53
+ }).catch((error) => {
54
+ console.error("[Orchestrator] Background task error:", error);
55
+ task.status = "failed";
56
+ task.error = String(error);
57
+ });
58
+ return task;
59
+ }
60
+ async cancel(id) {
61
+ const task = this.tasks.get(id);
62
+ if (!task)
63
+ return `Task not found: ${id}`;
64
+ if (task.status !== "running")
65
+ return `Task is not running (status: ${task.status})`;
66
+ task.status = "cancelled";
67
+ task.completedAt = new Date();
68
+ // Attempt to notify parent session
69
+ await this.client.session.prompt({
70
+ path: { id: task.parentSessionID },
71
+ body: {
72
+ parts: [{ type: "text", text: `[BACKGROUND TASK CANCELLED] Task "${task.description}" has been manually cancelled.` }],
73
+ },
74
+ }).catch(() => { });
75
+ return `Task ${id} cancelled successfully.`;
76
+ }
77
+ async pollRunningTasks() {
78
+ try {
79
+ const statusResult = await this.client.session.status();
80
+ const allStatuses = (statusResult.data ?? {});
81
+ for (const task of this.tasks.values()) {
82
+ if (task.status !== "running")
83
+ continue;
84
+ const sessionStatus = allStatuses[task.sessionID];
85
+ if (sessionStatus?.type === "idle") {
86
+ this.completeTask(task);
87
+ }
88
+ }
89
+ if (!this.hasRunningTasks()) {
90
+ this.stopPolling();
91
+ }
92
+ }
93
+ catch (e) {
94
+ console.error("[Orchestrator] Polling error:", e);
95
+ }
96
+ }
97
+ completeTask(task) {
98
+ task.status = "completed";
99
+ task.completedAt = new Date();
100
+ this.notifyParentSession(task);
101
+ }
102
+ async notifyParentSession(task) {
103
+ const message = `[BACKGROUND TASK COMPLETED] Task "${task.description}" finished. Use cdd_bg_output with task_id="${task.id}" to get results.`;
104
+ await this.client.session.prompt({
105
+ path: { id: task.parentSessionID },
106
+ body: {
107
+ parts: [{ type: "text", text: message }],
108
+ },
109
+ }).catch(() => { });
110
+ }
111
+ getTask(id) {
112
+ return this.tasks.get(id);
113
+ }
114
+ startPolling() {
115
+ if (this.pollingInterval)
116
+ return;
117
+ this.pollingInterval = setInterval(() => this.pollRunningTasks(), 3000);
118
+ }
119
+ stopPolling() {
120
+ if (this.pollingInterval) {
121
+ clearInterval(this.pollingInterval);
122
+ this.pollingInterval = undefined;
123
+ }
124
+ }
125
+ hasRunningTasks() {
126
+ return Array.from(this.tasks.values()).some(t => t.status === "running");
127
+ }
128
+ }
129
+ export function createBackgroundTask(manager) {
130
+ return tool({
131
+ description: BACKGROUND_TASK_DESCRIPTION,
132
+ args: {
133
+ description: tool.schema.string().describe("Short task description"),
134
+ prompt: tool.schema.string().describe("Full detailed prompt for the agent"),
135
+ agent: tool.schema.string().describe("Agent type to use"),
136
+ },
137
+ async execute(args, toolContext) {
138
+ const ctx = toolContext;
139
+ const task = await manager.launch({
140
+ description: args.description,
141
+ prompt: args.prompt,
142
+ agent: args.agent.trim(),
143
+ parentSessionID: ctx.sessionID,
144
+ parentMessageID: ctx.messageID,
145
+ });
146
+ return `Background task launched successfully. Task ID: ${task.id}`;
147
+ },
148
+ });
149
+ }
150
+ export function createBackgroundOutput(manager) {
151
+ return tool({
152
+ description: BACKGROUND_OUTPUT_DESCRIPTION,
153
+ args: {
154
+ task_id: tool.schema.string().describe("Task ID to get output from"),
155
+ block: tool.schema.boolean().optional().describe("Wait for completion"),
156
+ timeout: tool.schema.number().optional().describe("Max wait time in ms"),
157
+ },
158
+ async execute(args, toolContext) {
159
+ const task = manager.getTask(args.task_id);
160
+ if (!task)
161
+ return `Task not found: ${args.task_id}`;
162
+ if (args.block && task.status === "running") {
163
+ const startTime = Date.now();
164
+ const timeoutMs = Math.min(args.timeout ?? 60000, 600000);
165
+ while (Date.now() - startTime < timeoutMs) {
166
+ await new Promise(r => setTimeout(r, 2000));
167
+ if (manager.getTask(args.task_id)?.status === "completed")
168
+ break;
169
+ }
170
+ }
171
+ if (task.status === "completed") {
172
+ const client = toolContext.client || manager.client;
173
+ const messagesResult = await client.session.messages({
174
+ path: { id: task.sessionID },
175
+ });
176
+ const lastMessage = messagesResult.data
177
+ ?.filter((m) => m.info.role === "assistant")
178
+ .pop();
179
+ const responseText = lastMessage?.parts
180
+ .filter((p) => p.type === "text")
181
+ .map((p) => p.text).join("\n") || "No response.";
182
+ return `### Results for: ${task.description}\n\n${responseText}`;
183
+ }
184
+ return `Task status: ${task.status}. (Started at: ${task.startedAt.toISOString()})`;
185
+ },
186
+ });
187
+ }
188
+ export function createBackgroundCancel(manager) {
189
+ return tool({
190
+ description: "Cancel a running background task",
191
+ args: {
192
+ taskId: tool.schema.string().describe("Task ID to cancel"),
193
+ },
194
+ async execute(args) {
195
+ return await manager.cancel(args.taskId);
196
+ },
197
+ });
198
+ }
@@ -0,0 +1,11 @@
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;
7
+ export declare function createSetupTool(ctx: any): ToolDefinition;
8
+ export declare function createNewTrackTool(ctx: any): ToolDefinition;
9
+ export declare function createImplementTool(ctx: any): ToolDefinition;
10
+ export declare function createStatusTool(ctx: any): ToolDefinition;
11
+ export declare function createRevertTool(ctx: any): ToolDefinition;
@@ -0,0 +1,80 @@
1
+ import { tool } from "@opencode-ai/plugin/tool";
2
+ import { createCDDCommand } 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 setupCommand = createCDDCommand({
9
+ name: "cdd/setup.json",
10
+ description: "Directives lookup tool for scaffolding the project and setting up the CDD environment",
11
+ args: {},
12
+ });
13
+ export const newTrackCommand = createCDDCommand({
14
+ name: "cdd/newTrack.json",
15
+ description: "Directives lookup tool for planning a track, generating track-specific spec documents and updating the tracks file",
16
+ args: {
17
+ description: tool.schema.string().optional().describe("Brief description of the track (feature, bug fix, chore, etc.)"),
18
+ },
19
+ additionalContext: async (_ctx, args) => {
20
+ return {
21
+ args: args.description || "",
22
+ };
23
+ },
24
+ });
25
+ export const implementCommand = createCDDCommand({
26
+ name: "cdd/implement.json",
27
+ description: "Directives lookup tool for executing the tasks defined in the specified track's plan",
28
+ args: {
29
+ track_name: tool.schema.string().optional().describe("Name or description of the track to implement"),
30
+ },
31
+ additionalContext: async (ctx, args) => {
32
+ const strategyFile = ctx.isOMOActive ? "delegate.md" : "manual.md";
33
+ const strategyPath = join(__dirname, "../prompts/strategies", strategyFile);
34
+ let strategySection = "";
35
+ try {
36
+ strategySection = await readFile(strategyPath, "utf-8");
37
+ }
38
+ catch (e) {
39
+ console.warn(`[CDD] Failed to load strategy ${strategyFile}:`, e);
40
+ strategySection = "Error: Could not load execution strategy.";
41
+ }
42
+ return {
43
+ strategy_section: strategySection,
44
+ track_name: args.track_name || "",
45
+ };
46
+ },
47
+ });
48
+ export const statusCommand = createCDDCommand({
49
+ name: "cdd/status.json",
50
+ description: "Directives lookup tool for displaying the current progress of the project",
51
+ args: {},
52
+ });
53
+ export const revertCommand = createCDDCommand({
54
+ name: "cdd/revert.json",
55
+ description: "Directives lookup tool for reverting previous work",
56
+ args: {
57
+ target: tool.schema.string().optional().describe("Target to revert (e.g., 'track <track_id>', 'phase <phase_name>', 'task <task_name>')"),
58
+ },
59
+ additionalContext: async (_ctx, args) => {
60
+ return {
61
+ target: args.target || "",
62
+ };
63
+ },
64
+ });
65
+ // Export as functions for backward compatibility
66
+ export function createSetupTool(ctx) {
67
+ return setupCommand(ctx);
68
+ }
69
+ export function createNewTrackTool(ctx) {
70
+ return newTrackCommand(ctx);
71
+ }
72
+ export function createImplementTool(ctx) {
73
+ return implementCommand(ctx);
74
+ }
75
+ export function createStatusTool(ctx) {
76
+ return statusCommand(ctx);
77
+ }
78
+ export function createRevertTool(ctx) {
79
+ return revertCommand(ctx);
80
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,142 @@
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
+ // Mock fs/promises
5
+ vi.mock("fs/promises", () => ({
6
+ readFile: vi.fn(),
7
+ }));
8
+ describe("Command Tools", () => {
9
+ let mockCtx;
10
+ let mockToolContext;
11
+ beforeEach(() => {
12
+ vi.clearAllMocks();
13
+ mockCtx = {
14
+ directory: "/test/project",
15
+ isOMOActive: false,
16
+ };
17
+ mockToolContext = {
18
+ sessionID: "test-session",
19
+ messageID: "test-message",
20
+ };
21
+ vi.mocked(readFile).mockResolvedValue(JSON.stringify({
22
+ description: "Test command",
23
+ prompt: "Test prompt content"
24
+ }));
25
+ });
26
+ describe("createSetupTool", () => {
27
+ it("should create a tool with correct description", () => {
28
+ const tool = createSetupTool(mockCtx);
29
+ expect(tool.description).toBe("Directives lookup tool for scaffolding the project and setting up the CDD environment");
30
+ });
31
+ it("should return directives JSON string when executed", async () => {
32
+ vi.mocked(readFile).mockResolvedValue(JSON.stringify({
33
+ description: "Setup",
34
+ prompt: "Setup Prompt"
35
+ }));
36
+ const tool = createSetupTool(mockCtx);
37
+ const result = await tool.execute({}, mockToolContext);
38
+ expect(JSON.parse(result)).toEqual({ directives: "Setup Prompt" });
39
+ });
40
+ });
41
+ describe("createNewTrackTool", () => {
42
+ it("should create a tool with correct description", () => {
43
+ const tool = createNewTrackTool(mockCtx);
44
+ expect(tool.description).toBe("Directives lookup tool for planning a track, generating track-specific spec documents and updating the tracks file");
45
+ });
46
+ it("should have optional description argument", () => {
47
+ const tool = createNewTrackTool(mockCtx);
48
+ expect(tool.args).toHaveProperty("description");
49
+ });
50
+ it("should replace description in directives", async () => {
51
+ vi.mocked(readFile).mockResolvedValue(JSON.stringify({
52
+ description: "New Track",
53
+ prompt: "Track description: {{args}}"
54
+ }));
55
+ const tool = createNewTrackTool(mockCtx);
56
+ const result = await tool.execute({ description: "Login feature" }, mockToolContext);
57
+ expect(JSON.parse(result)).toEqual({ directives: "Track description: Login feature" });
58
+ });
59
+ });
60
+ describe("createImplementTool", () => {
61
+ it("should create a tool with correct description", () => {
62
+ const tool = createImplementTool(mockCtx);
63
+ expect(tool.description).toBe("Directives lookup tool for executing the tasks defined in the specified track's plan");
64
+ });
65
+ it("should have optional track_name argument", () => {
66
+ const tool = createImplementTool(mockCtx);
67
+ expect(tool.args).toHaveProperty("track_name");
68
+ });
69
+ it("should replace track_name in directives", async () => {
70
+ vi.mocked(readFile).mockResolvedValue(JSON.stringify({
71
+ description: "Implement",
72
+ prompt: "Track: {{track_name}}"
73
+ }));
74
+ const tool = createImplementTool(mockCtx);
75
+ const result = await tool.execute({ track_name: "auth-track" }, mockToolContext);
76
+ expect(JSON.parse(result)).toEqual({ directives: "Track: auth-track" });
77
+ });
78
+ it("should include strategy section in directives", async () => {
79
+ vi.mocked(readFile).mockImplementation(async (path) => {
80
+ if (typeof path === 'string' && path.endsWith("manual.md")) {
81
+ return "Manual Strategy";
82
+ }
83
+ return JSON.stringify({
84
+ description: "Implement",
85
+ prompt: "Strategy: {{strategy_section}}"
86
+ });
87
+ });
88
+ const tool = createImplementTool(mockCtx);
89
+ const result = await tool.execute({}, mockToolContext);
90
+ expect(JSON.parse(result)).toEqual({ directives: "Strategy: Manual Strategy" });
91
+ });
92
+ });
93
+ describe("createStatusTool", () => {
94
+ it("should create a tool with correct description", () => {
95
+ const tool = createStatusTool(mockCtx);
96
+ expect(tool.description).toBe("Directives lookup tool for displaying the current progress of the project");
97
+ });
98
+ it("should execute and return directives", async () => {
99
+ vi.mocked(readFile).mockResolvedValue(JSON.stringify({
100
+ description: "Status",
101
+ prompt: "Status Prompt"
102
+ }));
103
+ const tool = createStatusTool(mockCtx);
104
+ const result = await tool.execute({}, mockToolContext);
105
+ expect(JSON.parse(result)).toEqual({ directives: "Status Prompt" });
106
+ });
107
+ });
108
+ describe("createRevertTool", () => {
109
+ it("should create a tool with correct description", () => {
110
+ const tool = createRevertTool(mockCtx);
111
+ expect(tool.description).toBe("Directives lookup tool for reverting previous work");
112
+ });
113
+ it("should replace target in directives", async () => {
114
+ vi.mocked(readFile).mockResolvedValue(JSON.stringify({
115
+ description: "Revert",
116
+ prompt: "Target: {{target}}"
117
+ }));
118
+ const tool = createRevertTool(mockCtx);
119
+ const result = await tool.execute({ target: "track 1" }, mockToolContext);
120
+ expect(JSON.parse(result)).toEqual({ directives: "Target: track 1" });
121
+ });
122
+ });
123
+ describe("Error Handling", () => {
124
+ it("should return error in directives if readFile fails", async () => {
125
+ vi.mocked(readFile).mockRejectedValue(new Error("File not found"));
126
+ const tool = createSetupTool(mockCtx);
127
+ const result = await tool.execute({}, mockToolContext);
128
+ expect(JSON.parse(result).directives).toContain("SYSTEM ERROR: Failed to load prompt");
129
+ });
130
+ });
131
+ describe("Prompt Replacement", () => {
132
+ it("should replace standard variables in directives", async () => {
133
+ vi.mocked(readFile).mockResolvedValue(JSON.stringify({
134
+ description: "Test",
135
+ prompt: "Templates: {{templatesDir}}"
136
+ }));
137
+ const tool = createNewTrackTool(mockCtx);
138
+ const result = await tool.execute({}, mockToolContext);
139
+ expect(JSON.parse(result).directives).toContain("Templates:");
140
+ });
141
+ });
142
+ });
@@ -0,0 +1,3 @@
1
+ import { type PluginInput } from "@opencode-ai/plugin";
2
+ import { type ToolDefinition } from "@opencode-ai/plugin/tool";
3
+ export declare function createDelegationTool(ctx: PluginInput): ToolDefinition;
@@ -0,0 +1,45 @@
1
+ import { tool } from "@opencode-ai/plugin/tool";
2
+ export function createDelegationTool(ctx) {
3
+ return tool({
4
+ description: "Delegate a specific task to a specialized subagent",
5
+ args: {
6
+ task_description: tool.schema.string().describe("Summary of the work"),
7
+ subagent_type: tool.schema.string().describe("The name of the agent to call"),
8
+ prompt: tool.schema.string().describe("Detailed instructions for the subagent"),
9
+ },
10
+ async execute(args, toolContext) {
11
+ // 1. Create a sub-session linked to the current one
12
+ const createResult = await ctx.client.session.create({
13
+ body: {
14
+ parentID: toolContext.sessionID,
15
+ title: `${args.task_description} (Delegated to ${args.subagent_type})`,
16
+ },
17
+ });
18
+ if (createResult.error)
19
+ return `Error: ${createResult.error}`;
20
+ const sessionID = createResult.data.id;
21
+ // 2. Send the prompt to the subagent
22
+ await ctx.client.session.prompt({
23
+ path: { id: sessionID },
24
+ body: {
25
+ agent: args.subagent_type,
26
+ tools: {
27
+ "cdd_delegate": false,
28
+ },
29
+ parts: [{ type: "text", text: args.prompt }],
30
+ },
31
+ });
32
+ // 3. Fetch and return the assistant's response
33
+ const messagesResult = await ctx.client.session.messages({
34
+ path: { id: sessionID },
35
+ });
36
+ const lastMessage = messagesResult.data
37
+ ?.filter((m) => m.info.role === "assistant")
38
+ .pop();
39
+ const responseText = lastMessage?.parts
40
+ .filter((p) => p.type === "text")
41
+ .map((p) => p.text).join("\n") || "No response.";
42
+ return `${responseText}\n\n<task_metadata>\nsession_id: ${sessionID}\n</task_metadata>`;
43
+ },
44
+ });
45
+ }
@@ -0,0 +1,65 @@
1
+ import { ProjectContext } from './contextAnalysis.js';
2
+ export type SectionType = 'product_guide' | 'product_guidelines' | 'tech_stack' | 'workflow';
3
+ export type UserChoice = 'A' | 'B' | 'C';
4
+ export type FallbackChoice = 'A' | 'B' | 'C';
5
+ export type FlowStatus = 'generating' | 'presented' | 'accepted' | 'edited' | 'regenerating';
6
+ export type FallbackAction = 'manual' | 'regenerate' | 'accept_partial';
7
+ export interface AutogenerationState {
8
+ questionId: string;
9
+ attemptNumber: number;
10
+ status: FlowStatus;
11
+ content: string;
12
+ userChoice: UserChoice | null;
13
+ guidanceHistory: string[];
14
+ editHistory: string[];
15
+ timestamp: string;
16
+ }
17
+ export interface FlowResult {
18
+ success: boolean;
19
+ content?: string;
20
+ error?: string;
21
+ state?: AutogenerationState;
22
+ failureSummary?: string;
23
+ fallbackPrompt?: string;
24
+ fallbackAction?: FallbackAction;
25
+ fallbackState?: ContextSufficiencyResult;
26
+ ambiguityState?: AmbiguityResult;
27
+ }
28
+ export interface ValidationResult {
29
+ valid: boolean;
30
+ error?: string;
31
+ }
32
+ export interface ContextSufficiencyResult {
33
+ sufficient: boolean;
34
+ reason: string;
35
+ missing: string[];
36
+ }
37
+ export interface AmbiguityResult {
38
+ ambiguous: boolean;
39
+ reason: string;
40
+ signals: string[];
41
+ }
42
+ export declare const MAX_REGENERATION_ATTEMPTS = 3;
43
+ export declare const MIN_CONTEXT_SIGNALS = 2;
44
+ export declare const MIN_CONFIDENCE_THRESHOLD = 0.4;
45
+ export declare function createInitialState(questionId: string): AutogenerationState;
46
+ export declare function generateContentForSection(sectionType: SectionType, context: ProjectContext): FlowResult;
47
+ export declare function validateContent(content: string, sectionType: SectionType): ValidationResult;
48
+ export declare function evaluateContextSufficiency(context: ProjectContext): ContextSufficiencyResult;
49
+ export declare function formatAutogenerationFailure(sectionType: SectionType, error: string, sufficiency: ContextSufficiencyResult, ambiguity: AmbiguityResult): string;
50
+ export declare function evaluateAmbiguity(context: ProjectContext): AmbiguityResult;
51
+ export declare function formatFallbackPrompt(reason: string, missing: string[]): string;
52
+ export declare function parseFallbackChoice(input: string): FallbackChoice | null;
53
+ export declare function resolveFallbackAction(choice: FallbackChoice | null): FallbackAction;
54
+ export declare function handleAccept(state: AutogenerationState, sectionType: SectionType): FlowResult;
55
+ export declare function handleEdit(state: AutogenerationState, editedContent: string, sectionType: SectionType): FlowResult;
56
+ export declare function handleRegenerate(state: AutogenerationState, guidance: string, sectionType: SectionType, context: ProjectContext): FlowResult;
57
+ export declare function formatPresentationPrompt(content: string): string;
58
+ export declare function formatEditPrompt(content: string): string;
59
+ export declare function formatRegeneratePrompt(): string;
60
+ export declare function formatMaxAttemptsPrompt(): string;
61
+ export declare function parseUserChoice(input: string): UserChoice | null;
62
+ export declare function isCancel(input: string): boolean;
63
+ export declare function handleFallbackManual(sectionType: SectionType, reason: string): FlowResult;
64
+ export declare function handleFallbackAcceptPartial(partialContent: string, sectionType: SectionType, reason: string): FlowResult;
65
+ export declare function handleFallbackRegenerate(sectionType: SectionType, context: ProjectContext, additionalContext: string): FlowResult;