opencode-swarm-plugin 0.43.0 → 0.44.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.
Files changed (208) hide show
  1. package/bin/cass.characterization.test.ts +422 -0
  2. package/bin/swarm.serve.test.ts +6 -4
  3. package/bin/swarm.test.ts +68 -0
  4. package/bin/swarm.ts +81 -8
  5. package/dist/compaction-prompt-scoring.js +139 -0
  6. package/dist/contributor-tools.d.ts +42 -0
  7. package/dist/contributor-tools.d.ts.map +1 -0
  8. package/dist/eval-capture.js +12811 -0
  9. package/dist/hive.d.ts.map +1 -1
  10. package/dist/index.d.ts +12 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +7728 -62590
  13. package/dist/plugin.js +23833 -78695
  14. package/dist/sessions/agent-discovery.d.ts +59 -0
  15. package/dist/sessions/agent-discovery.d.ts.map +1 -0
  16. package/dist/sessions/index.d.ts +10 -0
  17. package/dist/sessions/index.d.ts.map +1 -0
  18. package/dist/swarm-orchestrate.d.ts.map +1 -1
  19. package/dist/swarm-prompts.d.ts.map +1 -1
  20. package/dist/swarm-review.d.ts.map +1 -1
  21. package/package.json +17 -5
  22. package/.changeset/swarm-insights-data-layer.md +0 -63
  23. package/.hive/analysis/eval-failure-analysis-2025-12-25.md +0 -331
  24. package/.hive/analysis/session-data-quality-audit.md +0 -320
  25. package/.hive/eval-results.json +0 -483
  26. package/.hive/issues.jsonl +0 -138
  27. package/.hive/memories.jsonl +0 -729
  28. package/.opencode/eval-history.jsonl +0 -327
  29. package/.turbo/turbo-build.log +0 -9
  30. package/CHANGELOG.md +0 -2255
  31. package/SCORER-ANALYSIS.md +0 -598
  32. package/docs/analysis/subagent-coordination-patterns.md +0 -902
  33. package/docs/analysis-socratic-planner-pattern.md +0 -504
  34. package/docs/planning/ADR-001-monorepo-structure.md +0 -171
  35. package/docs/planning/ADR-002-package-extraction.md +0 -393
  36. package/docs/planning/ADR-003-performance-improvements.md +0 -451
  37. package/docs/planning/ADR-004-message-queue-features.md +0 -187
  38. package/docs/planning/ADR-005-devtools-observability.md +0 -202
  39. package/docs/planning/ADR-007-swarm-enhancements-worktree-review.md +0 -168
  40. package/docs/planning/ADR-008-worker-handoff-protocol.md +0 -293
  41. package/docs/planning/ADR-009-oh-my-opencode-patterns.md +0 -353
  42. package/docs/planning/ROADMAP.md +0 -368
  43. package/docs/semantic-memory-cli-syntax.md +0 -123
  44. package/docs/swarm-mail-architecture.md +0 -1147
  45. package/docs/testing/context-recovery-test.md +0 -470
  46. package/evals/ARCHITECTURE.md +0 -1189
  47. package/evals/README.md +0 -768
  48. package/evals/compaction-prompt.eval.ts +0 -149
  49. package/evals/compaction-resumption.eval.ts +0 -289
  50. package/evals/coordinator-behavior.eval.ts +0 -307
  51. package/evals/coordinator-session.eval.ts +0 -154
  52. package/evals/evalite.config.ts.bak +0 -15
  53. package/evals/example.eval.ts +0 -31
  54. package/evals/fixtures/compaction-cases.ts +0 -350
  55. package/evals/fixtures/compaction-prompt-cases.ts +0 -311
  56. package/evals/fixtures/coordinator-sessions.ts +0 -328
  57. package/evals/fixtures/decomposition-cases.ts +0 -105
  58. package/evals/lib/compaction-loader.test.ts +0 -248
  59. package/evals/lib/compaction-loader.ts +0 -320
  60. package/evals/lib/data-loader.evalite-test.ts +0 -289
  61. package/evals/lib/data-loader.test.ts +0 -345
  62. package/evals/lib/data-loader.ts +0 -281
  63. package/evals/lib/llm.ts +0 -115
  64. package/evals/scorers/compaction-prompt-scorers.ts +0 -145
  65. package/evals/scorers/compaction-scorers.ts +0 -305
  66. package/evals/scorers/coordinator-discipline.evalite-test.ts +0 -539
  67. package/evals/scorers/coordinator-discipline.ts +0 -325
  68. package/evals/scorers/index.test.ts +0 -146
  69. package/evals/scorers/index.ts +0 -328
  70. package/evals/scorers/outcome-scorers.evalite-test.ts +0 -27
  71. package/evals/scorers/outcome-scorers.ts +0 -349
  72. package/evals/swarm-decomposition.eval.ts +0 -121
  73. package/examples/commands/swarm.md +0 -745
  74. package/examples/plugin-wrapper-template.ts +0 -2426
  75. package/examples/skills/hive-workflow/SKILL.md +0 -212
  76. package/examples/skills/skill-creator/SKILL.md +0 -223
  77. package/examples/skills/swarm-coordination/SKILL.md +0 -292
  78. package/global-skills/cli-builder/SKILL.md +0 -344
  79. package/global-skills/cli-builder/references/advanced-patterns.md +0 -244
  80. package/global-skills/learning-systems/SKILL.md +0 -644
  81. package/global-skills/skill-creator/LICENSE.txt +0 -202
  82. package/global-skills/skill-creator/SKILL.md +0 -352
  83. package/global-skills/skill-creator/references/output-patterns.md +0 -82
  84. package/global-skills/skill-creator/references/workflows.md +0 -28
  85. package/global-skills/swarm-coordination/SKILL.md +0 -995
  86. package/global-skills/swarm-coordination/references/coordinator-patterns.md +0 -235
  87. package/global-skills/swarm-coordination/references/strategies.md +0 -138
  88. package/global-skills/system-design/SKILL.md +0 -213
  89. package/global-skills/testing-patterns/SKILL.md +0 -430
  90. package/global-skills/testing-patterns/references/dependency-breaking-catalog.md +0 -586
  91. package/opencode-swarm-plugin-0.30.7.tgz +0 -0
  92. package/opencode-swarm-plugin-0.31.0.tgz +0 -0
  93. package/scripts/cleanup-test-memories.ts +0 -346
  94. package/scripts/init-skill.ts +0 -222
  95. package/scripts/migrate-unknown-sessions.ts +0 -349
  96. package/scripts/validate-skill.ts +0 -204
  97. package/src/agent-mail.ts +0 -1724
  98. package/src/anti-patterns.test.ts +0 -1167
  99. package/src/anti-patterns.ts +0 -448
  100. package/src/compaction-capture.integration.test.ts +0 -257
  101. package/src/compaction-hook.test.ts +0 -838
  102. package/src/compaction-hook.ts +0 -1204
  103. package/src/compaction-observability.integration.test.ts +0 -139
  104. package/src/compaction-observability.test.ts +0 -187
  105. package/src/compaction-observability.ts +0 -324
  106. package/src/compaction-prompt-scorers.test.ts +0 -475
  107. package/src/compaction-prompt-scoring.ts +0 -300
  108. package/src/dashboard.test.ts +0 -611
  109. package/src/dashboard.ts +0 -462
  110. package/src/error-enrichment.test.ts +0 -403
  111. package/src/error-enrichment.ts +0 -219
  112. package/src/eval-capture.test.ts +0 -1015
  113. package/src/eval-capture.ts +0 -929
  114. package/src/eval-gates.test.ts +0 -306
  115. package/src/eval-gates.ts +0 -218
  116. package/src/eval-history.test.ts +0 -508
  117. package/src/eval-history.ts +0 -214
  118. package/src/eval-learning.test.ts +0 -378
  119. package/src/eval-learning.ts +0 -360
  120. package/src/eval-runner.test.ts +0 -223
  121. package/src/eval-runner.ts +0 -402
  122. package/src/export-tools.test.ts +0 -476
  123. package/src/export-tools.ts +0 -257
  124. package/src/hive.integration.test.ts +0 -2241
  125. package/src/hive.ts +0 -1628
  126. package/src/index.ts +0 -935
  127. package/src/learning.integration.test.ts +0 -1815
  128. package/src/learning.ts +0 -1079
  129. package/src/logger.test.ts +0 -189
  130. package/src/logger.ts +0 -135
  131. package/src/mandate-promotion.test.ts +0 -473
  132. package/src/mandate-promotion.ts +0 -239
  133. package/src/mandate-storage.integration.test.ts +0 -601
  134. package/src/mandate-storage.test.ts +0 -578
  135. package/src/mandate-storage.ts +0 -794
  136. package/src/mandates.ts +0 -540
  137. package/src/memory-tools.test.ts +0 -195
  138. package/src/memory-tools.ts +0 -344
  139. package/src/memory.integration.test.ts +0 -334
  140. package/src/memory.test.ts +0 -158
  141. package/src/memory.ts +0 -527
  142. package/src/model-selection.test.ts +0 -188
  143. package/src/model-selection.ts +0 -68
  144. package/src/observability-tools.test.ts +0 -359
  145. package/src/observability-tools.ts +0 -871
  146. package/src/output-guardrails.test.ts +0 -438
  147. package/src/output-guardrails.ts +0 -381
  148. package/src/pattern-maturity.test.ts +0 -1160
  149. package/src/pattern-maturity.ts +0 -525
  150. package/src/planning-guardrails.test.ts +0 -491
  151. package/src/planning-guardrails.ts +0 -438
  152. package/src/plugin.ts +0 -23
  153. package/src/post-compaction-tracker.test.ts +0 -251
  154. package/src/post-compaction-tracker.ts +0 -237
  155. package/src/query-tools.test.ts +0 -636
  156. package/src/query-tools.ts +0 -324
  157. package/src/rate-limiter.integration.test.ts +0 -466
  158. package/src/rate-limiter.ts +0 -774
  159. package/src/replay-tools.test.ts +0 -496
  160. package/src/replay-tools.ts +0 -240
  161. package/src/repo-crawl.integration.test.ts +0 -441
  162. package/src/repo-crawl.ts +0 -610
  163. package/src/schemas/cell-events.test.ts +0 -347
  164. package/src/schemas/cell-events.ts +0 -807
  165. package/src/schemas/cell.ts +0 -257
  166. package/src/schemas/evaluation.ts +0 -166
  167. package/src/schemas/index.test.ts +0 -199
  168. package/src/schemas/index.ts +0 -286
  169. package/src/schemas/mandate.ts +0 -232
  170. package/src/schemas/swarm-context.ts +0 -115
  171. package/src/schemas/task.ts +0 -161
  172. package/src/schemas/worker-handoff.test.ts +0 -302
  173. package/src/schemas/worker-handoff.ts +0 -131
  174. package/src/skills.integration.test.ts +0 -1192
  175. package/src/skills.test.ts +0 -643
  176. package/src/skills.ts +0 -1549
  177. package/src/storage.integration.test.ts +0 -341
  178. package/src/storage.ts +0 -884
  179. package/src/structured.integration.test.ts +0 -817
  180. package/src/structured.test.ts +0 -1046
  181. package/src/structured.ts +0 -762
  182. package/src/swarm-decompose.test.ts +0 -188
  183. package/src/swarm-decompose.ts +0 -1302
  184. package/src/swarm-deferred.integration.test.ts +0 -157
  185. package/src/swarm-deferred.test.ts +0 -38
  186. package/src/swarm-insights.test.ts +0 -214
  187. package/src/swarm-insights.ts +0 -459
  188. package/src/swarm-mail.integration.test.ts +0 -970
  189. package/src/swarm-mail.ts +0 -739
  190. package/src/swarm-orchestrate.integration.test.ts +0 -282
  191. package/src/swarm-orchestrate.test.ts +0 -548
  192. package/src/swarm-orchestrate.ts +0 -3084
  193. package/src/swarm-prompts.test.ts +0 -1270
  194. package/src/swarm-prompts.ts +0 -2077
  195. package/src/swarm-research.integration.test.ts +0 -701
  196. package/src/swarm-research.test.ts +0 -698
  197. package/src/swarm-research.ts +0 -472
  198. package/src/swarm-review.integration.test.ts +0 -285
  199. package/src/swarm-review.test.ts +0 -879
  200. package/src/swarm-review.ts +0 -709
  201. package/src/swarm-strategies.ts +0 -407
  202. package/src/swarm-worktree.test.ts +0 -501
  203. package/src/swarm-worktree.ts +0 -575
  204. package/src/swarm.integration.test.ts +0 -2377
  205. package/src/swarm.ts +0 -38
  206. package/src/tool-adapter.integration.test.ts +0 -1221
  207. package/src/tool-availability.ts +0 -461
  208. package/tsconfig.json +0 -28
@@ -1,1302 +0,0 @@
1
- /**
2
- * Swarm Decompose Module - Task decomposition and validation
3
- *
4
- * Handles breaking tasks into parallelizable subtasks with file assignments,
5
- * validates decomposition structure, and detects conflicts.
6
- *
7
- * Key responsibilities:
8
- * - Decomposition prompt generation
9
- * - CellTree validation
10
- * - File conflict detection
11
- * - Instruction conflict detection
12
- * - Delegation to planner subagents
13
- */
14
-
15
- import { tool } from "@opencode-ai/plugin";
16
- import { z } from "zod";
17
- import { CellTreeSchema } from "./schemas";
18
- import {
19
- POSITIVE_MARKERS,
20
- NEGATIVE_MARKERS,
21
- type DecompositionStrategy,
22
- } from "./swarm-strategies";
23
- import { captureCoordinatorEvent } from "./eval-capture.js";
24
-
25
- // ============================================================================
26
- // Decomposition Prompt (temporary - will be moved to swarm-prompts.ts)
27
- // ============================================================================
28
-
29
- /**
30
- * Prompt for decomposing a task into parallelizable subtasks.
31
- *
32
- * Used by swarm_decompose to instruct the agent on how to break down work.
33
- * The agent responds with a CellTree that gets validated.
34
- */
35
- const DECOMPOSITION_PROMPT = `You are decomposing a task into parallelizable subtasks for a swarm of agents.
36
-
37
- ## Task
38
- {task}
39
-
40
- {context_section}
41
-
42
- ## MANDATORY: Beads Issue Tracking
43
-
44
- **Every subtask MUST become a bead.** This is non-negotiable.
45
-
46
- After decomposition, the coordinator will:
47
- 1. Create an epic bead for the overall task
48
- 2. Create child cells for each subtask
49
- 3. Track progress through bead status updates
50
- 4. Close cells with summaries when complete
51
-
52
- Agents MUST update their bead status as they work. No silent progress.
53
-
54
- ## Requirements
55
-
56
- 1. **Break into independent subtasks** that can run in parallel (as many as needed)
57
- 2. **Assign files** - each subtask must specify which files it will modify
58
- 3. **No file overlap** - files cannot appear in multiple subtasks (they get exclusive locks)
59
- 4. **Order by dependency** - if subtask B needs subtask A's output, A must come first in the array
60
- 5. **Estimate complexity** - 1 (trivial) to 5 (complex)
61
- 6. **Plan aggressively** - break down more than you think necessary, smaller is better
62
-
63
- ## Response Format
64
-
65
- Respond with a JSON object matching this schema:
66
-
67
- \`\`\`typescript
68
- {
69
- epic: {
70
- title: string, // Epic title for the hive tracker
71
- description?: string // Brief description of the overall goal
72
- },
73
- subtasks: [
74
- {
75
- title: string, // What this subtask accomplishes
76
- description?: string, // Detailed instructions for the agent
77
- files: string[], // Files this subtask will modify (globs allowed)
78
- dependencies: number[], // Indices of subtasks this depends on (0-indexed)
79
- estimated_complexity: 1-5 // Effort estimate
80
- },
81
- // ... more subtasks
82
- ]
83
- }
84
- \`\`\`
85
-
86
- ## Guidelines
87
-
88
- - **Plan aggressively** - when in doubt, split further. 3 small tasks > 1 medium task
89
- - **Prefer smaller, focused subtasks** over large complex ones
90
- - **Include test files** in the same subtask as the code they test
91
- - **Consider shared types** - if multiple files share types, handle that first
92
- - **Think about imports** - changes to exported APIs affect downstream files
93
- - **Explicit > implicit** - spell out what each subtask should do, don't assume
94
-
95
- ## File Assignment Examples
96
-
97
- - Schema change: \`["src/schemas/user.ts", "src/schemas/index.ts"]\`
98
- - Component + test: \`["src/components/Button.tsx", "src/components/Button.test.tsx"]\`
99
- - API route: \`["src/app/api/users/route.ts"]\`
100
-
101
- Now decompose the task:`;
102
-
103
- /**
104
- * Strategy-specific decomposition prompt template
105
- */
106
- const STRATEGY_DECOMPOSITION_PROMPT = `You are decomposing a task into parallelizable subtasks for a swarm of agents.
107
-
108
- ## Task
109
- {task}
110
-
111
- {strategy_guidelines}
112
-
113
- {context_section}
114
-
115
- {cass_history}
116
-
117
- {skills_context}
118
-
119
- ## MANDATORY: Beads Issue Tracking
120
-
121
- **Every subtask MUST become a bead.** This is non-negotiable.
122
-
123
- After decomposition, the coordinator will:
124
- 1. Create an epic bead for the overall task
125
- 2. Create child cells for each subtask
126
- 3. Track progress through bead status updates
127
- 4. Close cells with summaries when complete
128
-
129
- Agents MUST update their bead status as they work. No silent progress.
130
-
131
- ## Requirements
132
-
133
- 1. **Break into independent subtasks** that can run in parallel (as many as needed)
134
- 2. **Assign files** - each subtask must specify which files it will modify
135
- 3. **No file overlap** - files cannot appear in multiple subtasks (they get exclusive locks)
136
- 4. **Order by dependency** - if subtask B needs subtask A's output, A must come first in the array
137
- 5. **Estimate complexity** - 1 (trivial) to 5 (complex)
138
- 6. **Plan aggressively** - break down more than you think necessary, smaller is better
139
-
140
- ## Response Format
141
-
142
- Respond with a JSON object matching this schema:
143
-
144
- \`\`\`typescript
145
- {
146
- epic: {
147
- title: string, // Epic title for the hive tracker
148
- description?: string // Brief description of the overall goal
149
- },
150
- subtasks: [
151
- {
152
- title: string, // What this subtask accomplishes
153
- description?: string, // Detailed instructions for the agent
154
- files: string[], // Files this subtask will modify (globs allowed)
155
- dependencies: number[], // Indices of subtasks this depends on (0-indexed)
156
- estimated_complexity: 1-5 // Effort estimate
157
- },
158
- // ... more subtasks
159
- ]
160
- }
161
- \`\`\`
162
-
163
- Now decompose the task:`;
164
-
165
- // ============================================================================
166
- // Conflict Detection
167
- // ============================================================================
168
-
169
- /**
170
- * A detected conflict between subtask instructions
171
- */
172
- export interface InstructionConflict {
173
- subtask_a: number;
174
- subtask_b: number;
175
- directive_a: string;
176
- directive_b: string;
177
- conflict_type: "positive_negative" | "contradictory";
178
- description: string;
179
- }
180
-
181
- /**
182
- * Extract directives from text based on marker words
183
- */
184
- function extractDirectives(text: string): {
185
- positive: string[];
186
- negative: string[];
187
- } {
188
- const sentences = text.split(/[.!?\n]+/).map((s) => s.trim().toLowerCase());
189
- const positive: string[] = [];
190
- const negative: string[] = [];
191
-
192
- for (const sentence of sentences) {
193
- if (!sentence) continue;
194
-
195
- const hasPositive = POSITIVE_MARKERS.some((m) => sentence.includes(m));
196
- const hasNegative = NEGATIVE_MARKERS.some((m) => sentence.includes(m));
197
-
198
- if (hasPositive && !hasNegative) {
199
- positive.push(sentence);
200
- } else if (hasNegative) {
201
- negative.push(sentence);
202
- }
203
- }
204
-
205
- return { positive, negative };
206
- }
207
-
208
- /**
209
- * Check if two directives conflict
210
- *
211
- * Simple heuristic: look for common subjects with opposite polarity
212
- */
213
- function directivesConflict(positive: string, negative: string): boolean {
214
- // Extract key nouns/concepts (simple word overlap check)
215
- const positiveWords = new Set(
216
- positive.split(/\s+/).filter((w) => w.length > 3),
217
- );
218
- const negativeWords = negative.split(/\s+/).filter((w) => w.length > 3);
219
-
220
- // If they share significant words, they might conflict
221
- const overlap = negativeWords.filter((w) => positiveWords.has(w));
222
- return overlap.length >= 2;
223
- }
224
-
225
- /**
226
- * Detect conflicts between subtask instructions
227
- *
228
- * Looks for cases where one subtask says "always use X" and another says "avoid X".
229
- *
230
- * @param subtasks - Array of subtask descriptions
231
- * @returns Array of detected conflicts
232
- *
233
- * @see https://github.com/Dicklesworthstone/cass_memory_system/blob/main/src/curate.ts#L36-L89
234
- */
235
- export function detectInstructionConflicts(
236
- subtasks: Array<{ title: string; description?: string }>,
237
- ): InstructionConflict[] {
238
- const conflicts: InstructionConflict[] = [];
239
-
240
- // Extract directives from each subtask
241
- const subtaskDirectives = subtasks.map((s, i) => ({
242
- index: i,
243
- title: s.title,
244
- ...extractDirectives(`${s.title} ${s.description || ""}`),
245
- }));
246
-
247
- // Compare each pair of subtasks
248
- for (let i = 0; i < subtaskDirectives.length; i++) {
249
- for (let j = i + 1; j < subtaskDirectives.length; j++) {
250
- const a = subtaskDirectives[i];
251
- const b = subtaskDirectives[j];
252
-
253
- // Check if A's positive conflicts with B's negative
254
- for (const posA of a.positive) {
255
- for (const negB of b.negative) {
256
- if (directivesConflict(posA, negB)) {
257
- conflicts.push({
258
- subtask_a: i,
259
- subtask_b: j,
260
- directive_a: posA,
261
- directive_b: negB,
262
- conflict_type: "positive_negative",
263
- description: `Subtask ${i} says "${posA}" but subtask ${j} says "${negB}"`,
264
- });
265
- }
266
- }
267
- }
268
-
269
- // Check if B's positive conflicts with A's negative
270
- for (const posB of b.positive) {
271
- for (const negA of a.negative) {
272
- if (directivesConflict(posB, negA)) {
273
- conflicts.push({
274
- subtask_a: j,
275
- subtask_b: i,
276
- directive_a: posB,
277
- directive_b: negA,
278
- conflict_type: "positive_negative",
279
- description: `Subtask ${j} says "${posB}" but subtask ${i} says "${negA}"`,
280
- });
281
- }
282
- }
283
- }
284
- }
285
- }
286
-
287
- return conflicts;
288
- }
289
-
290
- /**
291
- * Detect file conflicts in a bead tree
292
- *
293
- * @param subtasks - Array of subtasks with file assignments
294
- * @returns Array of files that appear in multiple subtasks
295
- */
296
- export function detectFileConflicts(
297
- subtasks: Array<{ files: string[] }>,
298
- ): string[] {
299
- const allFiles = new Map<string, number>();
300
- const conflicts: string[] = [];
301
-
302
- for (const subtask of subtasks) {
303
- for (const file of subtask.files) {
304
- const count = allFiles.get(file) || 0;
305
- allFiles.set(file, count + 1);
306
- if (count === 1) {
307
- // Second occurrence - it's a conflict
308
- conflicts.push(file);
309
- }
310
- }
311
- }
312
-
313
- return conflicts;
314
- }
315
-
316
- // ============================================================================
317
- // CASS History Integration
318
- // ============================================================================
319
-
320
- /**
321
- * CASS search result from similar past tasks
322
- */
323
- interface CassSearchResult {
324
- query: string;
325
- results: Array<{
326
- source_path: string;
327
- line: number;
328
- agent: string;
329
- preview: string;
330
- score: number;
331
- }>;
332
- }
333
-
334
- /**
335
- * CASS query result with status
336
- */
337
- type CassQueryResult =
338
- | { status: "unavailable" }
339
- | { status: "failed"; error?: string }
340
- | { status: "empty"; query: string }
341
- | { status: "success"; data: CassSearchResult };
342
-
343
- /**
344
- * Query CASS for similar past tasks
345
- *
346
- * @param task - Task description to search for
347
- * @param limit - Maximum results to return
348
- * @returns Structured result with status indicator
349
- */
350
- async function queryCassHistory(
351
- task: string,
352
- limit: number = 3,
353
- ): Promise<CassQueryResult> {
354
- // Check if CASS is available
355
- try {
356
- const result = await Bun.$`cass search ${task} --limit ${limit} --json`
357
- .quiet()
358
- .nothrow();
359
-
360
- if (result.exitCode !== 0) {
361
- const error = result.stderr.toString();
362
- console.warn(
363
- `[swarm] CASS search failed (exit ${result.exitCode}):`,
364
- error,
365
- );
366
- return { status: "failed", error };
367
- }
368
-
369
- const output = result.stdout.toString();
370
- if (!output.trim()) {
371
- return { status: "empty", query: task };
372
- }
373
-
374
- try {
375
- const parsed = JSON.parse(output);
376
- const searchResult: CassSearchResult = {
377
- query: task,
378
- results: Array.isArray(parsed) ? parsed : parsed.results || [],
379
- };
380
-
381
- if (searchResult.results.length === 0) {
382
- return { status: "empty", query: task };
383
- }
384
-
385
- return { status: "success", data: searchResult };
386
- } catch (error) {
387
- console.warn(`[swarm] Failed to parse CASS output:`, error);
388
- return { status: "failed", error: String(error) };
389
- }
390
- } catch (error) {
391
- console.error(`[swarm] CASS query error:`, error);
392
- return { status: "unavailable" };
393
- }
394
- }
395
-
396
- /**
397
- * Format CASS history for inclusion in decomposition prompt
398
- */
399
- function formatCassHistoryForPrompt(history: CassSearchResult): string {
400
- if (history.results.length === 0) {
401
- return "";
402
- }
403
-
404
- const lines = [
405
- "## Similar Past Tasks",
406
- "",
407
- "These similar tasks were found in agent history:",
408
- "",
409
- ...history.results.slice(0, 3).map((r, i) => {
410
- const preview = r.preview.slice(0, 200).replace(/\n/g, " ");
411
- return `${i + 1}. [${r.agent}] ${preview}...`;
412
- }),
413
- "",
414
- "Consider patterns that worked in these past tasks.",
415
- "",
416
- ];
417
-
418
- return lines.join("\n");
419
- }
420
-
421
- // ============================================================================
422
- // Tool Definitions
423
- // ============================================================================
424
-
425
- /**
426
- * Decompose a task into a bead tree
427
- *
428
- * This is a PROMPT tool - it returns a prompt for the agent to respond to.
429
- * The agent's response (JSON) should be validated with CellTreeSchema.
430
- *
431
- * Optionally queries CASS for similar past tasks to inform decomposition.
432
- */
433
- export const swarm_decompose = tool({
434
- description:
435
- "Generate decomposition prompt for breaking task into parallelizable subtasks. Optionally queries CASS for similar past tasks.",
436
- args: {
437
- task: tool.schema.string().min(1).describe("Task description to decompose"),
438
- context: tool.schema
439
- .string()
440
- .optional()
441
- .describe("Additional context (codebase info, constraints, etc.)"),
442
- query_cass: tool.schema
443
- .boolean()
444
- .optional()
445
- .describe("Query CASS for similar past tasks (default: true)"),
446
- cass_limit: tool.schema
447
- .number()
448
- .int()
449
- .min(1)
450
- .optional()
451
- .describe("Max CASS results to include (default: 3)"),
452
- },
453
- async execute(args) {
454
- // Import needed modules
455
- const { formatMemoryQueryForDecomposition } = await import("./learning");
456
-
457
- // Query CASS for similar past tasks
458
- let cassContext = "";
459
- let cassResultInfo: {
460
- queried: boolean;
461
- results_found?: number;
462
- included_in_context?: boolean;
463
- reason?: string;
464
- };
465
-
466
- if (args.query_cass !== false) {
467
- const cassResult = await queryCassHistory(
468
- args.task,
469
- args.cass_limit ?? 3,
470
- );
471
- if (cassResult.status === "success") {
472
- cassContext = formatCassHistoryForPrompt(cassResult.data);
473
- cassResultInfo = {
474
- queried: true,
475
- results_found: cassResult.data.results.length,
476
- included_in_context: true,
477
- };
478
- } else {
479
- cassResultInfo = {
480
- queried: true,
481
- results_found: 0,
482
- included_in_context: false,
483
- reason: cassResult.status,
484
- };
485
- }
486
- } else {
487
- cassResultInfo = { queried: false, reason: "disabled" };
488
- }
489
-
490
- // Combine user context with CASS history
491
- const fullContext = [args.context, cassContext]
492
- .filter(Boolean)
493
- .join("\n\n");
494
-
495
- // Format the decomposition prompt
496
- const contextSection = fullContext
497
- ? `## Additional Context\n${fullContext}`
498
- : "## Additional Context\n(none provided)";
499
-
500
- const prompt = DECOMPOSITION_PROMPT.replace("{task}", args.task)
501
- .replace("{context_section}", contextSection);
502
-
503
- // Return the prompt and schema info for the caller
504
- return JSON.stringify(
505
- {
506
- prompt,
507
- expected_schema: "CellTree",
508
- schema_hint: {
509
- epic: { title: "string", description: "string?" },
510
- subtasks: [
511
- {
512
- title: "string",
513
- description: "string?",
514
- files: "string[]",
515
- dependencies: "number[]",
516
- estimated_complexity: "1-5",
517
- },
518
- ],
519
- },
520
- validation_note:
521
- "Parse agent response as JSON and validate with CellTreeSchema from schemas/bead.ts",
522
- cass_history: cassResultInfo,
523
- // Add semantic-memory query instruction
524
- memory_query: formatMemoryQueryForDecomposition(args.task, 3),
525
- },
526
- null,
527
- 2,
528
- );
529
- },
530
- });
531
-
532
- /**
533
- * Validate a decomposition response from an agent
534
- *
535
- * Use this after the agent responds to swarm:decompose to validate the structure.
536
- */
537
- export const swarm_validate_decomposition = tool({
538
- description: "Validate a decomposition response against CellTreeSchema and capture for eval",
539
- args: {
540
- response: tool.schema
541
- .string()
542
- .describe("JSON response from agent (CellTree format)"),
543
- project_path: tool.schema
544
- .string()
545
- .optional()
546
- .describe("Project path for eval capture"),
547
- task: tool.schema
548
- .string()
549
- .optional()
550
- .describe("Original task description for eval capture"),
551
- context: tool.schema
552
- .string()
553
- .optional()
554
- .describe("Context provided for decomposition"),
555
- strategy: tool.schema
556
- .enum(["file-based", "feature-based", "risk-based", "auto"])
557
- .optional()
558
- .describe("Decomposition strategy used"),
559
- epic_id: tool.schema
560
- .string()
561
- .optional()
562
- .describe("Epic ID for eval capture"),
563
- },
564
- async execute(args) {
565
- try {
566
- const parsed = JSON.parse(args.response);
567
- const validated = CellTreeSchema.parse(parsed);
568
-
569
- // Additional validation: check for file conflicts
570
- const conflicts = detectFileConflicts(validated.subtasks);
571
-
572
- if (conflicts.length > 0) {
573
- return JSON.stringify(
574
- {
575
- valid: false,
576
- error: `File conflicts detected: ${conflicts.join(", ")}`,
577
- hint: "Each file can only be assigned to one subtask",
578
- },
579
- null,
580
- 2,
581
- );
582
- }
583
-
584
- // Check dependency indices are valid
585
- for (let i = 0; i < validated.subtasks.length; i++) {
586
- const deps = validated.subtasks[i].dependencies;
587
- for (const dep of deps) {
588
- // Check bounds first
589
- if (dep < 0 || dep >= validated.subtasks.length) {
590
- return JSON.stringify(
591
- {
592
- valid: false,
593
- error: `Invalid dependency: subtask ${i} depends on ${dep}, but only ${validated.subtasks.length} subtasks exist (indices 0-${validated.subtasks.length - 1})`,
594
- hint: "Dependency index is out of bounds",
595
- },
596
- null,
597
- 2,
598
- );
599
- }
600
- // Check forward references
601
- if (dep >= i) {
602
- return JSON.stringify(
603
- {
604
- valid: false,
605
- error: `Invalid dependency: subtask ${i} depends on ${dep}, but dependencies must be earlier in the array`,
606
- hint: "Reorder subtasks so dependencies come before dependents",
607
- },
608
- null,
609
- 2,
610
- );
611
- }
612
- }
613
- }
614
-
615
- // Check for instruction conflicts between subtasks
616
- const instructionConflicts = detectInstructionConflicts(
617
- validated.subtasks,
618
- );
619
-
620
- // Capture decomposition for eval if all required params provided
621
- if (
622
- args.project_path &&
623
- args.task &&
624
- args.strategy &&
625
- args.epic_id
626
- ) {
627
- try {
628
- const { captureDecomposition } = await import("./eval-capture.js");
629
- captureDecomposition({
630
- epicId: args.epic_id,
631
- projectPath: args.project_path,
632
- task: args.task,
633
- context: args.context,
634
- strategy: args.strategy,
635
- epicTitle: validated.epic.title,
636
- epicDescription: validated.epic.description,
637
- subtasks: validated.subtasks.map((s) => ({
638
- title: s.title,
639
- description: s.description,
640
- files: s.files,
641
- dependencies: s.dependencies,
642
- estimated_complexity: s.estimated_complexity,
643
- })),
644
- });
645
- } catch (error) {
646
- // Non-fatal - don't block validation if capture fails
647
- console.warn("[swarm_validate_decomposition] Failed to capture decomposition:", error);
648
- }
649
- }
650
-
651
- return JSON.stringify(
652
- {
653
- valid: true,
654
- cell_tree: validated,
655
- stats: {
656
- subtask_count: validated.subtasks.length,
657
- total_files: new Set(validated.subtasks.flatMap((s) => s.files))
658
- .size,
659
- total_complexity: validated.subtasks.reduce(
660
- (sum, s) => sum + s.estimated_complexity,
661
- 0,
662
- ),
663
- },
664
- // Include conflicts as warnings (not blocking)
665
- warnings:
666
- instructionConflicts.length > 0
667
- ? {
668
- instruction_conflicts: instructionConflicts,
669
- hint: "Review these potential conflicts between subtask instructions",
670
- }
671
- : undefined,
672
- },
673
- null,
674
- 2,
675
- );
676
- } catch (error) {
677
- if (error instanceof z.ZodError) {
678
- return JSON.stringify(
679
- {
680
- valid: false,
681
- error: "Schema validation failed",
682
- details: error.issues,
683
- },
684
- null,
685
- 2,
686
- );
687
- }
688
- if (error instanceof SyntaxError) {
689
- return JSON.stringify(
690
- {
691
- valid: false,
692
- error: "Invalid JSON",
693
- details: error.message,
694
- },
695
- null,
696
- 2,
697
- );
698
- }
699
- throw error;
700
- }
701
- },
702
- });
703
-
704
- /**
705
- * Delegate task decomposition to a swarm/planner subagent
706
- *
707
- * Returns a prompt for spawning a planner agent that will handle all decomposition
708
- * reasoning. This keeps the coordinator context lean by offloading:
709
- * - Strategy selection
710
- * - CASS queries
711
- * - Skills discovery
712
- * - File analysis
713
- * - CellTree generation
714
- *
715
- * The planner returns ONLY structured CellTree JSON, which the coordinator
716
- * validates and uses to create cells.
717
- *
718
- * @example
719
- * ```typescript
720
- * // Coordinator workflow:
721
- * const delegateResult = await swarm_delegate_planning({
722
- * task: "Add user authentication",
723
- * context: "Next.js 14 app",
724
- * });
725
- *
726
- * // Parse the result
727
- * const { prompt, subagent_type } = JSON.parse(delegateResult);
728
- *
729
- * // Spawn subagent using Task tool
730
- * const plannerResponse = await Task(prompt, subagent_type);
731
- *
732
- * // Validate the response
733
- * await swarm_validate_decomposition({ response: plannerResponse });
734
- * ```
735
- */
736
- export const swarm_delegate_planning = tool({
737
- description:
738
- "Delegate task decomposition to a swarm/planner subagent. Returns a prompt to spawn the planner. Use this to keep coordinator context lean - all planning reasoning happens in the subagent.",
739
- args: {
740
- task: tool.schema.string().min(1).describe("The task to decompose"),
741
- context: tool.schema
742
- .string()
743
- .optional()
744
- .describe("Additional context to include"),
745
- strategy: tool.schema
746
- .enum(["auto", "file-based", "feature-based", "risk-based"])
747
- .optional()
748
- .default("auto")
749
- .describe("Decomposition strategy (default: auto-detect)"),
750
- query_cass: tool.schema
751
- .boolean()
752
- .optional()
753
- .default(true)
754
- .describe("Query CASS for similar past tasks (default: true)"),
755
- },
756
- async execute(args, _ctx) {
757
- // Import needed modules
758
- const { selectStrategy, formatStrategyGuidelines } =
759
- await import("./swarm-strategies");
760
- const { formatMemoryQueryForDecomposition } = await import("./learning");
761
- const { listSkills, getSkillsContextForSwarm, findRelevantSkills } =
762
- await import("./skills");
763
-
764
- // Select strategy
765
- let selectedStrategy: Exclude<DecompositionStrategy, "auto">;
766
- let strategyReasoning: string;
767
-
768
- if (args.strategy && args.strategy !== "auto") {
769
- selectedStrategy = args.strategy;
770
- strategyReasoning = `User-specified strategy: ${selectedStrategy}`;
771
- } else {
772
- const selection = selectStrategy(args.task);
773
- selectedStrategy = selection.strategy;
774
- strategyReasoning = selection.reasoning;
775
- }
776
-
777
- // Capture strategy selection decision
778
- try {
779
- captureCoordinatorEvent({
780
- session_id: _ctx.sessionID || "unknown",
781
- epic_id: "planning", // No epic ID yet - this is pre-decomposition
782
- timestamp: new Date().toISOString(),
783
- event_type: "DECISION",
784
- decision_type: "strategy_selected",
785
- payload: {
786
- strategy: selectedStrategy,
787
- reasoning: strategyReasoning,
788
- task_preview: args.task.slice(0, 100),
789
- },
790
- });
791
- } catch (error) {
792
- // Non-fatal - don't block planning if capture fails
793
- console.warn("[swarm_delegate_planning] Failed to capture strategy_selected:", error);
794
- }
795
-
796
- // Query CASS for similar past tasks
797
- let cassContext = "";
798
- let cassResultInfo: {
799
- queried: boolean;
800
- results_found?: number;
801
- included_in_context?: boolean;
802
- reason?: string;
803
- };
804
-
805
- if (args.query_cass !== false) {
806
- const cassResult = await queryCassHistory(args.task, 3);
807
- if (cassResult.status === "success") {
808
- cassContext = formatCassHistoryForPrompt(cassResult.data);
809
- cassResultInfo = {
810
- queried: true,
811
- results_found: cassResult.data.results.length,
812
- included_in_context: true,
813
- };
814
- } else {
815
- cassResultInfo = {
816
- queried: true,
817
- results_found: 0,
818
- included_in_context: false,
819
- reason: cassResult.status,
820
- };
821
- }
822
- } else {
823
- cassResultInfo = { queried: false, reason: "disabled" };
824
- }
825
-
826
- // Fetch skills context
827
- let skillsContext = "";
828
- let skillsInfo: { included: boolean; count?: number; relevant?: string[] } =
829
- {
830
- included: false,
831
- };
832
-
833
- const allSkills = await listSkills();
834
- if (allSkills.length > 0) {
835
- skillsContext = await getSkillsContextForSwarm();
836
- const relevantSkills = await findRelevantSkills(args.task);
837
- skillsInfo = {
838
- included: true,
839
- count: allSkills.length,
840
- relevant: relevantSkills,
841
- };
842
-
843
- // Add suggestion for relevant skills
844
- if (relevantSkills.length > 0) {
845
- skillsContext += `\n\n**Suggested skills for this task**: ${relevantSkills.join(", ")}`;
846
- }
847
- }
848
-
849
- // Format strategy guidelines
850
- const strategyGuidelines = formatStrategyGuidelines(selectedStrategy);
851
-
852
- // Combine user context
853
- const contextSection = args.context
854
- ? `## Additional Context\n${args.context}`
855
- : "## Additional Context\n(none provided)";
856
-
857
- // Build the planning prompt with clear instructions for JSON-only output
858
- const planningPrompt = STRATEGY_DECOMPOSITION_PROMPT.replace(
859
- "{task}",
860
- args.task,
861
- )
862
- .replace("{strategy_guidelines}", strategyGuidelines)
863
- .replace("{context_section}", contextSection)
864
- .replace("{cass_history}", cassContext || "")
865
- .replace("{skills_context}", skillsContext || "");
866
-
867
- // Add strict JSON-only instructions for the subagent
868
- const subagentInstructions = `
869
- ## CRITICAL: Output Format
870
-
871
- You are a planner subagent. Your ONLY output must be valid JSON matching the CellTree schema.
872
-
873
- DO NOT include:
874
- - Explanatory text before or after the JSON
875
- - Markdown code fences (\`\`\`json)
876
- - Commentary or reasoning
877
-
878
- OUTPUT ONLY the raw JSON object.
879
-
880
- ## Example Output
881
-
882
- {
883
- "epic": {
884
- "title": "Add user authentication",
885
- "description": "Implement OAuth-based authentication system"
886
- },
887
- "subtasks": [
888
- {
889
- "title": "Set up OAuth provider",
890
- "description": "Configure OAuth client credentials and redirect URLs",
891
- "files": ["src/auth/oauth.ts", "src/config/auth.ts"],
892
- "dependencies": [],
893
- "estimated_complexity": 2
894
- },
895
- {
896
- "title": "Create auth routes",
897
- "description": "Implement login, logout, and callback routes",
898
- "files": ["src/app/api/auth/[...nextauth]/route.ts"],
899
- "dependencies": [0],
900
- "estimated_complexity": 3
901
- }
902
- ]
903
- }
904
-
905
- Now generate the CellTree for the given task.`;
906
-
907
- const fullPrompt = `${planningPrompt}\n\n${subagentInstructions}`;
908
-
909
- // Return structured output for coordinator
910
- return JSON.stringify(
911
- {
912
- prompt: fullPrompt,
913
- subagent_type: "swarm/planner",
914
- description: "Task decomposition planning",
915
- strategy: {
916
- selected: selectedStrategy,
917
- reasoning: strategyReasoning,
918
- },
919
- expected_output: "CellTree JSON (raw JSON, no markdown)",
920
- next_steps: [
921
- "1. Spawn subagent with Task tool using returned prompt",
922
- "2. Parse subagent response as JSON",
923
- "3. Validate with swarm_validate_decomposition",
924
- "4. Create cells with hive_create_epic",
925
- ],
926
- cass_history: cassResultInfo,
927
- skills: skillsInfo,
928
- // Add semantic-memory query instruction
929
- memory_query: formatMemoryQueryForDecomposition(args.task, 3),
930
- },
931
- null,
932
- 2,
933
- );
934
- },
935
- });
936
-
937
- // ============================================================================
938
- // Errors
939
- // ============================================================================
940
-
941
- export class SwarmError extends Error {
942
- constructor(
943
- message: string,
944
- public readonly operation: string,
945
- public readonly details?: unknown,
946
- ) {
947
- super(message);
948
- this.name = "SwarmError";
949
- }
950
- }
951
-
952
- export class DecompositionError extends SwarmError {
953
- constructor(
954
- message: string,
955
- public readonly zodError?: z.ZodError,
956
- ) {
957
- super(message, "decompose", zodError?.issues);
958
- }
959
- }
960
-
961
- /**
962
- * Planning phase state machine for Socratic planning
963
- */
964
- type PlanningPhase = "questioning" | "alternatives" | "recommendation" | "ready";
965
-
966
- /**
967
- * Planning mode that determines interaction level
968
- */
969
- type PlanningMode = "socratic" | "fast" | "auto" | "confirm-only";
970
-
971
- /**
972
- * Socratic planning output structure
973
- */
974
- interface SocraticPlanOutput {
975
- mode: PlanningMode;
976
- phase: PlanningPhase;
977
- questions?: Array<{ question: string; options?: string[] }>;
978
- alternatives?: Array<{
979
- name: string;
980
- description: string;
981
- tradeoffs: string;
982
- }>;
983
- recommendation?: { approach: string; reasoning: string };
984
- memory_context?: string;
985
- codebase_context?: {
986
- git_status?: string;
987
- relevant_files?: string[];
988
- };
989
- ready_to_decompose: boolean;
990
- next_action?: string;
991
- }
992
-
993
- /**
994
- * Interactive planning tool with Socratic questioning
995
- *
996
- * Implements a planning phase BEFORE decomposition that:
997
- * 1. Gathers context (git, files, semantic memory)
998
- * 2. Asks clarifying questions (socratic mode)
999
- * 3. Explores alternatives with tradeoffs
1000
- * 4. Recommends an approach with reasoning
1001
- * 5. Confirms before proceeding to decomposition
1002
- *
1003
- * Modes:
1004
- * - socratic: Full interactive planning with questions, alternatives, recommendations
1005
- * - fast: Skip brainstorming, go straight to decomposition with memory context
1006
- * - auto: Auto-select best approach based on task keywords, minimal interaction
1007
- * - confirm-only: Show decomposition, wait for yes/no confirmation
1008
- *
1009
- * Based on the Socratic Planner Pattern from obra/superpowers.
1010
- *
1011
- * @see docs/analysis-socratic-planner-pattern.md
1012
- */
1013
- export const swarm_plan_interactive = tool({
1014
- description:
1015
- "Interactive planning phase with Socratic questioning before decomposition. Supports multiple modes from full interactive to auto-proceed.",
1016
- args: {
1017
- task: tool.schema.string().min(1).describe("The task to plan"),
1018
- mode: tool.schema
1019
- .enum(["socratic", "fast", "auto", "confirm-only"])
1020
- .default("socratic")
1021
- .describe("Planning mode: socratic (full), fast (skip questions), auto (minimal), confirm-only (single yes/no)"),
1022
- context: tool.schema
1023
- .string()
1024
- .optional()
1025
- .describe("Optional additional context about the task"),
1026
- user_response: tool.schema
1027
- .string()
1028
- .optional()
1029
- .describe("User's response to a previous question (for multi-turn socratic mode)"),
1030
- phase: tool.schema
1031
- .enum(["questioning", "alternatives", "recommendation", "ready"])
1032
- .optional()
1033
- .describe("Current planning phase (for resuming multi-turn interaction)"),
1034
- },
1035
- async execute(args): Promise<string> {
1036
- // Import needed modules
1037
- const { selectStrategy, formatStrategyGuidelines, STRATEGIES } =
1038
- await import("./swarm-strategies");
1039
- const { formatMemoryQueryForDecomposition } = await import("./learning");
1040
-
1041
- // Determine current phase
1042
- const currentPhase: PlanningPhase = args.phase || "questioning";
1043
- const mode: PlanningMode = args.mode || "socratic";
1044
-
1045
- // Gather context - always do this regardless of mode
1046
- let memoryContext = "";
1047
- let codebaseContext: { git_status?: string; relevant_files?: string[] } = {};
1048
-
1049
- // Generate semantic memory query instruction
1050
- // Note: Semantic memory is accessed via OpenCode's global tools, not as a direct import
1051
- // The coordinator should call semantic-memory_find before calling this tool
1052
- // and pass results in the context parameter
1053
- try {
1054
- const memoryQuery = formatMemoryQueryForDecomposition(args.task, 3);
1055
- memoryContext = `[Memory Query Instruction]\n${memoryQuery.instruction}\nQuery: "${memoryQuery.query}"\nLimit: ${memoryQuery.limit}`;
1056
- } catch (error) {
1057
- console.warn("[swarm_plan_interactive] Memory query formatting failed:", error);
1058
- }
1059
-
1060
- // Get git context for codebase awareness
1061
- try {
1062
- const gitResult = await Bun.$`git status --short`.quiet().nothrow();
1063
- if (gitResult.exitCode === 0) {
1064
- codebaseContext.git_status = gitResult.stdout.toString().trim();
1065
- }
1066
- } catch (error) {
1067
- // Git not available or not in a git repo - continue without it
1068
- }
1069
-
1070
- // Fast mode: Skip to recommendation
1071
- if (mode === "fast") {
1072
- const strategyResult = selectStrategy(args.task);
1073
- const guidelines = formatStrategyGuidelines(strategyResult.strategy);
1074
-
1075
- const output: SocraticPlanOutput = {
1076
- mode: "fast",
1077
- phase: "ready",
1078
- recommendation: {
1079
- approach: strategyResult.strategy,
1080
- reasoning: `${strategyResult.reasoning}\n\n${guidelines}`,
1081
- },
1082
- memory_context: memoryContext || undefined,
1083
- codebase_context: Object.keys(codebaseContext).length > 0 ? codebaseContext : undefined,
1084
- ready_to_decompose: true,
1085
- next_action: "Proceed to swarm_decompose or swarm_delegate_planning",
1086
- };
1087
-
1088
- return JSON.stringify(output, null, 2);
1089
- }
1090
-
1091
- // Auto mode: Auto-select and proceed
1092
- if (mode === "auto") {
1093
- const strategyResult = selectStrategy(args.task);
1094
-
1095
- const output: SocraticPlanOutput = {
1096
- mode: "auto",
1097
- phase: "ready",
1098
- recommendation: {
1099
- approach: strategyResult.strategy,
1100
- reasoning: `Auto-selected based on task keywords: ${strategyResult.reasoning}`,
1101
- },
1102
- memory_context: memoryContext || undefined,
1103
- codebase_context: Object.keys(codebaseContext).length > 0 ? codebaseContext : undefined,
1104
- ready_to_decompose: true,
1105
- next_action: "Auto-proceeding to decomposition",
1106
- };
1107
-
1108
- return JSON.stringify(output, null, 2);
1109
- }
1110
-
1111
- // Confirm-only mode: Generate decomposition, show it, wait for yes/no
1112
- if (mode === "confirm-only") {
1113
- // This mode will be handled by calling swarm_delegate_planning
1114
- // and then asking for confirmation on the result
1115
- const output: SocraticPlanOutput = {
1116
- mode: "confirm-only",
1117
- phase: "ready",
1118
- recommendation: {
1119
- approach: "Will generate decomposition for your review",
1120
- reasoning: "Use swarm_delegate_planning to generate the plan, then present it for yes/no confirmation",
1121
- },
1122
- memory_context: memoryContext || undefined,
1123
- codebase_context: Object.keys(codebaseContext).length > 0 ? codebaseContext : undefined,
1124
- ready_to_decompose: false,
1125
- next_action: "Call swarm_delegate_planning, then show result and ask for confirmation",
1126
- };
1127
-
1128
- return JSON.stringify(output, null, 2);
1129
- }
1130
-
1131
- // Socratic mode: Full interactive planning
1132
- // Phase 1: Questioning
1133
- if (currentPhase === "questioning") {
1134
- // Analyze task to identify what needs clarification
1135
- const taskLower = args.task.toLowerCase();
1136
- const questions: Array<{ question: string; options?: string[] }> = [];
1137
-
1138
- // Check for vague task signals from skill
1139
- const isVague = {
1140
- noFiles: !taskLower.includes("src/") && !taskLower.includes("file"),
1141
- vagueVerb:
1142
- taskLower.includes("improve") ||
1143
- taskLower.includes("fix") ||
1144
- taskLower.includes("update") ||
1145
- taskLower.includes("make better"),
1146
- noSuccessCriteria: !taskLower.includes("test") && !taskLower.includes("verify"),
1147
- };
1148
-
1149
- // Generate clarifying questions (one at a time)
1150
- if (isVague.noFiles) {
1151
- questions.push({
1152
- question: "Which part of the codebase should this change affect?",
1153
- options: [
1154
- "Core functionality (src/)",
1155
- "UI components (components/)",
1156
- "API routes (app/api/)",
1157
- "Configuration and tooling",
1158
- "Tests",
1159
- ],
1160
- });
1161
- } else if (isVague.vagueVerb) {
1162
- questions.push({
1163
- question: "What specific change are you looking for?",
1164
- options: [
1165
- "Add new functionality",
1166
- "Modify existing behavior",
1167
- "Remove/deprecate something",
1168
- "Refactor without behavior change",
1169
- "Fix a bug",
1170
- ],
1171
- });
1172
- } else if (isVague.noSuccessCriteria) {
1173
- questions.push({
1174
- question: "How will we know this task is complete?",
1175
- options: [
1176
- "All tests pass",
1177
- "Feature works as demonstrated",
1178
- "Code review approved",
1179
- "Documentation updated",
1180
- "Performance target met",
1181
- ],
1182
- });
1183
- }
1184
-
1185
- // If task seems clear, move to alternatives phase
1186
- if (questions.length === 0) {
1187
- const output: SocraticPlanOutput = {
1188
- mode: "socratic",
1189
- phase: "alternatives",
1190
- memory_context: memoryContext || undefined,
1191
- codebase_context: Object.keys(codebaseContext).length > 0 ? codebaseContext : undefined,
1192
- ready_to_decompose: false,
1193
- next_action: "Task is clear. Call again with phase=alternatives to explore approaches",
1194
- };
1195
- return JSON.stringify(output, null, 2);
1196
- }
1197
-
1198
- // Return first question only (Socratic principle: one at a time)
1199
- const output: SocraticPlanOutput = {
1200
- mode: "socratic",
1201
- phase: "questioning",
1202
- questions: [questions[0]],
1203
- memory_context: memoryContext || undefined,
1204
- codebase_context: Object.keys(codebaseContext).length > 0 ? codebaseContext : undefined,
1205
- ready_to_decompose: false,
1206
- next_action: "User should answer this question, then call again with user_response",
1207
- };
1208
-
1209
- return JSON.stringify(output, null, 2);
1210
- }
1211
-
1212
- // Phase 2: Alternatives
1213
- if (currentPhase === "alternatives") {
1214
- const strategyResult = selectStrategy(args.task);
1215
-
1216
- // Build 2-3 alternative approaches
1217
- const alternatives: Array<{
1218
- name: string;
1219
- description: string;
1220
- tradeoffs: string;
1221
- }> = [];
1222
-
1223
- // Primary recommendation
1224
- alternatives.push({
1225
- name: strategyResult.strategy,
1226
- description: strategyResult.reasoning,
1227
- tradeoffs: `Confidence: ${(strategyResult.confidence * 100).toFixed(0)}%. ${STRATEGIES[strategyResult.strategy].description}`,
1228
- });
1229
-
1230
- // Add top 2 alternatives
1231
- for (let i = 0; i < Math.min(2, strategyResult.alternatives.length); i++) {
1232
- const alt = strategyResult.alternatives[i];
1233
- alternatives.push({
1234
- name: alt.strategy,
1235
- description: STRATEGIES[alt.strategy].description,
1236
- tradeoffs: `Match score: ${alt.score}. ${STRATEGIES[alt.strategy].antiPatterns[0] || "Consider trade-offs carefully"}`,
1237
- });
1238
- }
1239
-
1240
- const output: SocraticPlanOutput = {
1241
- mode: "socratic",
1242
- phase: "alternatives",
1243
- alternatives,
1244
- memory_context: memoryContext || undefined,
1245
- codebase_context: Object.keys(codebaseContext).length > 0 ? codebaseContext : undefined,
1246
- ready_to_decompose: false,
1247
- next_action: "User should choose an approach, then call again with phase=recommendation",
1248
- };
1249
-
1250
- return JSON.stringify(output, null, 2);
1251
- }
1252
-
1253
- // Phase 3: Recommendation
1254
- if (currentPhase === "recommendation") {
1255
- const strategyResult = selectStrategy(args.task);
1256
- const guidelines = formatStrategyGuidelines(strategyResult.strategy);
1257
-
1258
- const output: SocraticPlanOutput = {
1259
- mode: "socratic",
1260
- phase: "recommendation",
1261
- recommendation: {
1262
- approach: strategyResult.strategy,
1263
- reasoning: `Based on your input and task analysis:\n\n${strategyResult.reasoning}\n\n${guidelines}`,
1264
- },
1265
- memory_context: memoryContext || undefined,
1266
- codebase_context: Object.keys(codebaseContext).length > 0 ? codebaseContext : undefined,
1267
- ready_to_decompose: false,
1268
- next_action: "User should confirm to proceed. Then call again with phase=ready",
1269
- };
1270
-
1271
- return JSON.stringify(output, null, 2);
1272
- }
1273
-
1274
- // Phase 4: Ready
1275
- if (currentPhase === "ready") {
1276
- const output: SocraticPlanOutput = {
1277
- mode: "socratic",
1278
- phase: "ready",
1279
- recommendation: {
1280
- approach: "Confirmed by user",
1281
- reasoning: "Ready to proceed with decomposition",
1282
- },
1283
- memory_context: memoryContext || undefined,
1284
- codebase_context: Object.keys(codebaseContext).length > 0 ? codebaseContext : undefined,
1285
- ready_to_decompose: true,
1286
- next_action: "Proceed to swarm_decompose or swarm_delegate_planning",
1287
- };
1288
-
1289
- return JSON.stringify(output, null, 2);
1290
- }
1291
-
1292
- // Should never reach here
1293
- throw new Error(`Invalid planning phase: ${currentPhase}`);
1294
- },
1295
- });
1296
-
1297
- export const decomposeTools = {
1298
- swarm_decompose,
1299
- swarm_validate_decomposition,
1300
- swarm_delegate_planning,
1301
- swarm_plan_interactive,
1302
- };