opencode-swarm-plugin 0.44.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 (205) hide show
  1. package/bin/swarm.serve.test.ts +6 -4
  2. package/bin/swarm.ts +16 -10
  3. package/dist/compaction-prompt-scoring.js +139 -0
  4. package/dist/eval-capture.js +12811 -0
  5. package/dist/hive.d.ts.map +1 -1
  6. package/dist/index.js +7644 -62599
  7. package/dist/plugin.js +23766 -78721
  8. package/dist/swarm-orchestrate.d.ts.map +1 -1
  9. package/dist/swarm-prompts.d.ts.map +1 -1
  10. package/dist/swarm-review.d.ts.map +1 -1
  11. package/package.json +17 -5
  12. package/.changeset/swarm-insights-data-layer.md +0 -63
  13. package/.hive/analysis/eval-failure-analysis-2025-12-25.md +0 -331
  14. package/.hive/analysis/session-data-quality-audit.md +0 -320
  15. package/.hive/eval-results.json +0 -483
  16. package/.hive/issues.jsonl +0 -138
  17. package/.hive/memories.jsonl +0 -729
  18. package/.opencode/eval-history.jsonl +0 -327
  19. package/.turbo/turbo-build.log +0 -9
  20. package/CHANGELOG.md +0 -2286
  21. package/SCORER-ANALYSIS.md +0 -598
  22. package/docs/analysis/subagent-coordination-patterns.md +0 -902
  23. package/docs/analysis-socratic-planner-pattern.md +0 -504
  24. package/docs/planning/ADR-001-monorepo-structure.md +0 -171
  25. package/docs/planning/ADR-002-package-extraction.md +0 -393
  26. package/docs/planning/ADR-003-performance-improvements.md +0 -451
  27. package/docs/planning/ADR-004-message-queue-features.md +0 -187
  28. package/docs/planning/ADR-005-devtools-observability.md +0 -202
  29. package/docs/planning/ADR-007-swarm-enhancements-worktree-review.md +0 -168
  30. package/docs/planning/ADR-008-worker-handoff-protocol.md +0 -293
  31. package/docs/planning/ADR-009-oh-my-opencode-patterns.md +0 -353
  32. package/docs/planning/ADR-010-cass-inhousing.md +0 -1215
  33. package/docs/planning/ROADMAP.md +0 -368
  34. package/docs/semantic-memory-cli-syntax.md +0 -123
  35. package/docs/swarm-mail-architecture.md +0 -1147
  36. package/docs/testing/context-recovery-test.md +0 -470
  37. package/evals/ARCHITECTURE.md +0 -1189
  38. package/evals/README.md +0 -768
  39. package/evals/compaction-prompt.eval.ts +0 -149
  40. package/evals/compaction-resumption.eval.ts +0 -289
  41. package/evals/coordinator-behavior.eval.ts +0 -307
  42. package/evals/coordinator-session.eval.ts +0 -154
  43. package/evals/evalite.config.ts.bak +0 -15
  44. package/evals/example.eval.ts +0 -31
  45. package/evals/fixtures/cass-baseline.ts +0 -217
  46. package/evals/fixtures/compaction-cases.ts +0 -350
  47. package/evals/fixtures/compaction-prompt-cases.ts +0 -311
  48. package/evals/fixtures/coordinator-sessions.ts +0 -328
  49. package/evals/fixtures/decomposition-cases.ts +0 -105
  50. package/evals/lib/compaction-loader.test.ts +0 -248
  51. package/evals/lib/compaction-loader.ts +0 -320
  52. package/evals/lib/data-loader.evalite-test.ts +0 -289
  53. package/evals/lib/data-loader.test.ts +0 -345
  54. package/evals/lib/data-loader.ts +0 -281
  55. package/evals/lib/llm.ts +0 -115
  56. package/evals/scorers/compaction-prompt-scorers.ts +0 -145
  57. package/evals/scorers/compaction-scorers.ts +0 -305
  58. package/evals/scorers/coordinator-discipline.evalite-test.ts +0 -539
  59. package/evals/scorers/coordinator-discipline.ts +0 -325
  60. package/evals/scorers/index.test.ts +0 -146
  61. package/evals/scorers/index.ts +0 -328
  62. package/evals/scorers/outcome-scorers.evalite-test.ts +0 -27
  63. package/evals/scorers/outcome-scorers.ts +0 -349
  64. package/evals/swarm-decomposition.eval.ts +0 -121
  65. package/examples/commands/swarm.md +0 -745
  66. package/examples/plugin-wrapper-template.ts +0 -2515
  67. package/examples/skills/hive-workflow/SKILL.md +0 -212
  68. package/examples/skills/skill-creator/SKILL.md +0 -223
  69. package/examples/skills/swarm-coordination/SKILL.md +0 -292
  70. package/global-skills/cli-builder/SKILL.md +0 -344
  71. package/global-skills/cli-builder/references/advanced-patterns.md +0 -244
  72. package/global-skills/learning-systems/SKILL.md +0 -644
  73. package/global-skills/skill-creator/LICENSE.txt +0 -202
  74. package/global-skills/skill-creator/SKILL.md +0 -352
  75. package/global-skills/skill-creator/references/output-patterns.md +0 -82
  76. package/global-skills/skill-creator/references/workflows.md +0 -28
  77. package/global-skills/swarm-coordination/SKILL.md +0 -995
  78. package/global-skills/swarm-coordination/references/coordinator-patterns.md +0 -235
  79. package/global-skills/swarm-coordination/references/strategies.md +0 -138
  80. package/global-skills/system-design/SKILL.md +0 -213
  81. package/global-skills/testing-patterns/SKILL.md +0 -430
  82. package/global-skills/testing-patterns/references/dependency-breaking-catalog.md +0 -586
  83. package/opencode-swarm-plugin-0.30.7.tgz +0 -0
  84. package/opencode-swarm-plugin-0.31.0.tgz +0 -0
  85. package/scripts/cleanup-test-memories.ts +0 -346
  86. package/scripts/init-skill.ts +0 -222
  87. package/scripts/migrate-unknown-sessions.ts +0 -349
  88. package/scripts/validate-skill.ts +0 -204
  89. package/src/agent-mail.ts +0 -1724
  90. package/src/anti-patterns.test.ts +0 -1167
  91. package/src/anti-patterns.ts +0 -448
  92. package/src/compaction-capture.integration.test.ts +0 -257
  93. package/src/compaction-hook.test.ts +0 -838
  94. package/src/compaction-hook.ts +0 -1204
  95. package/src/compaction-observability.integration.test.ts +0 -139
  96. package/src/compaction-observability.test.ts +0 -187
  97. package/src/compaction-observability.ts +0 -324
  98. package/src/compaction-prompt-scorers.test.ts +0 -475
  99. package/src/compaction-prompt-scoring.ts +0 -300
  100. package/src/contributor-tools.test.ts +0 -133
  101. package/src/contributor-tools.ts +0 -201
  102. package/src/dashboard.test.ts +0 -611
  103. package/src/dashboard.ts +0 -462
  104. package/src/error-enrichment.test.ts +0 -403
  105. package/src/error-enrichment.ts +0 -219
  106. package/src/eval-capture.test.ts +0 -1015
  107. package/src/eval-capture.ts +0 -929
  108. package/src/eval-gates.test.ts +0 -306
  109. package/src/eval-gates.ts +0 -218
  110. package/src/eval-history.test.ts +0 -508
  111. package/src/eval-history.ts +0 -214
  112. package/src/eval-learning.test.ts +0 -378
  113. package/src/eval-learning.ts +0 -360
  114. package/src/eval-runner.test.ts +0 -223
  115. package/src/eval-runner.ts +0 -402
  116. package/src/export-tools.test.ts +0 -476
  117. package/src/export-tools.ts +0 -257
  118. package/src/hive.integration.test.ts +0 -2241
  119. package/src/hive.ts +0 -1628
  120. package/src/index.ts +0 -940
  121. package/src/learning.integration.test.ts +0 -1815
  122. package/src/learning.ts +0 -1079
  123. package/src/logger.test.ts +0 -189
  124. package/src/logger.ts +0 -135
  125. package/src/mandate-promotion.test.ts +0 -473
  126. package/src/mandate-promotion.ts +0 -239
  127. package/src/mandate-storage.integration.test.ts +0 -601
  128. package/src/mandate-storage.test.ts +0 -578
  129. package/src/mandate-storage.ts +0 -794
  130. package/src/mandates.ts +0 -540
  131. package/src/memory-tools.test.ts +0 -195
  132. package/src/memory-tools.ts +0 -344
  133. package/src/memory.integration.test.ts +0 -334
  134. package/src/memory.test.ts +0 -158
  135. package/src/memory.ts +0 -527
  136. package/src/model-selection.test.ts +0 -188
  137. package/src/model-selection.ts +0 -68
  138. package/src/observability-tools.test.ts +0 -359
  139. package/src/observability-tools.ts +0 -871
  140. package/src/output-guardrails.test.ts +0 -438
  141. package/src/output-guardrails.ts +0 -381
  142. package/src/pattern-maturity.test.ts +0 -1160
  143. package/src/pattern-maturity.ts +0 -525
  144. package/src/planning-guardrails.test.ts +0 -491
  145. package/src/planning-guardrails.ts +0 -438
  146. package/src/plugin.ts +0 -23
  147. package/src/post-compaction-tracker.test.ts +0 -251
  148. package/src/post-compaction-tracker.ts +0 -237
  149. package/src/query-tools.test.ts +0 -636
  150. package/src/query-tools.ts +0 -324
  151. package/src/rate-limiter.integration.test.ts +0 -466
  152. package/src/rate-limiter.ts +0 -774
  153. package/src/replay-tools.test.ts +0 -496
  154. package/src/replay-tools.ts +0 -240
  155. package/src/repo-crawl.integration.test.ts +0 -441
  156. package/src/repo-crawl.ts +0 -610
  157. package/src/schemas/cell-events.test.ts +0 -347
  158. package/src/schemas/cell-events.ts +0 -807
  159. package/src/schemas/cell.ts +0 -257
  160. package/src/schemas/evaluation.ts +0 -166
  161. package/src/schemas/index.test.ts +0 -199
  162. package/src/schemas/index.ts +0 -286
  163. package/src/schemas/mandate.ts +0 -232
  164. package/src/schemas/swarm-context.ts +0 -115
  165. package/src/schemas/task.ts +0 -161
  166. package/src/schemas/worker-handoff.test.ts +0 -302
  167. package/src/schemas/worker-handoff.ts +0 -131
  168. package/src/sessions/agent-discovery.test.ts +0 -137
  169. package/src/sessions/agent-discovery.ts +0 -112
  170. package/src/sessions/index.ts +0 -15
  171. package/src/skills.integration.test.ts +0 -1192
  172. package/src/skills.test.ts +0 -643
  173. package/src/skills.ts +0 -1549
  174. package/src/storage.integration.test.ts +0 -341
  175. package/src/storage.ts +0 -884
  176. package/src/structured.integration.test.ts +0 -817
  177. package/src/structured.test.ts +0 -1046
  178. package/src/structured.ts +0 -762
  179. package/src/swarm-decompose.test.ts +0 -188
  180. package/src/swarm-decompose.ts +0 -1302
  181. package/src/swarm-deferred.integration.test.ts +0 -157
  182. package/src/swarm-deferred.test.ts +0 -38
  183. package/src/swarm-insights.test.ts +0 -214
  184. package/src/swarm-insights.ts +0 -459
  185. package/src/swarm-mail.integration.test.ts +0 -970
  186. package/src/swarm-mail.ts +0 -739
  187. package/src/swarm-orchestrate.integration.test.ts +0 -282
  188. package/src/swarm-orchestrate.test.ts +0 -548
  189. package/src/swarm-orchestrate.ts +0 -3084
  190. package/src/swarm-prompts.test.ts +0 -1270
  191. package/src/swarm-prompts.ts +0 -2077
  192. package/src/swarm-research.integration.test.ts +0 -701
  193. package/src/swarm-research.test.ts +0 -698
  194. package/src/swarm-research.ts +0 -472
  195. package/src/swarm-review.integration.test.ts +0 -285
  196. package/src/swarm-review.test.ts +0 -879
  197. package/src/swarm-review.ts +0 -709
  198. package/src/swarm-strategies.ts +0 -407
  199. package/src/swarm-worktree.test.ts +0 -501
  200. package/src/swarm-worktree.ts +0 -575
  201. package/src/swarm.integration.test.ts +0 -2377
  202. package/src/swarm.ts +0 -38
  203. package/src/tool-adapter.integration.test.ts +0 -1221
  204. package/src/tool-availability.ts +0 -461
  205. 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
- };