opencode-swarm-plugin 0.43.0 → 0.44.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cass.characterization.test.ts +422 -0
- package/bin/swarm.serve.test.ts +6 -4
- package/bin/swarm.test.ts +68 -0
- package/bin/swarm.ts +81 -8
- package/dist/compaction-prompt-scoring.js +139 -0
- package/dist/contributor-tools.d.ts +42 -0
- package/dist/contributor-tools.d.ts.map +1 -0
- package/dist/eval-capture.js +12811 -0
- package/dist/hive.d.ts.map +1 -1
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7728 -62590
- package/dist/plugin.js +23833 -78695
- package/dist/sessions/agent-discovery.d.ts +59 -0
- package/dist/sessions/agent-discovery.d.ts.map +1 -0
- package/dist/sessions/index.d.ts +10 -0
- package/dist/sessions/index.d.ts.map +1 -0
- package/dist/swarm-orchestrate.d.ts.map +1 -1
- package/dist/swarm-prompts.d.ts.map +1 -1
- package/dist/swarm-review.d.ts.map +1 -1
- 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 -2255
- 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/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/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 -2426
- 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/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 -935
- 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/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-review.ts
DELETED
|
@@ -1,709 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Swarm Structured Review Module
|
|
3
|
-
*
|
|
4
|
-
* Provides coordinator-driven review of worker output before completion.
|
|
5
|
-
* The review is epic-aware - it checks if work serves the overall goal
|
|
6
|
-
* and enables downstream tasks.
|
|
7
|
-
*
|
|
8
|
-
* Key features:
|
|
9
|
-
* - Generate review prompts with full epic context
|
|
10
|
-
* - Track review attempts (max 3 before task fails)
|
|
11
|
-
* - Send structured feedback to workers
|
|
12
|
-
* - Gate completion on review approval
|
|
13
|
-
*
|
|
14
|
-
* Credit: Review patterns inspired by https://github.com/nexxeln/opencode-config
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
import { tool } from "@opencode-ai/plugin";
|
|
18
|
-
import { z } from "zod";
|
|
19
|
-
import { sendSwarmMessage, type HiveAdapter } from "swarm-mail";
|
|
20
|
-
import { getHiveAdapter } from "./hive";
|
|
21
|
-
import { captureCoordinatorEvent } from "./eval-capture.js";
|
|
22
|
-
|
|
23
|
-
// ============================================================================
|
|
24
|
-
// Types & Schemas
|
|
25
|
-
// ============================================================================
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Review issue - a specific problem found during review
|
|
29
|
-
*/
|
|
30
|
-
export interface ReviewIssue {
|
|
31
|
-
file: string;
|
|
32
|
-
line?: number;
|
|
33
|
-
issue: string;
|
|
34
|
-
suggestion?: string;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export const ReviewIssueSchema = z.object({
|
|
38
|
-
file: z.string(),
|
|
39
|
-
line: z.number().optional(),
|
|
40
|
-
issue: z.string(),
|
|
41
|
-
suggestion: z.string().optional(),
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Review result - the outcome of a review
|
|
46
|
-
*/
|
|
47
|
-
export interface ReviewResult {
|
|
48
|
-
status: "approved" | "needs_changes";
|
|
49
|
-
summary?: string;
|
|
50
|
-
issues?: ReviewIssue[];
|
|
51
|
-
remaining_attempts?: number;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export const ReviewResultSchema = z
|
|
55
|
-
.object({
|
|
56
|
-
status: z.enum(["approved", "needs_changes"]),
|
|
57
|
-
summary: z.string().optional(),
|
|
58
|
-
issues: z.array(ReviewIssueSchema).optional(),
|
|
59
|
-
remaining_attempts: z.number().optional(),
|
|
60
|
-
})
|
|
61
|
-
.refine(
|
|
62
|
-
(data) => {
|
|
63
|
-
// If status is needs_changes, issues must be provided
|
|
64
|
-
if (data.status === "needs_changes") {
|
|
65
|
-
return data.issues && data.issues.length > 0;
|
|
66
|
-
}
|
|
67
|
-
return true;
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
message: "issues array is required when status is 'needs_changes'",
|
|
71
|
-
}
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Dependency info for review context
|
|
76
|
-
*/
|
|
77
|
-
export interface DependencyInfo {
|
|
78
|
-
id: string;
|
|
79
|
-
title: string;
|
|
80
|
-
summary?: string;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Downstream task info
|
|
85
|
-
*/
|
|
86
|
-
export interface DownstreamTask {
|
|
87
|
-
id: string;
|
|
88
|
-
title: string;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Review prompt context
|
|
93
|
-
*/
|
|
94
|
-
export interface ReviewPromptContext {
|
|
95
|
-
epic_id: string;
|
|
96
|
-
epic_title: string;
|
|
97
|
-
epic_description?: string;
|
|
98
|
-
task_id: string;
|
|
99
|
-
task_title: string;
|
|
100
|
-
task_description?: string;
|
|
101
|
-
files_touched: string[];
|
|
102
|
-
diff: string;
|
|
103
|
-
completed_dependencies?: DependencyInfo[];
|
|
104
|
-
downstream_tasks?: DownstreamTask[];
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// ============================================================================
|
|
108
|
-
// Review Attempt Tracking
|
|
109
|
-
// ============================================================================
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* In-memory tracking of review attempts per task
|
|
113
|
-
* Key: task_id, Value: attempt count
|
|
114
|
-
*/
|
|
115
|
-
const reviewAttempts = new Map<string, number>();
|
|
116
|
-
|
|
117
|
-
const MAX_REVIEW_ATTEMPTS = 3;
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Get current attempt count for a task
|
|
121
|
-
*/
|
|
122
|
-
function getAttemptCount(taskId: string): number {
|
|
123
|
-
return reviewAttempts.get(taskId) || 0;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Increment attempt count for a task
|
|
128
|
-
* @returns New attempt count
|
|
129
|
-
*/
|
|
130
|
-
function incrementAttempt(taskId: string): number {
|
|
131
|
-
const current = getAttemptCount(taskId);
|
|
132
|
-
const newCount = current + 1;
|
|
133
|
-
reviewAttempts.set(taskId, newCount);
|
|
134
|
-
return newCount;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Clear attempt count (on success or task reset)
|
|
139
|
-
*/
|
|
140
|
-
function clearAttempts(taskId: string): void {
|
|
141
|
-
reviewAttempts.delete(taskId);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Get remaining attempts
|
|
146
|
-
*/
|
|
147
|
-
function getRemainingAttempts(taskId: string): number {
|
|
148
|
-
return MAX_REVIEW_ATTEMPTS - getAttemptCount(taskId);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// ============================================================================
|
|
152
|
-
// Review Prompt Generation
|
|
153
|
-
// ============================================================================
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Generate a review prompt with full epic context
|
|
157
|
-
*
|
|
158
|
-
* The prompt includes:
|
|
159
|
-
* - Epic goal (big picture)
|
|
160
|
-
* - Task requirements
|
|
161
|
-
* - Dependency context (what this builds on)
|
|
162
|
-
* - Downstream context (what depends on this)
|
|
163
|
-
* - The actual code diff
|
|
164
|
-
* - Review criteria checklist
|
|
165
|
-
*/
|
|
166
|
-
export function generateReviewPrompt(context: ReviewPromptContext): string {
|
|
167
|
-
const sections: string[] = [];
|
|
168
|
-
|
|
169
|
-
// Header
|
|
170
|
-
sections.push(`# Code Review: ${context.task_title}`);
|
|
171
|
-
sections.push("");
|
|
172
|
-
|
|
173
|
-
// Epic context (big picture)
|
|
174
|
-
sections.push("## Epic Goal");
|
|
175
|
-
sections.push(`**${context.epic_title}**`);
|
|
176
|
-
if (context.epic_description) {
|
|
177
|
-
sections.push(context.epic_description);
|
|
178
|
-
}
|
|
179
|
-
sections.push("");
|
|
180
|
-
|
|
181
|
-
// Task requirements
|
|
182
|
-
sections.push("## Task Requirements");
|
|
183
|
-
sections.push(`**${context.task_title}**`);
|
|
184
|
-
if (context.task_description) {
|
|
185
|
-
sections.push(context.task_description);
|
|
186
|
-
}
|
|
187
|
-
sections.push("");
|
|
188
|
-
|
|
189
|
-
// Dependency context
|
|
190
|
-
if (
|
|
191
|
-
context.completed_dependencies &&
|
|
192
|
-
context.completed_dependencies.length > 0
|
|
193
|
-
) {
|
|
194
|
-
sections.push("## This Task Builds On");
|
|
195
|
-
for (const dep of context.completed_dependencies) {
|
|
196
|
-
sections.push(`- **${dep.title}** (${dep.id})`);
|
|
197
|
-
if (dep.summary) {
|
|
198
|
-
sections.push(` ${dep.summary}`);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
sections.push("");
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// Downstream context
|
|
205
|
-
if (context.downstream_tasks && context.downstream_tasks.length > 0) {
|
|
206
|
-
sections.push("## Downstream Tasks (depend on this)");
|
|
207
|
-
for (const task of context.downstream_tasks) {
|
|
208
|
-
sections.push(`- **${task.title}** (${task.id})`);
|
|
209
|
-
}
|
|
210
|
-
sections.push("");
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// Files touched
|
|
214
|
-
sections.push("## Files Modified");
|
|
215
|
-
for (const file of context.files_touched) {
|
|
216
|
-
sections.push(`- \`${file}\``);
|
|
217
|
-
}
|
|
218
|
-
sections.push("");
|
|
219
|
-
|
|
220
|
-
// Code diff
|
|
221
|
-
sections.push("## Code Changes");
|
|
222
|
-
sections.push("```diff");
|
|
223
|
-
sections.push(context.diff);
|
|
224
|
-
sections.push("```");
|
|
225
|
-
sections.push("");
|
|
226
|
-
|
|
227
|
-
// Review criteria
|
|
228
|
-
sections.push("## Review Criteria");
|
|
229
|
-
sections.push("");
|
|
230
|
-
sections.push("Please evaluate the changes against these criteria:");
|
|
231
|
-
sections.push("");
|
|
232
|
-
sections.push(
|
|
233
|
-
"1. **Fulfills Requirements**: Does the code implement what the task requires?"
|
|
234
|
-
);
|
|
235
|
-
sections.push(
|
|
236
|
-
"2. **Serves Epic Goal**: Does this work contribute to the overall epic objective?"
|
|
237
|
-
);
|
|
238
|
-
sections.push(
|
|
239
|
-
"3. **Enables Downstream**: Can downstream tasks use this work as expected?"
|
|
240
|
-
);
|
|
241
|
-
sections.push("4. **Type Safety**: Are types correct and complete?");
|
|
242
|
-
sections.push("5. **No Critical Bugs**: Are there any obvious bugs or issues?");
|
|
243
|
-
sections.push(
|
|
244
|
-
"6. **Test Coverage**: Are there tests for the new code? (warning only)"
|
|
245
|
-
);
|
|
246
|
-
sections.push("");
|
|
247
|
-
|
|
248
|
-
// Response format
|
|
249
|
-
sections.push("## Response Format");
|
|
250
|
-
sections.push("");
|
|
251
|
-
sections.push("Respond with a JSON object:");
|
|
252
|
-
sections.push("```json");
|
|
253
|
-
sections.push(`{
|
|
254
|
-
"status": "approved" | "needs_changes",
|
|
255
|
-
"summary": "Brief summary of your review",
|
|
256
|
-
"issues": [
|
|
257
|
-
{
|
|
258
|
-
"file": "path/to/file.ts",
|
|
259
|
-
"line": 42,
|
|
260
|
-
"issue": "Description of the problem",
|
|
261
|
-
"suggestion": "How to fix it"
|
|
262
|
-
}
|
|
263
|
-
]
|
|
264
|
-
}`);
|
|
265
|
-
sections.push("```");
|
|
266
|
-
|
|
267
|
-
return sections.join("\n");
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// ============================================================================
|
|
271
|
-
// HiveAdapter Helper
|
|
272
|
-
// ============================================================================
|
|
273
|
-
|
|
274
|
-
/**
|
|
275
|
-
* Get or create a HiveAdapter for a project (safe wrapper)
|
|
276
|
-
*/
|
|
277
|
-
async function getHiveAdapterSafe(projectPath: string): Promise<HiveAdapter | null> {
|
|
278
|
-
try {
|
|
279
|
-
return getHiveAdapter(projectPath);
|
|
280
|
-
} catch {
|
|
281
|
-
return null;
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* Get dependencies for a cell by querying all cells and checking their parent_id
|
|
287
|
-
* Note: This is a simplified approach - a full implementation would use the dependency graph
|
|
288
|
-
*/
|
|
289
|
-
async function getCellDependencies(
|
|
290
|
-
adapter: HiveAdapter,
|
|
291
|
-
projectKey: string,
|
|
292
|
-
_cellId: string,
|
|
293
|
-
epicId: string
|
|
294
|
-
): Promise<{ completed: DependencyInfo[]; downstream: DownstreamTask[] }> {
|
|
295
|
-
const completedDependencies: DependencyInfo[] = [];
|
|
296
|
-
const downstreamTasks: DownstreamTask[] = [];
|
|
297
|
-
|
|
298
|
-
try {
|
|
299
|
-
// Get all subtasks of the epic
|
|
300
|
-
const subtasks = await adapter.queryCells(projectKey, { parent_id: epicId });
|
|
301
|
-
|
|
302
|
-
for (const subtask of subtasks) {
|
|
303
|
-
// Skip the current task
|
|
304
|
-
if (subtask.id === _cellId) continue;
|
|
305
|
-
|
|
306
|
-
// Completed tasks are potential dependencies
|
|
307
|
-
if (subtask.status === "closed") {
|
|
308
|
-
completedDependencies.push({
|
|
309
|
-
id: subtask.id,
|
|
310
|
-
title: subtask.title,
|
|
311
|
-
summary: subtask.closed_reason ?? undefined,
|
|
312
|
-
});
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// For downstream, we'd need to check the dependency graph
|
|
316
|
-
// For now, we include all non-closed tasks as potential downstream
|
|
317
|
-
if (subtask.status !== "closed") {
|
|
318
|
-
downstreamTasks.push({
|
|
319
|
-
id: subtask.id,
|
|
320
|
-
title: subtask.title,
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
} catch {
|
|
325
|
-
// Continue without dependency info
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
return { completed: completedDependencies, downstream: downstreamTasks };
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
// ============================================================================
|
|
332
|
-
// Tool Definitions
|
|
333
|
-
// ============================================================================
|
|
334
|
-
|
|
335
|
-
/**
|
|
336
|
-
* Generate a review prompt for a completed subtask
|
|
337
|
-
*
|
|
338
|
-
* Fetches epic and task details, gets the git diff, and generates
|
|
339
|
-
* a comprehensive review prompt.
|
|
340
|
-
*/
|
|
341
|
-
export const swarm_review = tool({
|
|
342
|
-
description:
|
|
343
|
-
"Generate a review prompt for a completed subtask. Includes epic context, dependencies, and diff.",
|
|
344
|
-
args: {
|
|
345
|
-
project_key: z.string().describe("Project path"),
|
|
346
|
-
epic_id: z.string().describe("Epic cell ID"),
|
|
347
|
-
task_id: z.string().describe("Subtask cell ID to review"),
|
|
348
|
-
files_touched: z
|
|
349
|
-
.array(z.string())
|
|
350
|
-
.optional()
|
|
351
|
-
.describe("Files modified (will get diff for these)"),
|
|
352
|
-
},
|
|
353
|
-
async execute(args): Promise<string> {
|
|
354
|
-
let epicTitle = args.epic_id;
|
|
355
|
-
let epicDescription: string | undefined;
|
|
356
|
-
let taskTitle = args.task_id;
|
|
357
|
-
let taskDescription: string | undefined;
|
|
358
|
-
let completedDependencies: DependencyInfo[] = [];
|
|
359
|
-
let downstreamTasks: DownstreamTask[] = [];
|
|
360
|
-
|
|
361
|
-
// Try to get cell details from HiveAdapter
|
|
362
|
-
const adapter = await getHiveAdapterSafe(args.project_key);
|
|
363
|
-
if (adapter) {
|
|
364
|
-
try {
|
|
365
|
-
// Get epic details
|
|
366
|
-
const epic = await adapter.getCell(args.project_key, args.epic_id);
|
|
367
|
-
if (epic) {
|
|
368
|
-
epicTitle = epic.title || epicTitle;
|
|
369
|
-
epicDescription = epic.description ?? undefined;
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
// Get task details
|
|
373
|
-
const task = await adapter.getCell(args.project_key, args.task_id);
|
|
374
|
-
if (task) {
|
|
375
|
-
taskTitle = task.title || taskTitle;
|
|
376
|
-
taskDescription = task.description ?? undefined;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
// Get dependencies
|
|
380
|
-
const deps = await getCellDependencies(
|
|
381
|
-
adapter,
|
|
382
|
-
args.project_key,
|
|
383
|
-
args.task_id,
|
|
384
|
-
args.epic_id
|
|
385
|
-
);
|
|
386
|
-
completedDependencies = deps.completed;
|
|
387
|
-
downstreamTasks = deps.downstream;
|
|
388
|
-
} catch {
|
|
389
|
-
// Continue with defaults if adapter fails
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
// Get git diff for files
|
|
394
|
-
let diff = "";
|
|
395
|
-
if (args.files_touched && args.files_touched.length > 0) {
|
|
396
|
-
try {
|
|
397
|
-
const diffResult = await Bun.$`git diff HEAD~1 -- ${args.files_touched}`
|
|
398
|
-
.cwd(args.project_key)
|
|
399
|
-
.quiet()
|
|
400
|
-
.nothrow();
|
|
401
|
-
|
|
402
|
-
if (diffResult.exitCode === 0) {
|
|
403
|
-
diff = diffResult.stdout.toString();
|
|
404
|
-
} else {
|
|
405
|
-
// Try staged diff
|
|
406
|
-
const stagedResult =
|
|
407
|
-
await Bun.$`git diff --cached -- ${args.files_touched}`
|
|
408
|
-
.cwd(args.project_key)
|
|
409
|
-
.quiet()
|
|
410
|
-
.nothrow();
|
|
411
|
-
diff = stagedResult.stdout.toString();
|
|
412
|
-
}
|
|
413
|
-
} catch {
|
|
414
|
-
// Git diff failed, continue without it
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
// Generate the review prompt
|
|
419
|
-
const reviewPrompt = generateReviewPrompt({
|
|
420
|
-
epic_id: args.epic_id,
|
|
421
|
-
epic_title: epicTitle,
|
|
422
|
-
epic_description: epicDescription,
|
|
423
|
-
task_id: args.task_id,
|
|
424
|
-
task_title: taskTitle,
|
|
425
|
-
task_description: taskDescription,
|
|
426
|
-
files_touched: args.files_touched || [],
|
|
427
|
-
diff: diff || "(no diff available)",
|
|
428
|
-
completed_dependencies:
|
|
429
|
-
completedDependencies.length > 0 ? completedDependencies : undefined,
|
|
430
|
-
downstream_tasks:
|
|
431
|
-
downstreamTasks.length > 0 ? downstreamTasks : undefined,
|
|
432
|
-
});
|
|
433
|
-
|
|
434
|
-
return JSON.stringify(
|
|
435
|
-
{
|
|
436
|
-
review_prompt: reviewPrompt,
|
|
437
|
-
context: {
|
|
438
|
-
epic_id: args.epic_id,
|
|
439
|
-
epic_title: epicTitle,
|
|
440
|
-
task_id: args.task_id,
|
|
441
|
-
task_title: taskTitle,
|
|
442
|
-
files_touched: args.files_touched || [],
|
|
443
|
-
completed_dependencies: completedDependencies.length,
|
|
444
|
-
downstream_tasks: downstreamTasks.length,
|
|
445
|
-
remaining_attempts: getRemainingAttempts(args.task_id),
|
|
446
|
-
},
|
|
447
|
-
},
|
|
448
|
-
null,
|
|
449
|
-
2
|
|
450
|
-
);
|
|
451
|
-
},
|
|
452
|
-
});
|
|
453
|
-
|
|
454
|
-
/**
|
|
455
|
-
* Send review feedback to a worker
|
|
456
|
-
*
|
|
457
|
-
* Tracks review attempts and fails the task after 3 rejections.
|
|
458
|
-
*/
|
|
459
|
-
export const swarm_review_feedback = tool({
|
|
460
|
-
description:
|
|
461
|
-
"Send review feedback to a worker. Tracks attempts (max 3). Fails task after 3 rejections.",
|
|
462
|
-
args: {
|
|
463
|
-
project_key: z.string().describe("Project path"),
|
|
464
|
-
task_id: z.string().describe("Subtask cell ID"),
|
|
465
|
-
worker_id: z.string().describe("Worker agent name"),
|
|
466
|
-
status: z.enum(["approved", "needs_changes"]).describe("Review status"),
|
|
467
|
-
summary: z.string().optional().describe("Review summary"),
|
|
468
|
-
issues: z
|
|
469
|
-
.string()
|
|
470
|
-
.optional()
|
|
471
|
-
.describe("JSON array of ReviewIssue objects (for needs_changes)"),
|
|
472
|
-
},
|
|
473
|
-
async execute(args, _ctx): Promise<string> {
|
|
474
|
-
// Parse issues if provided
|
|
475
|
-
let parsedIssues: ReviewIssue[] = [];
|
|
476
|
-
if (args.issues) {
|
|
477
|
-
try {
|
|
478
|
-
parsedIssues = JSON.parse(args.issues);
|
|
479
|
-
} catch {
|
|
480
|
-
return JSON.stringify(
|
|
481
|
-
{
|
|
482
|
-
success: false,
|
|
483
|
-
error: "Failed to parse issues JSON",
|
|
484
|
-
},
|
|
485
|
-
null,
|
|
486
|
-
2
|
|
487
|
-
);
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
// Validate: needs_changes requires issues
|
|
492
|
-
if (args.status === "needs_changes" && parsedIssues.length === 0) {
|
|
493
|
-
return JSON.stringify(
|
|
494
|
-
{
|
|
495
|
-
success: false,
|
|
496
|
-
error: "needs_changes status requires at least one issue",
|
|
497
|
-
},
|
|
498
|
-
null,
|
|
499
|
-
2
|
|
500
|
-
);
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
// Extract epic ID for thread
|
|
504
|
-
const epicId = args.task_id.includes(".")
|
|
505
|
-
? args.task_id.split(".")[0]
|
|
506
|
-
: args.task_id;
|
|
507
|
-
|
|
508
|
-
if (args.status === "approved") {
|
|
509
|
-
// Mark as approved and clear attempts
|
|
510
|
-
markReviewApproved(args.task_id);
|
|
511
|
-
|
|
512
|
-
// Capture review approval decision
|
|
513
|
-
try {
|
|
514
|
-
captureCoordinatorEvent({
|
|
515
|
-
session_id: _ctx.sessionID || "unknown",
|
|
516
|
-
epic_id: epicId,
|
|
517
|
-
timestamp: new Date().toISOString(),
|
|
518
|
-
event_type: "DECISION",
|
|
519
|
-
decision_type: "review_completed",
|
|
520
|
-
payload: {
|
|
521
|
-
task_id: args.task_id,
|
|
522
|
-
status: "approved",
|
|
523
|
-
retry_count: 0,
|
|
524
|
-
},
|
|
525
|
-
});
|
|
526
|
-
} catch (error) {
|
|
527
|
-
// Non-fatal - don't block approval if capture fails
|
|
528
|
-
console.warn("[swarm_review_feedback] Failed to capture review_completed:", error);
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
// Send approval message
|
|
532
|
-
await sendSwarmMessage({
|
|
533
|
-
projectPath: args.project_key,
|
|
534
|
-
fromAgent: "coordinator",
|
|
535
|
-
toAgents: [args.worker_id],
|
|
536
|
-
subject: `APPROVED: ${args.task_id}`,
|
|
537
|
-
body: `## Review Approved ✓
|
|
538
|
-
|
|
539
|
-
${args.summary || "Your work has been approved."}
|
|
540
|
-
|
|
541
|
-
You may now complete the task with \`swarm_complete\`.`,
|
|
542
|
-
threadId: epicId,
|
|
543
|
-
importance: "normal",
|
|
544
|
-
});
|
|
545
|
-
|
|
546
|
-
return JSON.stringify(
|
|
547
|
-
{
|
|
548
|
-
success: true,
|
|
549
|
-
status: "approved",
|
|
550
|
-
task_id: args.task_id,
|
|
551
|
-
message: "Review approved. Worker can now complete the task.",
|
|
552
|
-
},
|
|
553
|
-
null,
|
|
554
|
-
2
|
|
555
|
-
);
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
// Handle needs_changes
|
|
559
|
-
const attemptNumber = incrementAttempt(args.task_id);
|
|
560
|
-
const remaining = MAX_REVIEW_ATTEMPTS - attemptNumber;
|
|
561
|
-
|
|
562
|
-
// Capture review rejection decision
|
|
563
|
-
try {
|
|
564
|
-
captureCoordinatorEvent({
|
|
565
|
-
session_id: _ctx.sessionID || "unknown",
|
|
566
|
-
epic_id: epicId,
|
|
567
|
-
timestamp: new Date().toISOString(),
|
|
568
|
-
event_type: "DECISION",
|
|
569
|
-
decision_type: "review_completed",
|
|
570
|
-
payload: {
|
|
571
|
-
task_id: args.task_id,
|
|
572
|
-
status: "needs_changes",
|
|
573
|
-
retry_count: attemptNumber,
|
|
574
|
-
remaining_attempts: remaining,
|
|
575
|
-
issues_count: parsedIssues.length,
|
|
576
|
-
},
|
|
577
|
-
});
|
|
578
|
-
} catch (error) {
|
|
579
|
-
// Non-fatal - don't block feedback if capture fails
|
|
580
|
-
console.warn("[swarm_review_feedback] Failed to capture review_completed:", error);
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
// Check if task should fail
|
|
584
|
-
if (remaining <= 0) {
|
|
585
|
-
// Mark task as blocked using HiveAdapter
|
|
586
|
-
const adapter = await getHiveAdapterSafe(args.project_key);
|
|
587
|
-
if (adapter) {
|
|
588
|
-
try {
|
|
589
|
-
await adapter.changeCellStatus(args.project_key, args.task_id, "blocked");
|
|
590
|
-
} catch {
|
|
591
|
-
// Continue even if status update fails
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
// NO sendSwarmMessage - worker is dead, can't read it
|
|
596
|
-
// Coordinator handles retry or escalation
|
|
597
|
-
|
|
598
|
-
return JSON.stringify(
|
|
599
|
-
{
|
|
600
|
-
success: true,
|
|
601
|
-
status: "needs_changes",
|
|
602
|
-
task_failed: true,
|
|
603
|
-
task_id: args.task_id,
|
|
604
|
-
attempt: attemptNumber,
|
|
605
|
-
remaining_attempts: 0,
|
|
606
|
-
message: `Task failed after ${MAX_REVIEW_ATTEMPTS} review attempts`,
|
|
607
|
-
},
|
|
608
|
-
null,
|
|
609
|
-
2
|
|
610
|
-
);
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
// NO sendSwarmMessage for needs_changes - worker is dead
|
|
614
|
-
// Instead, return retry_context for coordinator to use with swarm_spawn_retry
|
|
615
|
-
|
|
616
|
-
return JSON.stringify(
|
|
617
|
-
{
|
|
618
|
-
success: true,
|
|
619
|
-
status: "needs_changes",
|
|
620
|
-
task_id: args.task_id,
|
|
621
|
-
attempt: attemptNumber,
|
|
622
|
-
remaining_attempts: remaining,
|
|
623
|
-
issues: parsedIssues,
|
|
624
|
-
message: `Review feedback ready. ${remaining} attempt(s) remaining.`,
|
|
625
|
-
retry_context: {
|
|
626
|
-
task_id: args.task_id,
|
|
627
|
-
attempt: attemptNumber,
|
|
628
|
-
max_attempts: MAX_REVIEW_ATTEMPTS,
|
|
629
|
-
issues: parsedIssues,
|
|
630
|
-
next_action: "Use swarm_spawn_retry to spawn new worker with these issues",
|
|
631
|
-
},
|
|
632
|
-
},
|
|
633
|
-
null,
|
|
634
|
-
2
|
|
635
|
-
);
|
|
636
|
-
},
|
|
637
|
-
});
|
|
638
|
-
|
|
639
|
-
// ============================================================================
|
|
640
|
-
// Review Gate for swarm_complete
|
|
641
|
-
// ============================================================================
|
|
642
|
-
|
|
643
|
-
/**
|
|
644
|
-
* Review status for a task
|
|
645
|
-
*/
|
|
646
|
-
interface TaskReviewStatus {
|
|
647
|
-
reviewed: boolean;
|
|
648
|
-
approved: boolean;
|
|
649
|
-
attempt_count: number;
|
|
650
|
-
remaining_attempts: number;
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
/**
|
|
654
|
-
* In-memory tracking of review status per task
|
|
655
|
-
*/
|
|
656
|
-
const reviewStatus = new Map<string, { approved: boolean; timestamp: number }>();
|
|
657
|
-
|
|
658
|
-
/**
|
|
659
|
-
* Mark a task as reviewed and approved
|
|
660
|
-
*/
|
|
661
|
-
export function markReviewApproved(taskId: string): void {
|
|
662
|
-
reviewStatus.set(taskId, { approved: true, timestamp: Date.now() });
|
|
663
|
-
clearAttempts(taskId);
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
/**
|
|
667
|
-
* Check if a task has been approved
|
|
668
|
-
*/
|
|
669
|
-
export function isReviewApproved(taskId: string): boolean {
|
|
670
|
-
const status = reviewStatus.get(taskId);
|
|
671
|
-
return status?.approved ?? false;
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
/**
|
|
675
|
-
* Get review status for a task
|
|
676
|
-
*/
|
|
677
|
-
export function getReviewStatus(taskId: string): TaskReviewStatus {
|
|
678
|
-
const status = reviewStatus.get(taskId);
|
|
679
|
-
return {
|
|
680
|
-
reviewed: status !== undefined,
|
|
681
|
-
approved: status?.approved ?? false,
|
|
682
|
-
attempt_count: getAttemptCount(taskId),
|
|
683
|
-
remaining_attempts: getRemainingAttempts(taskId),
|
|
684
|
-
};
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
/**
|
|
688
|
-
* Clear review status (for testing or reset)
|
|
689
|
-
*/
|
|
690
|
-
export function clearReviewStatus(taskId: string): void {
|
|
691
|
-
reviewStatus.delete(taskId);
|
|
692
|
-
clearAttempts(taskId);
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
/**
|
|
696
|
-
* Mark a task as reviewed but not approved (for testing)
|
|
697
|
-
*/
|
|
698
|
-
export function markReviewRejected(taskId: string): void {
|
|
699
|
-
reviewStatus.set(taskId, { approved: false, timestamp: Date.now() });
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
// ============================================================================
|
|
703
|
-
// Exports
|
|
704
|
-
// ============================================================================
|
|
705
|
-
|
|
706
|
-
export const reviewTools = {
|
|
707
|
-
swarm_review,
|
|
708
|
-
swarm_review_feedback,
|
|
709
|
-
};
|