opencode-swarm-plugin 0.44.0 → 0.44.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/swarm.serve.test.ts +6 -4
- package/bin/swarm.ts +18 -12
- package/dist/compaction-prompt-scoring.js +139 -0
- package/dist/eval-capture.js +12811 -0
- package/dist/hive.d.ts.map +1 -1
- package/dist/hive.js +14834 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7743 -62593
- package/dist/plugin.js +24052 -78907
- package/dist/swarm-orchestrate.d.ts.map +1 -1
- package/dist/swarm-prompts.d.ts.map +1 -1
- package/dist/swarm-prompts.js +39407 -0
- package/dist/swarm-review.d.ts.map +1 -1
- package/dist/swarm-validation.d.ts +127 -0
- package/dist/swarm-validation.d.ts.map +1 -0
- package/dist/validators/index.d.ts +7 -0
- package/dist/validators/index.d.ts.map +1 -0
- package/dist/validators/schema-validator.d.ts +58 -0
- package/dist/validators/schema-validator.d.ts.map +1 -0
- package/package.json +17 -5
- package/.changeset/swarm-insights-data-layer.md +0 -63
- package/.hive/analysis/eval-failure-analysis-2025-12-25.md +0 -331
- package/.hive/analysis/session-data-quality-audit.md +0 -320
- package/.hive/eval-results.json +0 -483
- package/.hive/issues.jsonl +0 -138
- package/.hive/memories.jsonl +0 -729
- package/.opencode/eval-history.jsonl +0 -327
- package/.turbo/turbo-build.log +0 -9
- package/CHANGELOG.md +0 -2286
- package/SCORER-ANALYSIS.md +0 -598
- package/docs/analysis/subagent-coordination-patterns.md +0 -902
- package/docs/analysis-socratic-planner-pattern.md +0 -504
- package/docs/planning/ADR-001-monorepo-structure.md +0 -171
- package/docs/planning/ADR-002-package-extraction.md +0 -393
- package/docs/planning/ADR-003-performance-improvements.md +0 -451
- package/docs/planning/ADR-004-message-queue-features.md +0 -187
- package/docs/planning/ADR-005-devtools-observability.md +0 -202
- package/docs/planning/ADR-007-swarm-enhancements-worktree-review.md +0 -168
- package/docs/planning/ADR-008-worker-handoff-protocol.md +0 -293
- package/docs/planning/ADR-009-oh-my-opencode-patterns.md +0 -353
- package/docs/planning/ADR-010-cass-inhousing.md +0 -1215
- package/docs/planning/ROADMAP.md +0 -368
- package/docs/semantic-memory-cli-syntax.md +0 -123
- package/docs/swarm-mail-architecture.md +0 -1147
- package/docs/testing/context-recovery-test.md +0 -470
- package/evals/ARCHITECTURE.md +0 -1189
- package/evals/README.md +0 -768
- package/evals/compaction-prompt.eval.ts +0 -149
- package/evals/compaction-resumption.eval.ts +0 -289
- package/evals/coordinator-behavior.eval.ts +0 -307
- package/evals/coordinator-session.eval.ts +0 -154
- package/evals/evalite.config.ts.bak +0 -15
- package/evals/example.eval.ts +0 -31
- package/evals/fixtures/cass-baseline.ts +0 -217
- package/evals/fixtures/compaction-cases.ts +0 -350
- package/evals/fixtures/compaction-prompt-cases.ts +0 -311
- package/evals/fixtures/coordinator-sessions.ts +0 -328
- package/evals/fixtures/decomposition-cases.ts +0 -105
- package/evals/lib/compaction-loader.test.ts +0 -248
- package/evals/lib/compaction-loader.ts +0 -320
- package/evals/lib/data-loader.evalite-test.ts +0 -289
- package/evals/lib/data-loader.test.ts +0 -345
- package/evals/lib/data-loader.ts +0 -281
- package/evals/lib/llm.ts +0 -115
- package/evals/scorers/compaction-prompt-scorers.ts +0 -145
- package/evals/scorers/compaction-scorers.ts +0 -305
- package/evals/scorers/coordinator-discipline.evalite-test.ts +0 -539
- package/evals/scorers/coordinator-discipline.ts +0 -325
- package/evals/scorers/index.test.ts +0 -146
- package/evals/scorers/index.ts +0 -328
- package/evals/scorers/outcome-scorers.evalite-test.ts +0 -27
- package/evals/scorers/outcome-scorers.ts +0 -349
- package/evals/swarm-decomposition.eval.ts +0 -121
- package/examples/commands/swarm.md +0 -745
- package/examples/plugin-wrapper-template.ts +0 -2515
- package/examples/skills/hive-workflow/SKILL.md +0 -212
- package/examples/skills/skill-creator/SKILL.md +0 -223
- package/examples/skills/swarm-coordination/SKILL.md +0 -292
- package/global-skills/cli-builder/SKILL.md +0 -344
- package/global-skills/cli-builder/references/advanced-patterns.md +0 -244
- package/global-skills/learning-systems/SKILL.md +0 -644
- package/global-skills/skill-creator/LICENSE.txt +0 -202
- package/global-skills/skill-creator/SKILL.md +0 -352
- package/global-skills/skill-creator/references/output-patterns.md +0 -82
- package/global-skills/skill-creator/references/workflows.md +0 -28
- package/global-skills/swarm-coordination/SKILL.md +0 -995
- package/global-skills/swarm-coordination/references/coordinator-patterns.md +0 -235
- package/global-skills/swarm-coordination/references/strategies.md +0 -138
- package/global-skills/system-design/SKILL.md +0 -213
- package/global-skills/testing-patterns/SKILL.md +0 -430
- package/global-skills/testing-patterns/references/dependency-breaking-catalog.md +0 -586
- package/opencode-swarm-plugin-0.30.7.tgz +0 -0
- package/opencode-swarm-plugin-0.31.0.tgz +0 -0
- package/scripts/cleanup-test-memories.ts +0 -346
- package/scripts/init-skill.ts +0 -222
- package/scripts/migrate-unknown-sessions.ts +0 -349
- package/scripts/validate-skill.ts +0 -204
- package/src/agent-mail.ts +0 -1724
- package/src/anti-patterns.test.ts +0 -1167
- package/src/anti-patterns.ts +0 -448
- package/src/compaction-capture.integration.test.ts +0 -257
- package/src/compaction-hook.test.ts +0 -838
- package/src/compaction-hook.ts +0 -1204
- package/src/compaction-observability.integration.test.ts +0 -139
- package/src/compaction-observability.test.ts +0 -187
- package/src/compaction-observability.ts +0 -324
- package/src/compaction-prompt-scorers.test.ts +0 -475
- package/src/compaction-prompt-scoring.ts +0 -300
- package/src/contributor-tools.test.ts +0 -133
- package/src/contributor-tools.ts +0 -201
- package/src/dashboard.test.ts +0 -611
- package/src/dashboard.ts +0 -462
- package/src/error-enrichment.test.ts +0 -403
- package/src/error-enrichment.ts +0 -219
- package/src/eval-capture.test.ts +0 -1015
- package/src/eval-capture.ts +0 -929
- package/src/eval-gates.test.ts +0 -306
- package/src/eval-gates.ts +0 -218
- package/src/eval-history.test.ts +0 -508
- package/src/eval-history.ts +0 -214
- package/src/eval-learning.test.ts +0 -378
- package/src/eval-learning.ts +0 -360
- package/src/eval-runner.test.ts +0 -223
- package/src/eval-runner.ts +0 -402
- package/src/export-tools.test.ts +0 -476
- package/src/export-tools.ts +0 -257
- package/src/hive.integration.test.ts +0 -2241
- package/src/hive.ts +0 -1628
- package/src/index.ts +0 -940
- package/src/learning.integration.test.ts +0 -1815
- package/src/learning.ts +0 -1079
- package/src/logger.test.ts +0 -189
- package/src/logger.ts +0 -135
- package/src/mandate-promotion.test.ts +0 -473
- package/src/mandate-promotion.ts +0 -239
- package/src/mandate-storage.integration.test.ts +0 -601
- package/src/mandate-storage.test.ts +0 -578
- package/src/mandate-storage.ts +0 -794
- package/src/mandates.ts +0 -540
- package/src/memory-tools.test.ts +0 -195
- package/src/memory-tools.ts +0 -344
- package/src/memory.integration.test.ts +0 -334
- package/src/memory.test.ts +0 -158
- package/src/memory.ts +0 -527
- package/src/model-selection.test.ts +0 -188
- package/src/model-selection.ts +0 -68
- package/src/observability-tools.test.ts +0 -359
- package/src/observability-tools.ts +0 -871
- package/src/output-guardrails.test.ts +0 -438
- package/src/output-guardrails.ts +0 -381
- package/src/pattern-maturity.test.ts +0 -1160
- package/src/pattern-maturity.ts +0 -525
- package/src/planning-guardrails.test.ts +0 -491
- package/src/planning-guardrails.ts +0 -438
- package/src/plugin.ts +0 -23
- package/src/post-compaction-tracker.test.ts +0 -251
- package/src/post-compaction-tracker.ts +0 -237
- package/src/query-tools.test.ts +0 -636
- package/src/query-tools.ts +0 -324
- package/src/rate-limiter.integration.test.ts +0 -466
- package/src/rate-limiter.ts +0 -774
- package/src/replay-tools.test.ts +0 -496
- package/src/replay-tools.ts +0 -240
- package/src/repo-crawl.integration.test.ts +0 -441
- package/src/repo-crawl.ts +0 -610
- package/src/schemas/cell-events.test.ts +0 -347
- package/src/schemas/cell-events.ts +0 -807
- package/src/schemas/cell.ts +0 -257
- package/src/schemas/evaluation.ts +0 -166
- package/src/schemas/index.test.ts +0 -199
- package/src/schemas/index.ts +0 -286
- package/src/schemas/mandate.ts +0 -232
- package/src/schemas/swarm-context.ts +0 -115
- package/src/schemas/task.ts +0 -161
- package/src/schemas/worker-handoff.test.ts +0 -302
- package/src/schemas/worker-handoff.ts +0 -131
- package/src/sessions/agent-discovery.test.ts +0 -137
- package/src/sessions/agent-discovery.ts +0 -112
- package/src/sessions/index.ts +0 -15
- package/src/skills.integration.test.ts +0 -1192
- package/src/skills.test.ts +0 -643
- package/src/skills.ts +0 -1549
- package/src/storage.integration.test.ts +0 -341
- package/src/storage.ts +0 -884
- package/src/structured.integration.test.ts +0 -817
- package/src/structured.test.ts +0 -1046
- package/src/structured.ts +0 -762
- package/src/swarm-decompose.test.ts +0 -188
- package/src/swarm-decompose.ts +0 -1302
- package/src/swarm-deferred.integration.test.ts +0 -157
- package/src/swarm-deferred.test.ts +0 -38
- package/src/swarm-insights.test.ts +0 -214
- package/src/swarm-insights.ts +0 -459
- package/src/swarm-mail.integration.test.ts +0 -970
- package/src/swarm-mail.ts +0 -739
- package/src/swarm-orchestrate.integration.test.ts +0 -282
- package/src/swarm-orchestrate.test.ts +0 -548
- package/src/swarm-orchestrate.ts +0 -3084
- package/src/swarm-prompts.test.ts +0 -1270
- package/src/swarm-prompts.ts +0 -2077
- package/src/swarm-research.integration.test.ts +0 -701
- package/src/swarm-research.test.ts +0 -698
- package/src/swarm-research.ts +0 -472
- package/src/swarm-review.integration.test.ts +0 -285
- package/src/swarm-review.test.ts +0 -879
- package/src/swarm-review.ts +0 -709
- package/src/swarm-strategies.ts +0 -407
- package/src/swarm-worktree.test.ts +0 -501
- package/src/swarm-worktree.ts +0 -575
- package/src/swarm.integration.test.ts +0 -2377
- package/src/swarm.ts +0 -38
- package/src/tool-adapter.integration.test.ts +0 -1221
- package/src/tool-availability.ts +0 -461
- package/tsconfig.json +0 -28
package/src/swarm-decompose.ts
DELETED
|
@@ -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
|
-
};
|