opencode-swarm-plugin 0.44.0 → 0.44.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/swarm.serve.test.ts +6 -4
- package/bin/swarm.ts +16 -10
- 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/index.js +7644 -62599
- package/dist/plugin.js +23766 -78721
- 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 -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
|
@@ -1,1270 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for swarm-prompts.ts
|
|
3
|
-
*
|
|
4
|
-
* Validates that prompt templates contain required sections and emphasis
|
|
5
|
-
* for memory usage, coordination, and TDD workflow.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { describe, expect, test } from "bun:test";
|
|
9
|
-
import {
|
|
10
|
-
formatSubtaskPromptV2,
|
|
11
|
-
formatResearcherPrompt,
|
|
12
|
-
formatCoordinatorPrompt,
|
|
13
|
-
SUBTASK_PROMPT_V2,
|
|
14
|
-
RESEARCHER_PROMPT,
|
|
15
|
-
COORDINATOR_PROMPT,
|
|
16
|
-
} from "./swarm-prompts";
|
|
17
|
-
|
|
18
|
-
describe("SUBTASK_PROMPT_V2", () => {
|
|
19
|
-
describe("memory query emphasis", () => {
|
|
20
|
-
test("Step 2 is semantic-memory_find and marked MANDATORY", () => {
|
|
21
|
-
expect(SUBTASK_PROMPT_V2).toContain("### Step 2:");
|
|
22
|
-
expect(SUBTASK_PROMPT_V2).toContain("semantic-memory_find");
|
|
23
|
-
// Must have MANDATORY in the step header
|
|
24
|
-
expect(SUBTASK_PROMPT_V2).toMatch(/### Step 2:.*MANDATORY/i);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
test("memory query step has visual emphasis (emoji or caps)", () => {
|
|
28
|
-
// Should have emoji or CRITICAL/ALWAYS in caps
|
|
29
|
-
const step2Match = SUBTASK_PROMPT_V2.match(/### Step 2:[\s\S]*?### Step 3:/);
|
|
30
|
-
expect(step2Match).not.toBeNull();
|
|
31
|
-
if (!step2Match) return;
|
|
32
|
-
const step2Content = step2Match[0];
|
|
33
|
-
|
|
34
|
-
// Must have at least one of: emoji, CRITICAL, ALWAYS, MANDATORY
|
|
35
|
-
const hasEmphasis =
|
|
36
|
-
/🧠|⚠️|CRITICAL|ALWAYS|MANDATORY/.test(step2Content);
|
|
37
|
-
expect(hasEmphasis).toBe(true);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test("memory query step includes query examples by task type", () => {
|
|
41
|
-
const step2Match = SUBTASK_PROMPT_V2.match(/### Step 2:[\s\S]*?### Step 3:/);
|
|
42
|
-
expect(step2Match).not.toBeNull();
|
|
43
|
-
if (!step2Match) return;
|
|
44
|
-
const step2Content = step2Match[0];
|
|
45
|
-
|
|
46
|
-
// Should have examples for different task types
|
|
47
|
-
expect(step2Content).toContain("Bug fix");
|
|
48
|
-
expect(step2Content).toContain("New feature");
|
|
49
|
-
expect(step2Content).toContain("Refactor");
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
test("memory query step explains WHY it's mandatory", () => {
|
|
53
|
-
const step2Match = SUBTASK_PROMPT_V2.match(/### Step 2:[\s\S]*?### Step 3:/);
|
|
54
|
-
expect(step2Match).not.toBeNull();
|
|
55
|
-
if (!step2Match) return;
|
|
56
|
-
const step2Content = step2Match[0];
|
|
57
|
-
|
|
58
|
-
// Should explain consequences of skipping
|
|
59
|
-
expect(step2Content).toMatch(/skip|waste|repeat|already.solved/i);
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
describe("memory storage emphasis", () => {
|
|
64
|
-
test("has a dedicated section for storing learnings", () => {
|
|
65
|
-
// Should have a prominent section about storing memories
|
|
66
|
-
expect(SUBTASK_PROMPT_V2).toMatch(/##.*STORE.*LEARNING|### Step.*Store.*Learning/i);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
test("storage section lists triggers for when to store", () => {
|
|
70
|
-
// Should mention triggers: bugs, gotchas, patterns, failed approaches
|
|
71
|
-
expect(SUBTASK_PROMPT_V2).toContain("bug");
|
|
72
|
-
expect(SUBTASK_PROMPT_V2).toMatch(/gotcha|quirk|workaround/i);
|
|
73
|
-
expect(SUBTASK_PROMPT_V2).toMatch(/pattern|domain/i);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
test("storage section emphasizes WHY not just WHAT", () => {
|
|
77
|
-
expect(SUBTASK_PROMPT_V2).toMatch(/WHY.*not.*WHAT|why.*matters/i);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
test("storage section warns against generic knowledge", () => {
|
|
81
|
-
expect(SUBTASK_PROMPT_V2).toMatch(/don't store.*generic|generic knowledge/i);
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
describe("checklist order", () => {
|
|
86
|
-
test("Step 1 is swarmmail_init", () => {
|
|
87
|
-
expect(SUBTASK_PROMPT_V2).toMatch(/### Step 1:[\s\S]*?swarmmail_init/);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
test("Step 2 is semantic-memory_find (before skills)", () => {
|
|
91
|
-
const step2Pos = SUBTASK_PROMPT_V2.indexOf("### Step 2:");
|
|
92
|
-
const step3Pos = SUBTASK_PROMPT_V2.indexOf("### Step 3:");
|
|
93
|
-
const memoryFindPos = SUBTASK_PROMPT_V2.indexOf("semantic-memory_find");
|
|
94
|
-
const skillsPos = SUBTASK_PROMPT_V2.indexOf("skills_list");
|
|
95
|
-
|
|
96
|
-
// Memory find should be in Step 2, before skills in Step 3
|
|
97
|
-
expect(memoryFindPos).toBeGreaterThan(step2Pos);
|
|
98
|
-
expect(memoryFindPos).toBeLessThan(step3Pos);
|
|
99
|
-
expect(skillsPos).toBeGreaterThan(step3Pos);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
test("semantic-memory_store comes before swarm_complete", () => {
|
|
103
|
-
const storePos = SUBTASK_PROMPT_V2.indexOf("semantic-memory_store");
|
|
104
|
-
const completePos = SUBTASK_PROMPT_V2.lastIndexOf("swarm_complete");
|
|
105
|
-
|
|
106
|
-
expect(storePos).toBeGreaterThan(0);
|
|
107
|
-
expect(storePos).toBeLessThan(completePos);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
test("final step is swarm_complete (not hive_close)", () => {
|
|
111
|
-
// Find the last "### Step N:" pattern
|
|
112
|
-
const stepMatches = [...SUBTASK_PROMPT_V2.matchAll(/### Step (\d+):/g)];
|
|
113
|
-
expect(stepMatches.length).toBeGreaterThan(0);
|
|
114
|
-
|
|
115
|
-
const lastStepNum = Math.max(...stepMatches.map(m => parseInt(m[1])));
|
|
116
|
-
const lastStepMatch = SUBTASK_PROMPT_V2.match(
|
|
117
|
-
new RegExp(`### Step ${lastStepNum}:[\\s\\S]*?(?=## \\[|$)`)
|
|
118
|
-
);
|
|
119
|
-
expect(lastStepMatch).not.toBeNull();
|
|
120
|
-
if (!lastStepMatch) return;
|
|
121
|
-
|
|
122
|
-
const lastStepContent = lastStepMatch[0];
|
|
123
|
-
expect(lastStepContent).toContain("swarm_complete");
|
|
124
|
-
expect(lastStepContent).toMatch(/NOT.*hive_close|DO NOT.*hive_close/i);
|
|
125
|
-
});
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
describe("critical requirements section", () => {
|
|
129
|
-
test("lists memory query as non-negotiable", () => {
|
|
130
|
-
const criticalSection = SUBTASK_PROMPT_V2.match(/\[CRITICAL REQUIREMENTS\][\s\S]*?Begin now/);
|
|
131
|
-
expect(criticalSection).not.toBeNull();
|
|
132
|
-
if (!criticalSection) return;
|
|
133
|
-
|
|
134
|
-
expect(criticalSection[0]).toMatch(/semantic-memory_find|memory.*MUST|Step 2.*MUST/i);
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
test("lists consequences of skipping memory steps", () => {
|
|
138
|
-
const criticalSection = SUBTASK_PROMPT_V2.match(/\[CRITICAL REQUIREMENTS\][\s\S]*?Begin now/);
|
|
139
|
-
expect(criticalSection).not.toBeNull();
|
|
140
|
-
if (!criticalSection) return;
|
|
141
|
-
|
|
142
|
-
// Should mention consequences for skipping memory
|
|
143
|
-
expect(criticalSection[0]).toMatch(/repeat|waste|already.solved|mistakes/i);
|
|
144
|
-
});
|
|
145
|
-
});
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
describe("formatSubtaskPromptV2", () => {
|
|
149
|
-
test("substitutes all placeholders correctly", async () => {
|
|
150
|
-
const result = await formatSubtaskPromptV2({
|
|
151
|
-
bead_id: "test-project-abc123-bead456",
|
|
152
|
-
epic_id: "test-project-abc123-epic789",
|
|
153
|
-
subtask_title: "Test Subtask",
|
|
154
|
-
subtask_description: "Do the test thing",
|
|
155
|
-
files: ["src/test.ts", "src/test.test.ts"],
|
|
156
|
-
shared_context: "This is shared context",
|
|
157
|
-
project_path: "/path/to/project",
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
expect(result).toContain("test-project-abc123-bead456");
|
|
161
|
-
expect(result).toContain("test-project-abc123-epic789");
|
|
162
|
-
expect(result).toContain("Test Subtask");
|
|
163
|
-
expect(result).toContain("Do the test thing");
|
|
164
|
-
expect(result).toContain("src/test.ts");
|
|
165
|
-
expect(result).toContain("/path/to/project");
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
test("includes memory query step with MANDATORY emphasis", async () => {
|
|
169
|
-
const result = await formatSubtaskPromptV2({
|
|
170
|
-
bead_id: "test-project-abc123-def456",
|
|
171
|
-
epic_id: "test-project-abc123-ghi789",
|
|
172
|
-
subtask_title: "Test",
|
|
173
|
-
subtask_description: "",
|
|
174
|
-
files: [],
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
expect(result).toMatch(/Step 2:.*MANDATORY/i);
|
|
178
|
-
expect(result).toContain("semantic-memory_find");
|
|
179
|
-
});
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
describe("swarm_spawn_subtask tool", () => {
|
|
183
|
-
test("returns post_completion_instructions field in JSON response", async () => {
|
|
184
|
-
const { swarm_spawn_subtask } = await import("./swarm-prompts");
|
|
185
|
-
|
|
186
|
-
const result = await swarm_spawn_subtask.execute({
|
|
187
|
-
bead_id: "test-project-abc123-task1",
|
|
188
|
-
epic_id: "test-project-abc123-epic1",
|
|
189
|
-
subtask_title: "Implement feature X",
|
|
190
|
-
subtask_description: "Add feature X to the system",
|
|
191
|
-
files: ["src/feature.ts", "src/feature.test.ts"],
|
|
192
|
-
shared_context: "Epic context here",
|
|
193
|
-
project_path: "/Users/joel/Code/project",
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
const parsed = JSON.parse(result);
|
|
197
|
-
expect(parsed).toHaveProperty("post_completion_instructions");
|
|
198
|
-
expect(typeof parsed.post_completion_instructions).toBe("string");
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
test("post_completion_instructions contains mandatory review steps", async () => {
|
|
202
|
-
const { swarm_spawn_subtask } = await import("./swarm-prompts");
|
|
203
|
-
|
|
204
|
-
const result = await swarm_spawn_subtask.execute({
|
|
205
|
-
bead_id: "test-project-abc123-task1",
|
|
206
|
-
epic_id: "test-project-abc123-epic1",
|
|
207
|
-
subtask_title: "Implement feature X",
|
|
208
|
-
files: ["src/feature.ts"],
|
|
209
|
-
project_path: "/Users/joel/Code/project",
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
const parsed = JSON.parse(result);
|
|
213
|
-
const instructions = parsed.post_completion_instructions;
|
|
214
|
-
|
|
215
|
-
// Should contain all 5 steps
|
|
216
|
-
expect(instructions).toContain("Step 1: Check Swarm Mail");
|
|
217
|
-
expect(instructions).toContain("swarmmail_inbox()");
|
|
218
|
-
expect(instructions).toContain("Step 2: Review the Work");
|
|
219
|
-
expect(instructions).toContain("swarm_review");
|
|
220
|
-
expect(instructions).toContain("Step 3: Evaluate Against Criteria");
|
|
221
|
-
expect(instructions).toContain("Step 4: Send Feedback");
|
|
222
|
-
expect(instructions).toContain("swarm_review_feedback");
|
|
223
|
-
expect(instructions).toContain("Step 5: Take Action Based on Review");
|
|
224
|
-
expect(instructions).toContain("swarm_spawn_retry"); // Should include retry flow
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
test("post_completion_instructions substitutes placeholders", async () => {
|
|
228
|
-
const { swarm_spawn_subtask } = await import("./swarm-prompts");
|
|
229
|
-
|
|
230
|
-
const result = await swarm_spawn_subtask.execute({
|
|
231
|
-
bead_id: "test-project-abc123-task1",
|
|
232
|
-
epic_id: "test-project-abc123-epic1",
|
|
233
|
-
subtask_title: "Implement feature X",
|
|
234
|
-
files: ["src/feature.ts", "src/feature.test.ts"],
|
|
235
|
-
project_path: "/Users/joel/Code/project",
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
const parsed = JSON.parse(result);
|
|
239
|
-
const instructions = parsed.post_completion_instructions;
|
|
240
|
-
|
|
241
|
-
// Placeholders should be replaced
|
|
242
|
-
expect(instructions).toContain("/Users/joel/Code/project");
|
|
243
|
-
expect(instructions).toContain("test-project-abc123-epic1");
|
|
244
|
-
expect(instructions).toContain("test-project-abc123-task1");
|
|
245
|
-
expect(instructions).toContain('"src/feature.ts"');
|
|
246
|
-
expect(instructions).toContain('"src/feature.test.ts"');
|
|
247
|
-
|
|
248
|
-
// Placeholders should NOT remain
|
|
249
|
-
expect(instructions).not.toContain("{project_key}");
|
|
250
|
-
expect(instructions).not.toContain("{epic_id}");
|
|
251
|
-
expect(instructions).not.toContain("{task_id}");
|
|
252
|
-
expect(instructions).not.toContain("{files_touched}");
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
test("post_completion_instructions emphasizes mandatory nature", async () => {
|
|
256
|
-
const { swarm_spawn_subtask } = await import("./swarm-prompts");
|
|
257
|
-
|
|
258
|
-
const result = await swarm_spawn_subtask.execute({
|
|
259
|
-
bead_id: "test-project-abc123-task1",
|
|
260
|
-
epic_id: "test-project-abc123-epic1",
|
|
261
|
-
subtask_title: "Implement feature X",
|
|
262
|
-
files: ["src/feature.ts"],
|
|
263
|
-
project_path: "/Users/joel/Code/project",
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
const parsed = JSON.parse(result);
|
|
267
|
-
const instructions = parsed.post_completion_instructions;
|
|
268
|
-
|
|
269
|
-
// Should have strong language
|
|
270
|
-
expect(instructions).toMatch(/⚠️|MANDATORY|NON-NEGOTIABLE|DO NOT skip/i);
|
|
271
|
-
expect(instructions).toContain("DO THIS IMMEDIATELY");
|
|
272
|
-
});
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
describe("RESEARCHER_PROMPT", () => {
|
|
276
|
-
describe("required sections", () => {
|
|
277
|
-
test("includes IDENTITY section with research_id and epic_id", () => {
|
|
278
|
-
expect(RESEARCHER_PROMPT).toContain("## [IDENTITY]");
|
|
279
|
-
expect(RESEARCHER_PROMPT).toContain("{research_id}");
|
|
280
|
-
expect(RESEARCHER_PROMPT).toContain("{epic_id}");
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
test("includes MISSION section explaining the role", () => {
|
|
284
|
-
expect(RESEARCHER_PROMPT).toContain("## [MISSION]");
|
|
285
|
-
expect(RESEARCHER_PROMPT).toMatch(/gather.*documentation/i);
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
test("includes WORKFLOW section with numbered steps", () => {
|
|
289
|
-
expect(RESEARCHER_PROMPT).toContain("## [WORKFLOW]");
|
|
290
|
-
expect(RESEARCHER_PROMPT).toContain("### Step 1:");
|
|
291
|
-
expect(RESEARCHER_PROMPT).toContain("### Step 2:");
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
test("includes CRITICAL REQUIREMENTS section", () => {
|
|
295
|
-
expect(RESEARCHER_PROMPT).toContain("## [CRITICAL REQUIREMENTS]");
|
|
296
|
-
expect(RESEARCHER_PROMPT).toMatch(/NON-NEGOTIABLE/i);
|
|
297
|
-
});
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
describe("workflow steps", () => {
|
|
301
|
-
test("Step 1 is swarmmail_init (MANDATORY FIRST)", () => {
|
|
302
|
-
expect(RESEARCHER_PROMPT).toMatch(/### Step 1:.*Initialize/i);
|
|
303
|
-
expect(RESEARCHER_PROMPT).toContain("swarmmail_init");
|
|
304
|
-
expect(RESEARCHER_PROMPT).toContain("project_path");
|
|
305
|
-
});
|
|
306
|
-
|
|
307
|
-
test("Step 2 is discovering available documentation tools", () => {
|
|
308
|
-
const step2Match = RESEARCHER_PROMPT.match(/### Step 2:[\s\S]*?### Step 3:/);
|
|
309
|
-
expect(step2Match).not.toBeNull();
|
|
310
|
-
if (!step2Match) return;
|
|
311
|
-
|
|
312
|
-
const step2Content = step2Match[0];
|
|
313
|
-
expect(step2Content).toMatch(/discover.*tools/i);
|
|
314
|
-
expect(step2Content).toContain("nextjs_docs");
|
|
315
|
-
expect(step2Content).toContain("context7");
|
|
316
|
-
expect(step2Content).toContain("fetch");
|
|
317
|
-
expect(step2Content).toContain("pdf-brain");
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
test("Step 3 is reading installed versions", () => {
|
|
321
|
-
const step3Match = RESEARCHER_PROMPT.match(/### Step 3:[\s\S]*?### Step 4:/);
|
|
322
|
-
expect(step3Match).not.toBeNull();
|
|
323
|
-
if (!step3Match) return;
|
|
324
|
-
|
|
325
|
-
const step3Content = step3Match[0];
|
|
326
|
-
expect(step3Content).toMatch(/read.*installed.*version/i);
|
|
327
|
-
expect(step3Content).toContain("package.json");
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
test("Step 4 is fetching documentation", () => {
|
|
331
|
-
const step4Match = RESEARCHER_PROMPT.match(/### Step 4:[\s\S]*?### Step 5:/);
|
|
332
|
-
expect(step4Match).not.toBeNull();
|
|
333
|
-
if (!step4Match) return;
|
|
334
|
-
|
|
335
|
-
const step4Content = step4Match[0];
|
|
336
|
-
expect(step4Content).toMatch(/fetch.*documentation/i);
|
|
337
|
-
expect(step4Content).toContain("INSTALLED version");
|
|
338
|
-
});
|
|
339
|
-
|
|
340
|
-
test("Step 5 is storing detailed findings in semantic-memory", () => {
|
|
341
|
-
const step5Match = RESEARCHER_PROMPT.match(/### Step 5:[\s\S]*?### Step 6:/);
|
|
342
|
-
expect(step5Match).not.toBeNull();
|
|
343
|
-
if (!step5Match) return;
|
|
344
|
-
|
|
345
|
-
const step5Content = step5Match[0];
|
|
346
|
-
expect(step5Content).toContain("semantic-memory_store");
|
|
347
|
-
expect(step5Content).toMatch(/store.*individually/i);
|
|
348
|
-
});
|
|
349
|
-
|
|
350
|
-
test("Step 6 is broadcasting summary to coordinator", () => {
|
|
351
|
-
const step6Match = RESEARCHER_PROMPT.match(/### Step 6:[\s\S]*?### Step 7:/);
|
|
352
|
-
expect(step6Match).not.toBeNull();
|
|
353
|
-
if (!step6Match) return;
|
|
354
|
-
|
|
355
|
-
const step6Content = step6Match[0];
|
|
356
|
-
expect(step6Content).toContain("swarmmail_send");
|
|
357
|
-
expect(step6Content).toContain("coordinator");
|
|
358
|
-
});
|
|
359
|
-
|
|
360
|
-
test("Step 7 is returning structured JSON output", () => {
|
|
361
|
-
const step7Match = RESEARCHER_PROMPT.match(/### Step 7:[\s\S]*?(?=## \[|$)/);
|
|
362
|
-
expect(step7Match).not.toBeNull();
|
|
363
|
-
if (!step7Match) return;
|
|
364
|
-
|
|
365
|
-
const step7Content = step7Match[0];
|
|
366
|
-
expect(step7Content).toContain("JSON");
|
|
367
|
-
expect(step7Content).toContain("technologies");
|
|
368
|
-
expect(step7Content).toContain("summary");
|
|
369
|
-
});
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
describe("coordinator-provided tech stack", () => {
|
|
373
|
-
test("emphasizes that coordinator provides the tech list", () => {
|
|
374
|
-
expect(RESEARCHER_PROMPT).toMatch(/COORDINATOR PROVIDED.*TECHNOLOGIES/i);
|
|
375
|
-
expect(RESEARCHER_PROMPT).toContain("{tech_stack}");
|
|
376
|
-
});
|
|
377
|
-
|
|
378
|
-
test("clarifies researcher does NOT discover what to research", () => {
|
|
379
|
-
expect(RESEARCHER_PROMPT).toMatch(/NOT discover what to research/i);
|
|
380
|
-
expect(RESEARCHER_PROMPT).toMatch(/DO discover.*TOOLS/i);
|
|
381
|
-
});
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
describe("upgrade comparison mode", () => {
|
|
385
|
-
test("includes placeholder for check_upgrades mode", () => {
|
|
386
|
-
expect(RESEARCHER_PROMPT).toContain("{check_upgrades}");
|
|
387
|
-
});
|
|
388
|
-
|
|
389
|
-
test("mentions comparing installed vs latest when in upgrade mode", () => {
|
|
390
|
-
expect(RESEARCHER_PROMPT).toMatch(/check-upgrades/i);
|
|
391
|
-
expect(RESEARCHER_PROMPT).toMatch(/compare|latest.*version/i);
|
|
392
|
-
});
|
|
393
|
-
});
|
|
394
|
-
|
|
395
|
-
describe("output requirements", () => {
|
|
396
|
-
test("specifies TWO output destinations: semantic-memory and return JSON", () => {
|
|
397
|
-
expect(RESEARCHER_PROMPT).toMatch(/TWO places/i);
|
|
398
|
-
expect(RESEARCHER_PROMPT).toContain("semantic-memory");
|
|
399
|
-
expect(RESEARCHER_PROMPT).toContain("Return JSON");
|
|
400
|
-
});
|
|
401
|
-
|
|
402
|
-
test("explains semantic-memory is for detailed findings", () => {
|
|
403
|
-
expect(RESEARCHER_PROMPT).toMatch(/semantic-memory.*detailed/i);
|
|
404
|
-
});
|
|
405
|
-
|
|
406
|
-
test("explains return JSON is for condensed summary", () => {
|
|
407
|
-
expect(RESEARCHER_PROMPT).toMatch(/return.*condensed.*summary/i);
|
|
408
|
-
});
|
|
409
|
-
});
|
|
410
|
-
});
|
|
411
|
-
|
|
412
|
-
describe("formatResearcherPrompt", () => {
|
|
413
|
-
test("substitutes research_id placeholder", () => {
|
|
414
|
-
const result = formatResearcherPrompt({
|
|
415
|
-
research_id: "research-abc123",
|
|
416
|
-
epic_id: "epic-xyz789",
|
|
417
|
-
tech_stack: ["Next.js", "React"],
|
|
418
|
-
project_path: "/path/to/project",
|
|
419
|
-
check_upgrades: false,
|
|
420
|
-
});
|
|
421
|
-
|
|
422
|
-
expect(result).toContain("research-abc123");
|
|
423
|
-
expect(result).not.toContain("{research_id}");
|
|
424
|
-
});
|
|
425
|
-
|
|
426
|
-
test("substitutes epic_id placeholder", () => {
|
|
427
|
-
const result = formatResearcherPrompt({
|
|
428
|
-
research_id: "research-abc123",
|
|
429
|
-
epic_id: "epic-xyz789",
|
|
430
|
-
tech_stack: ["Next.js"],
|
|
431
|
-
project_path: "/path/to/project",
|
|
432
|
-
check_upgrades: false,
|
|
433
|
-
});
|
|
434
|
-
|
|
435
|
-
expect(result).toContain("epic-xyz789");
|
|
436
|
-
expect(result).not.toContain("{epic_id}");
|
|
437
|
-
});
|
|
438
|
-
|
|
439
|
-
test("formats tech_stack as bulleted list", () => {
|
|
440
|
-
const result = formatResearcherPrompt({
|
|
441
|
-
research_id: "research-abc123",
|
|
442
|
-
epic_id: "epic-xyz789",
|
|
443
|
-
tech_stack: ["Next.js", "React", "TypeScript"],
|
|
444
|
-
project_path: "/path/to/project",
|
|
445
|
-
check_upgrades: false,
|
|
446
|
-
});
|
|
447
|
-
|
|
448
|
-
expect(result).toContain("- Next.js");
|
|
449
|
-
expect(result).toContain("- React");
|
|
450
|
-
expect(result).toContain("- TypeScript");
|
|
451
|
-
});
|
|
452
|
-
|
|
453
|
-
test("substitutes project_path placeholder", () => {
|
|
454
|
-
const result = formatResearcherPrompt({
|
|
455
|
-
research_id: "research-abc123",
|
|
456
|
-
epic_id: "epic-xyz789",
|
|
457
|
-
tech_stack: ["Next.js"],
|
|
458
|
-
project_path: "/Users/joel/Code/my-project",
|
|
459
|
-
check_upgrades: false,
|
|
460
|
-
});
|
|
461
|
-
|
|
462
|
-
expect(result).toContain("/Users/joel/Code/my-project");
|
|
463
|
-
expect(result).not.toContain("{project_path}");
|
|
464
|
-
});
|
|
465
|
-
|
|
466
|
-
test("includes DEFAULT MODE text when check_upgrades=false", () => {
|
|
467
|
-
const result = formatResearcherPrompt({
|
|
468
|
-
research_id: "research-abc123",
|
|
469
|
-
epic_id: "epic-xyz789",
|
|
470
|
-
tech_stack: ["Next.js"],
|
|
471
|
-
project_path: "/path/to/project",
|
|
472
|
-
check_upgrades: false,
|
|
473
|
-
});
|
|
474
|
-
|
|
475
|
-
expect(result).toContain("DEFAULT MODE");
|
|
476
|
-
expect(result).toContain("INSTALLED versions only");
|
|
477
|
-
});
|
|
478
|
-
|
|
479
|
-
test("includes UPGRADE COMPARISON MODE text when check_upgrades=true", () => {
|
|
480
|
-
const result = formatResearcherPrompt({
|
|
481
|
-
research_id: "research-abc123",
|
|
482
|
-
epic_id: "epic-xyz789",
|
|
483
|
-
tech_stack: ["Next.js"],
|
|
484
|
-
project_path: "/path/to/project",
|
|
485
|
-
check_upgrades: true,
|
|
486
|
-
});
|
|
487
|
-
|
|
488
|
-
expect(result).toContain("UPGRADE COMPARISON MODE");
|
|
489
|
-
expect(result).toContain("BOTH installed AND latest");
|
|
490
|
-
expect(result).toContain("breaking changes");
|
|
491
|
-
});
|
|
492
|
-
});
|
|
493
|
-
|
|
494
|
-
describe("on-demand research section", () => {
|
|
495
|
-
test("includes ON-DEMAND RESEARCH section after Step 9", () => {
|
|
496
|
-
// Find Step 9 and the section after it
|
|
497
|
-
const step9Pos = SUBTASK_PROMPT_V2.indexOf("### Step 9:");
|
|
498
|
-
const swarmMailPos = SUBTASK_PROMPT_V2.indexOf("## [SWARM MAIL COMMUNICATION]");
|
|
499
|
-
|
|
500
|
-
expect(step9Pos).toBeGreaterThan(0);
|
|
501
|
-
expect(swarmMailPos).toBeGreaterThan(step9Pos);
|
|
502
|
-
|
|
503
|
-
// Extract the section between Step 9 and SWARM MAIL
|
|
504
|
-
const betweenSection = SUBTASK_PROMPT_V2.substring(step9Pos, swarmMailPos);
|
|
505
|
-
|
|
506
|
-
expect(betweenSection).toContain("## [ON-DEMAND RESEARCH]");
|
|
507
|
-
});
|
|
508
|
-
|
|
509
|
-
test("research section instructs to check semantic-memory first", () => {
|
|
510
|
-
const researchMatch = SUBTASK_PROMPT_V2.match(/## \[ON-DEMAND RESEARCH\][\s\S]*?## \[SWARM MAIL/);
|
|
511
|
-
expect(researchMatch).not.toBeNull();
|
|
512
|
-
if (!researchMatch) return;
|
|
513
|
-
|
|
514
|
-
const researchContent = researchMatch[0];
|
|
515
|
-
expect(researchContent).toContain("semantic-memory_find");
|
|
516
|
-
expect(researchContent).toMatch(/check.*semantic-memory.*first/i);
|
|
517
|
-
});
|
|
518
|
-
|
|
519
|
-
test("research section includes swarm_spawn_researcher tool usage", () => {
|
|
520
|
-
const researchMatch = SUBTASK_PROMPT_V2.match(/## \[ON-DEMAND RESEARCH\][\s\S]*?## \[SWARM MAIL/);
|
|
521
|
-
expect(researchMatch).not.toBeNull();
|
|
522
|
-
if (!researchMatch) return;
|
|
523
|
-
|
|
524
|
-
const researchContent = researchMatch[0];
|
|
525
|
-
expect(researchContent).toContain("swarm_spawn_researcher");
|
|
526
|
-
});
|
|
527
|
-
|
|
528
|
-
test("research section lists specific research triggers", () => {
|
|
529
|
-
const researchMatch = SUBTASK_PROMPT_V2.match(/## \[ON-DEMAND RESEARCH\][\s\S]*?## \[SWARM MAIL/);
|
|
530
|
-
expect(researchMatch).not.toBeNull();
|
|
531
|
-
if (!researchMatch) return;
|
|
532
|
-
|
|
533
|
-
const researchContent = researchMatch[0];
|
|
534
|
-
|
|
535
|
-
// Should list when TO research
|
|
536
|
-
expect(researchContent).toMatch(/triggers|when to research/i);
|
|
537
|
-
expect(researchContent).toMatch(/API.*works|breaking changes|outdated/i);
|
|
538
|
-
});
|
|
539
|
-
|
|
540
|
-
test("research section lists when NOT to research", () => {
|
|
541
|
-
const researchMatch = SUBTASK_PROMPT_V2.match(/## \[ON-DEMAND RESEARCH\][\s\S]*?## \[SWARM MAIL/);
|
|
542
|
-
expect(researchMatch).not.toBeNull();
|
|
543
|
-
if (!researchMatch) return;
|
|
544
|
-
|
|
545
|
-
const researchContent = researchMatch[0];
|
|
546
|
-
|
|
547
|
-
// Should list when to SKIP research
|
|
548
|
-
expect(researchContent).toMatch(/don't research|skip research/i);
|
|
549
|
-
expect(researchContent).toMatch(/standard patterns|well-documented|obvious/i);
|
|
550
|
-
});
|
|
551
|
-
|
|
552
|
-
test("research section includes 3-step workflow", () => {
|
|
553
|
-
const researchMatch = SUBTASK_PROMPT_V2.match(/## \[ON-DEMAND RESEARCH\][\s\S]*?## \[SWARM MAIL/);
|
|
554
|
-
expect(researchMatch).not.toBeNull();
|
|
555
|
-
if (!researchMatch) return;
|
|
556
|
-
|
|
557
|
-
const researchContent = researchMatch[0];
|
|
558
|
-
|
|
559
|
-
// Should have numbered steps
|
|
560
|
-
expect(researchContent).toMatch(/1\.\s*.*Check semantic-memory/i);
|
|
561
|
-
expect(researchContent).toMatch(/2\.\s*.*spawn researcher/i);
|
|
562
|
-
expect(researchContent).toMatch(/3\.\s*.*wait.*continue/i);
|
|
563
|
-
});
|
|
564
|
-
});
|
|
565
|
-
|
|
566
|
-
describe("swarm_spawn_researcher tool", () => {
|
|
567
|
-
test("returns JSON with prompt field", async () => {
|
|
568
|
-
const { swarm_spawn_researcher } = await import("./swarm-prompts");
|
|
569
|
-
|
|
570
|
-
const result = await swarm_spawn_researcher.execute({
|
|
571
|
-
research_id: "research-abc123",
|
|
572
|
-
epic_id: "epic-xyz789",
|
|
573
|
-
tech_stack: ["Next.js", "React"],
|
|
574
|
-
project_path: "/Users/joel/Code/project",
|
|
575
|
-
});
|
|
576
|
-
|
|
577
|
-
const parsed = JSON.parse(result);
|
|
578
|
-
expect(parsed).toHaveProperty("prompt");
|
|
579
|
-
expect(typeof parsed.prompt).toBe("string");
|
|
580
|
-
expect(parsed.prompt.length).toBeGreaterThan(100);
|
|
581
|
-
});
|
|
582
|
-
|
|
583
|
-
test("returns subagent_type field as 'swarm/researcher'", async () => {
|
|
584
|
-
const { swarm_spawn_researcher } = await import("./swarm-prompts");
|
|
585
|
-
|
|
586
|
-
const result = await swarm_spawn_researcher.execute({
|
|
587
|
-
research_id: "research-abc123",
|
|
588
|
-
epic_id: "epic-xyz789",
|
|
589
|
-
tech_stack: ["Next.js"],
|
|
590
|
-
project_path: "/Users/joel/Code/project",
|
|
591
|
-
});
|
|
592
|
-
|
|
593
|
-
const parsed = JSON.parse(result);
|
|
594
|
-
expect(parsed.subagent_type).toBe("swarm/researcher");
|
|
595
|
-
});
|
|
596
|
-
|
|
597
|
-
test("returns expected_output schema", async () => {
|
|
598
|
-
const { swarm_spawn_researcher } = await import("./swarm-prompts");
|
|
599
|
-
|
|
600
|
-
const result = await swarm_spawn_researcher.execute({
|
|
601
|
-
research_id: "research-abc123",
|
|
602
|
-
epic_id: "epic-xyz789",
|
|
603
|
-
tech_stack: ["Next.js"],
|
|
604
|
-
project_path: "/Users/joel/Code/project",
|
|
605
|
-
});
|
|
606
|
-
|
|
607
|
-
const parsed = JSON.parse(result);
|
|
608
|
-
expect(parsed).toHaveProperty("expected_output");
|
|
609
|
-
expect(parsed.expected_output).toHaveProperty("technologies");
|
|
610
|
-
expect(parsed.expected_output).toHaveProperty("summary");
|
|
611
|
-
});
|
|
612
|
-
|
|
613
|
-
test("defaults check_upgrades to false when not provided", async () => {
|
|
614
|
-
const { swarm_spawn_researcher } = await import("./swarm-prompts");
|
|
615
|
-
|
|
616
|
-
const result = await swarm_spawn_researcher.execute({
|
|
617
|
-
research_id: "research-abc123",
|
|
618
|
-
epic_id: "epic-xyz789",
|
|
619
|
-
tech_stack: ["Next.js"],
|
|
620
|
-
project_path: "/Users/joel/Code/project",
|
|
621
|
-
});
|
|
622
|
-
|
|
623
|
-
const parsed = JSON.parse(result);
|
|
624
|
-
expect(parsed.check_upgrades).toBe(false);
|
|
625
|
-
});
|
|
626
|
-
|
|
627
|
-
test("respects check_upgrades when provided as true", async () => {
|
|
628
|
-
const { swarm_spawn_researcher } = await import("./swarm-prompts");
|
|
629
|
-
|
|
630
|
-
const result = await swarm_spawn_researcher.execute({
|
|
631
|
-
research_id: "research-abc123",
|
|
632
|
-
epic_id: "epic-xyz789",
|
|
633
|
-
tech_stack: ["Next.js"],
|
|
634
|
-
project_path: "/Users/joel/Code/project",
|
|
635
|
-
check_upgrades: true,
|
|
636
|
-
});
|
|
637
|
-
|
|
638
|
-
const parsed = JSON.parse(result);
|
|
639
|
-
expect(parsed.check_upgrades).toBe(true);
|
|
640
|
-
});
|
|
641
|
-
|
|
642
|
-
test("includes all input parameters in returned JSON", async () => {
|
|
643
|
-
const { swarm_spawn_researcher } = await import("./swarm-prompts");
|
|
644
|
-
|
|
645
|
-
const result = await swarm_spawn_researcher.execute({
|
|
646
|
-
research_id: "research-abc123",
|
|
647
|
-
epic_id: "epic-xyz789",
|
|
648
|
-
tech_stack: ["Next.js", "React", "TypeScript"],
|
|
649
|
-
project_path: "/Users/joel/Code/project",
|
|
650
|
-
check_upgrades: true,
|
|
651
|
-
});
|
|
652
|
-
|
|
653
|
-
const parsed = JSON.parse(result);
|
|
654
|
-
expect(parsed.research_id).toBe("research-abc123");
|
|
655
|
-
expect(parsed.epic_id).toBe("epic-xyz789");
|
|
656
|
-
expect(parsed.tech_stack).toEqual(["Next.js", "React", "TypeScript"]);
|
|
657
|
-
expect(parsed.project_path).toBe("/Users/joel/Code/project");
|
|
658
|
-
expect(parsed.check_upgrades).toBe(true);
|
|
659
|
-
});
|
|
660
|
-
});
|
|
661
|
-
|
|
662
|
-
describe("swarm_spawn_retry tool", () => {
|
|
663
|
-
test("generates valid retry prompt with issues", async () => {
|
|
664
|
-
const { swarm_spawn_retry } = await import("./swarm-prompts");
|
|
665
|
-
|
|
666
|
-
const result = await swarm_spawn_retry.execute({
|
|
667
|
-
bead_id: "test-project-abc123-task1",
|
|
668
|
-
epic_id: "test-project-abc123-epic1",
|
|
669
|
-
original_prompt: "Original task: implement feature X",
|
|
670
|
-
attempt: 1,
|
|
671
|
-
issues: JSON.stringify([
|
|
672
|
-
{ file: "src/feature.ts", line: 42, issue: "Missing null check", suggestion: "Add null check" }
|
|
673
|
-
]),
|
|
674
|
-
files: ["src/feature.ts"],
|
|
675
|
-
project_path: "/Users/joel/Code/project",
|
|
676
|
-
});
|
|
677
|
-
|
|
678
|
-
const parsed = JSON.parse(result);
|
|
679
|
-
expect(parsed).toHaveProperty("prompt");
|
|
680
|
-
expect(typeof parsed.prompt).toBe("string");
|
|
681
|
-
expect(parsed.prompt).toContain("RETRY ATTEMPT");
|
|
682
|
-
expect(parsed.prompt).toContain("Missing null check");
|
|
683
|
-
});
|
|
684
|
-
|
|
685
|
-
test("includes attempt number in prompt header", async () => {
|
|
686
|
-
const { swarm_spawn_retry } = await import("./swarm-prompts");
|
|
687
|
-
|
|
688
|
-
const result = await swarm_spawn_retry.execute({
|
|
689
|
-
bead_id: "test-project-abc123-task1",
|
|
690
|
-
epic_id: "test-project-abc123-epic1",
|
|
691
|
-
original_prompt: "Original task",
|
|
692
|
-
attempt: 2,
|
|
693
|
-
issues: "[]",
|
|
694
|
-
files: ["src/test.ts"],
|
|
695
|
-
});
|
|
696
|
-
|
|
697
|
-
const parsed = JSON.parse(result);
|
|
698
|
-
expect(parsed.prompt).toContain("RETRY ATTEMPT 2/3");
|
|
699
|
-
expect(parsed.attempt).toBe(2);
|
|
700
|
-
});
|
|
701
|
-
|
|
702
|
-
test("includes diff when provided", async () => {
|
|
703
|
-
const { swarm_spawn_retry } = await import("./swarm-prompts");
|
|
704
|
-
|
|
705
|
-
const diffContent = `diff --git a/src/test.ts b/src/test.ts
|
|
706
|
-
+++ b/src/test.ts
|
|
707
|
-
@@ -1 +1 @@
|
|
708
|
-
-const x = 1;
|
|
709
|
-
+const x = null;`;
|
|
710
|
-
|
|
711
|
-
const result = await swarm_spawn_retry.execute({
|
|
712
|
-
bead_id: "test-project-abc123-task1",
|
|
713
|
-
epic_id: "test-project-abc123-epic1",
|
|
714
|
-
original_prompt: "Original task",
|
|
715
|
-
attempt: 1,
|
|
716
|
-
issues: "[]",
|
|
717
|
-
diff: diffContent,
|
|
718
|
-
files: ["src/test.ts"],
|
|
719
|
-
});
|
|
720
|
-
|
|
721
|
-
const parsed = JSON.parse(result);
|
|
722
|
-
expect(parsed.prompt).toContain(diffContent);
|
|
723
|
-
expect(parsed.prompt).toContain("PREVIOUS ATTEMPT");
|
|
724
|
-
});
|
|
725
|
-
|
|
726
|
-
test("rejects attempt > 3 with error", async () => {
|
|
727
|
-
const { swarm_spawn_retry } = await import("./swarm-prompts");
|
|
728
|
-
|
|
729
|
-
await expect(async () => {
|
|
730
|
-
await swarm_spawn_retry.execute({
|
|
731
|
-
bead_id: "test-project-abc123-task1",
|
|
732
|
-
epic_id: "test-project-abc123-epic1",
|
|
733
|
-
original_prompt: "Original task",
|
|
734
|
-
attempt: 4,
|
|
735
|
-
issues: "[]",
|
|
736
|
-
files: ["src/test.ts"],
|
|
737
|
-
});
|
|
738
|
-
}).toThrow(/attempt.*exceeds.*maximum/i);
|
|
739
|
-
});
|
|
740
|
-
|
|
741
|
-
test("formats issues as readable list", async () => {
|
|
742
|
-
const { swarm_spawn_retry } = await import("./swarm-prompts");
|
|
743
|
-
|
|
744
|
-
const issues = [
|
|
745
|
-
{ file: "src/a.ts", line: 10, issue: "Missing error handling", suggestion: "Add try-catch" },
|
|
746
|
-
{ file: "src/b.ts", line: 20, issue: "Type mismatch", suggestion: "Fix types" }
|
|
747
|
-
];
|
|
748
|
-
|
|
749
|
-
const result = await swarm_spawn_retry.execute({
|
|
750
|
-
bead_id: "test-project-abc123-task1",
|
|
751
|
-
epic_id: "test-project-abc123-epic1",
|
|
752
|
-
original_prompt: "Original task",
|
|
753
|
-
attempt: 1,
|
|
754
|
-
issues: JSON.stringify(issues),
|
|
755
|
-
files: ["src/a.ts", "src/b.ts"],
|
|
756
|
-
});
|
|
757
|
-
|
|
758
|
-
const parsed = JSON.parse(result);
|
|
759
|
-
expect(parsed.prompt).toContain("ISSUES FROM PREVIOUS ATTEMPT");
|
|
760
|
-
expect(parsed.prompt).toContain("src/a.ts:10");
|
|
761
|
-
expect(parsed.prompt).toContain("Missing error handling");
|
|
762
|
-
expect(parsed.prompt).toContain("src/b.ts:20");
|
|
763
|
-
expect(parsed.prompt).toContain("Type mismatch");
|
|
764
|
-
});
|
|
765
|
-
|
|
766
|
-
test("returns expected response structure", async () => {
|
|
767
|
-
const { swarm_spawn_retry } = await import("./swarm-prompts");
|
|
768
|
-
|
|
769
|
-
const result = await swarm_spawn_retry.execute({
|
|
770
|
-
bead_id: "test-project-abc123-task1",
|
|
771
|
-
epic_id: "test-project-abc123-epic1",
|
|
772
|
-
original_prompt: "Original task",
|
|
773
|
-
attempt: 1,
|
|
774
|
-
issues: "[]",
|
|
775
|
-
files: ["src/test.ts"],
|
|
776
|
-
project_path: "/Users/joel/Code/project",
|
|
777
|
-
});
|
|
778
|
-
|
|
779
|
-
const parsed = JSON.parse(result);
|
|
780
|
-
expect(parsed).toHaveProperty("prompt");
|
|
781
|
-
expect(parsed).toHaveProperty("bead_id", "test-project-abc123-task1");
|
|
782
|
-
expect(parsed).toHaveProperty("attempt", 1);
|
|
783
|
-
expect(parsed).toHaveProperty("max_attempts", 3);
|
|
784
|
-
expect(parsed).toHaveProperty("files");
|
|
785
|
-
expect(parsed.files).toEqual(["src/test.ts"]);
|
|
786
|
-
});
|
|
787
|
-
|
|
788
|
-
test("includes standard worker contract (swarmmail_init, reserve, complete)", async () => {
|
|
789
|
-
const { swarm_spawn_retry } = await import("./swarm-prompts");
|
|
790
|
-
|
|
791
|
-
const result = await swarm_spawn_retry.execute({
|
|
792
|
-
bead_id: "test-project-abc123-task1",
|
|
793
|
-
epic_id: "test-project-abc123-epic1",
|
|
794
|
-
original_prompt: "Original task",
|
|
795
|
-
attempt: 1,
|
|
796
|
-
issues: "[]",
|
|
797
|
-
files: ["src/test.ts"],
|
|
798
|
-
project_path: "/Users/joel/Code/project",
|
|
799
|
-
});
|
|
800
|
-
|
|
801
|
-
const parsed = JSON.parse(result);
|
|
802
|
-
expect(parsed.prompt).toContain("swarmmail_init");
|
|
803
|
-
expect(parsed.prompt).toContain("swarmmail_reserve");
|
|
804
|
-
expect(parsed.prompt).toContain("swarm_complete");
|
|
805
|
-
});
|
|
806
|
-
|
|
807
|
-
test("instructs to preserve working changes", async () => {
|
|
808
|
-
const { swarm_spawn_retry } = await import("./swarm-prompts");
|
|
809
|
-
|
|
810
|
-
const result = await swarm_spawn_retry.execute({
|
|
811
|
-
bead_id: "test-project-abc123-task1",
|
|
812
|
-
epic_id: "test-project-abc123-epic1",
|
|
813
|
-
original_prompt: "Original task",
|
|
814
|
-
attempt: 1,
|
|
815
|
-
issues: JSON.stringify([{ file: "src/test.ts", line: 1, issue: "Bug", suggestion: "Fix" }]),
|
|
816
|
-
files: ["src/test.ts"],
|
|
817
|
-
});
|
|
818
|
-
|
|
819
|
-
const parsed = JSON.parse(result);
|
|
820
|
-
expect(parsed.prompt).toMatch(/preserve.*working|fix.*while preserving/i);
|
|
821
|
-
});
|
|
822
|
-
});
|
|
823
|
-
|
|
824
|
-
describe("COORDINATOR_PROMPT", () => {
|
|
825
|
-
test("constant exists and is exported", () => {
|
|
826
|
-
expect(COORDINATOR_PROMPT).toBeDefined();
|
|
827
|
-
expect(typeof COORDINATOR_PROMPT).toBe("string");
|
|
828
|
-
expect(COORDINATOR_PROMPT.length).toBeGreaterThan(100);
|
|
829
|
-
});
|
|
830
|
-
|
|
831
|
-
test("contains all phase headers (0-8)", () => {
|
|
832
|
-
expect(COORDINATOR_PROMPT).toContain("Phase 0:");
|
|
833
|
-
expect(COORDINATOR_PROMPT).toContain("Phase 1:");
|
|
834
|
-
expect(COORDINATOR_PROMPT).toContain("Phase 2:");
|
|
835
|
-
expect(COORDINATOR_PROMPT).toContain("Phase 3:");
|
|
836
|
-
expect(COORDINATOR_PROMPT).toContain("Phase 4:");
|
|
837
|
-
expect(COORDINATOR_PROMPT).toContain("Phase 5:");
|
|
838
|
-
expect(COORDINATOR_PROMPT).toContain("Phase 6:");
|
|
839
|
-
expect(COORDINATOR_PROMPT).toContain("Phase 7:");
|
|
840
|
-
expect(COORDINATOR_PROMPT).toContain("Phase 8:");
|
|
841
|
-
});
|
|
842
|
-
|
|
843
|
-
test("contains Phase 1.5: Research Phase section", () => {
|
|
844
|
-
expect(COORDINATOR_PROMPT).toContain("Phase 1.5:");
|
|
845
|
-
expect(COORDINATOR_PROMPT).toMatch(/Phase 1\.5:.*Research/i);
|
|
846
|
-
});
|
|
847
|
-
|
|
848
|
-
test("Phase 1.5 documents swarm_spawn_researcher usage", () => {
|
|
849
|
-
// Extract Phase 1.5 section
|
|
850
|
-
const phase15Match = COORDINATOR_PROMPT.match(/Phase 1\.5:[\s\S]*?Phase 2:/);
|
|
851
|
-
expect(phase15Match).not.toBeNull();
|
|
852
|
-
if (!phase15Match) return;
|
|
853
|
-
const phase15Content = phase15Match[0];
|
|
854
|
-
|
|
855
|
-
expect(phase15Content).toContain("swarm_spawn_researcher");
|
|
856
|
-
expect(phase15Content).toContain("Task(subagent_type=\"swarm/researcher\"");
|
|
857
|
-
});
|
|
858
|
-
|
|
859
|
-
test("has section explicitly forbidding direct research tool calls", () => {
|
|
860
|
-
expect(COORDINATOR_PROMPT).toMatch(/NEVER.*direct|forbidden.*tools|do not call directly/i);
|
|
861
|
-
});
|
|
862
|
-
|
|
863
|
-
test("forbidden tools section lists all prohibited tools", () => {
|
|
864
|
-
const forbiddenTools = [
|
|
865
|
-
"repo-crawl_",
|
|
866
|
-
"repo-autopsy_",
|
|
867
|
-
"webfetch",
|
|
868
|
-
"fetch_fetch",
|
|
869
|
-
"context7_",
|
|
870
|
-
"pdf-brain_search",
|
|
871
|
-
"pdf-brain_read"
|
|
872
|
-
];
|
|
873
|
-
|
|
874
|
-
for (const tool of forbiddenTools) {
|
|
875
|
-
expect(COORDINATOR_PROMPT).toContain(tool);
|
|
876
|
-
}
|
|
877
|
-
});
|
|
878
|
-
|
|
879
|
-
test("forbidden tools section explains to use swarm_spawn_researcher instead", () => {
|
|
880
|
-
// Find the forbidden tools section
|
|
881
|
-
const forbiddenMatch = COORDINATOR_PROMPT.match(/(FORBIDDEN.*for coordinators|NEVER.*FETCH.*DIRECTLY)[\s\S]{0,500}swarm_spawn_researcher/i);
|
|
882
|
-
expect(forbiddenMatch).not.toBeNull();
|
|
883
|
-
});
|
|
884
|
-
|
|
885
|
-
test("contains coordinator role boundaries section", () => {
|
|
886
|
-
expect(COORDINATOR_PROMPT).toContain("Coordinator Role Boundaries");
|
|
887
|
-
expect(COORDINATOR_PROMPT).toMatch(/COORDINATORS NEVER.*EXECUTE.*WORK/i);
|
|
888
|
-
});
|
|
889
|
-
|
|
890
|
-
test("contains MANDATORY review loop section", () => {
|
|
891
|
-
expect(COORDINATOR_PROMPT).toContain("MANDATORY Review Loop");
|
|
892
|
-
expect(COORDINATOR_PROMPT).toContain("swarm_review");
|
|
893
|
-
expect(COORDINATOR_PROMPT).toContain("swarm_review_feedback");
|
|
894
|
-
});
|
|
895
|
-
|
|
896
|
-
test("Phase 1.5 positioned between Phase 1 (Initialize) and Phase 2 (Knowledge)", () => {
|
|
897
|
-
const phase1Pos = COORDINATOR_PROMPT.indexOf("Phase 1:");
|
|
898
|
-
const phase15Pos = COORDINATOR_PROMPT.indexOf("Phase 1.5:");
|
|
899
|
-
const phase2Pos = COORDINATOR_PROMPT.indexOf("Phase 2:");
|
|
900
|
-
|
|
901
|
-
expect(phase15Pos).toBeGreaterThan(phase1Pos);
|
|
902
|
-
expect(phase15Pos).toBeLessThan(phase2Pos);
|
|
903
|
-
});
|
|
904
|
-
});
|
|
905
|
-
|
|
906
|
-
describe("formatCoordinatorPrompt", () => {
|
|
907
|
-
test("function exists and returns string", () => {
|
|
908
|
-
expect(formatCoordinatorPrompt).toBeDefined();
|
|
909
|
-
const result = formatCoordinatorPrompt({ task: "test task", projectPath: "/test" });
|
|
910
|
-
expect(typeof result).toBe("string");
|
|
911
|
-
});
|
|
912
|
-
|
|
913
|
-
test("substitutes {task} placeholder", () => {
|
|
914
|
-
const result = formatCoordinatorPrompt({
|
|
915
|
-
task: "Implement auth",
|
|
916
|
-
projectPath: "/test"
|
|
917
|
-
});
|
|
918
|
-
expect(result).toContain("Implement auth");
|
|
919
|
-
});
|
|
920
|
-
|
|
921
|
-
test("substitutes {project_path} placeholder", () => {
|
|
922
|
-
const result = formatCoordinatorPrompt({
|
|
923
|
-
task: "test",
|
|
924
|
-
projectPath: "/Users/joel/my-project"
|
|
925
|
-
});
|
|
926
|
-
expect(result).toContain("/Users/joel/my-project");
|
|
927
|
-
});
|
|
928
|
-
|
|
929
|
-
test("returns complete prompt with all phases", () => {
|
|
930
|
-
const result = formatCoordinatorPrompt({
|
|
931
|
-
task: "test",
|
|
932
|
-
projectPath: "/test"
|
|
933
|
-
});
|
|
934
|
-
|
|
935
|
-
// Should contain all phase headers
|
|
936
|
-
for (let i = 0; i <= 8; i++) {
|
|
937
|
-
expect(result).toContain(`Phase ${i}:`);
|
|
938
|
-
}
|
|
939
|
-
expect(result).toContain("Phase 1.5:");
|
|
940
|
-
});
|
|
941
|
-
});
|
|
942
|
-
|
|
943
|
-
describe("getRecentEvalFailures", () => {
|
|
944
|
-
test("returns empty string when no failures exist", async () => {
|
|
945
|
-
const { getRecentEvalFailures } = await import("./swarm-prompts");
|
|
946
|
-
const result = await getRecentEvalFailures();
|
|
947
|
-
|
|
948
|
-
// Should not throw and returns string
|
|
949
|
-
expect(typeof result).toBe("string");
|
|
950
|
-
// When no failures, returns empty or a message - either is acceptable
|
|
951
|
-
});
|
|
952
|
-
|
|
953
|
-
test("returns formatted string when failures exist", async () => {
|
|
954
|
-
const { getRecentEvalFailures } = await import("./swarm-prompts");
|
|
955
|
-
|
|
956
|
-
// This test depends on actual memory state
|
|
957
|
-
// Just verify it doesn't throw and returns a string
|
|
958
|
-
const result = await getRecentEvalFailures();
|
|
959
|
-
expect(typeof result).toBe("string");
|
|
960
|
-
});
|
|
961
|
-
|
|
962
|
-
test("includes warning emoji in header when failures present", async () => {
|
|
963
|
-
const { getRecentEvalFailures } = await import("./swarm-prompts");
|
|
964
|
-
|
|
965
|
-
// If there are failures in the system, the header should have ⚠️
|
|
966
|
-
const result = await getRecentEvalFailures();
|
|
967
|
-
|
|
968
|
-
// Either empty (no failures) or contains the warning section
|
|
969
|
-
if (result.length > 0) {
|
|
970
|
-
expect(result).toMatch(/⚠️|Recent Eval Failures/);
|
|
971
|
-
}
|
|
972
|
-
});
|
|
973
|
-
|
|
974
|
-
test("handles memory adapter errors gracefully", async () => {
|
|
975
|
-
const { getRecentEvalFailures } = await import("./swarm-prompts");
|
|
976
|
-
|
|
977
|
-
// Should not throw even if memory is unavailable
|
|
978
|
-
await expect(getRecentEvalFailures()).resolves.toBeDefined();
|
|
979
|
-
});
|
|
980
|
-
});
|
|
981
|
-
|
|
982
|
-
describe("getPromptInsights", () => {
|
|
983
|
-
describe("for coordinators (planning prompts)", () => {
|
|
984
|
-
test("returns formatted insights string", async () => {
|
|
985
|
-
const { getPromptInsights } = await import("./swarm-prompts");
|
|
986
|
-
const result = await getPromptInsights({ role: "coordinator" });
|
|
987
|
-
|
|
988
|
-
expect(typeof result).toBe("string");
|
|
989
|
-
});
|
|
990
|
-
|
|
991
|
-
test.skip("includes strategy success rates when data exists", async () => {
|
|
992
|
-
const { getPromptInsights } = await import("./swarm-prompts");
|
|
993
|
-
const result = await getPromptInsights({ role: "coordinator" });
|
|
994
|
-
|
|
995
|
-
// If there's data, should mention strategies
|
|
996
|
-
if (result.length > 0) {
|
|
997
|
-
expect(result).toMatch(/strategy|file-based|feature-based|risk-based/i);
|
|
998
|
-
}
|
|
999
|
-
});
|
|
1000
|
-
|
|
1001
|
-
test.skip("includes recent failure patterns", async () => {
|
|
1002
|
-
const { getPromptInsights } = await import("./swarm-prompts");
|
|
1003
|
-
const result = await getPromptInsights({ role: "coordinator" });
|
|
1004
|
-
|
|
1005
|
-
// Should query for failures and anti-patterns
|
|
1006
|
-
if (result.length > 0) {
|
|
1007
|
-
expect(result).toMatch(/avoid|failure|anti-pattern|success rate/i);
|
|
1008
|
-
}
|
|
1009
|
-
});
|
|
1010
|
-
|
|
1011
|
-
test.skip("returns empty string when no data available", async () => {
|
|
1012
|
-
const { getPromptInsights } = await import("./swarm-prompts");
|
|
1013
|
-
|
|
1014
|
-
// With project_key filter that doesn't exist, should return empty
|
|
1015
|
-
const result = await getPromptInsights({
|
|
1016
|
-
role: "coordinator",
|
|
1017
|
-
project_key: "non-existent-project-xyz123"
|
|
1018
|
-
});
|
|
1019
|
-
|
|
1020
|
-
expect(typeof result).toBe("string");
|
|
1021
|
-
});
|
|
1022
|
-
});
|
|
1023
|
-
|
|
1024
|
-
describe("for workers (subtask prompts)", () => {
|
|
1025
|
-
test.skip("returns formatted insights string", async () => {
|
|
1026
|
-
const { getPromptInsights } = await import("./swarm-prompts");
|
|
1027
|
-
const result = await getPromptInsights({
|
|
1028
|
-
role: "worker",
|
|
1029
|
-
files: ["src/test.ts"]
|
|
1030
|
-
});
|
|
1031
|
-
|
|
1032
|
-
expect(typeof result).toBe("string");
|
|
1033
|
-
});
|
|
1034
|
-
|
|
1035
|
-
test.skip("queries semantic-memory for file-specific learnings", async () => {
|
|
1036
|
-
const { getPromptInsights } = await import("./swarm-prompts");
|
|
1037
|
-
const result = await getPromptInsights({
|
|
1038
|
-
role: "worker",
|
|
1039
|
-
files: ["src/auth.ts", "src/api/login.ts"]
|
|
1040
|
-
});
|
|
1041
|
-
|
|
1042
|
-
// Should query semantic memory with file/domain keywords
|
|
1043
|
-
// Result format doesn't matter, just verify it doesn't throw
|
|
1044
|
-
expect(typeof result).toBe("string");
|
|
1045
|
-
});
|
|
1046
|
-
|
|
1047
|
-
test.skip("includes common pitfalls for domain area", async () => {
|
|
1048
|
-
const { getPromptInsights } = await import("./swarm-prompts");
|
|
1049
|
-
const result = await getPromptInsights({
|
|
1050
|
-
role: "worker",
|
|
1051
|
-
domain: "authentication"
|
|
1052
|
-
});
|
|
1053
|
-
|
|
1054
|
-
if (result.length > 0) {
|
|
1055
|
-
expect(result).toMatch(/pitfall|gotcha|warning|common|issue/i);
|
|
1056
|
-
}
|
|
1057
|
-
});
|
|
1058
|
-
});
|
|
1059
|
-
|
|
1060
|
-
describe("handles errors gracefully", () => {
|
|
1061
|
-
test.skip("returns empty string when database unavailable", async () => {
|
|
1062
|
-
const { getPromptInsights } = await import("./swarm-prompts");
|
|
1063
|
-
|
|
1064
|
-
// Should not throw even if swarm-mail DB is unavailable
|
|
1065
|
-
await expect(getPromptInsights({ role: "coordinator" })).resolves.toBeDefined();
|
|
1066
|
-
});
|
|
1067
|
-
|
|
1068
|
-
test.skip("returns empty string when semantic-memory unavailable", async () => {
|
|
1069
|
-
const { getPromptInsights } = await import("./swarm-prompts");
|
|
1070
|
-
|
|
1071
|
-
// Should not throw even if memory is unavailable
|
|
1072
|
-
await expect(getPromptInsights({ role: "worker", files: [] })).resolves.toBeDefined();
|
|
1073
|
-
});
|
|
1074
|
-
});
|
|
1075
|
-
|
|
1076
|
-
describe("formatting", () => {
|
|
1077
|
-
test.skip("formats strategy stats as readable table", async () => {
|
|
1078
|
-
const { getPromptInsights } = await import("./swarm-prompts");
|
|
1079
|
-
const result = await getPromptInsights({ role: "coordinator" });
|
|
1080
|
-
|
|
1081
|
-
if (result.includes("Strategy")) {
|
|
1082
|
-
// Should use markdown table or similar readable format
|
|
1083
|
-
expect(result).toMatch(/\|.*\||\n-+\n|Strategy.*Success/i);
|
|
1084
|
-
}
|
|
1085
|
-
});
|
|
1086
|
-
|
|
1087
|
-
test.skip("limits output to prevent context bloat", async () => {
|
|
1088
|
-
const { getPromptInsights } = await import("./swarm-prompts");
|
|
1089
|
-
const result = await getPromptInsights({ role: "coordinator" });
|
|
1090
|
-
|
|
1091
|
-
// Should cap at reasonable length (say, 1500 chars max)
|
|
1092
|
-
expect(result.length).toBeLessThan(2000);
|
|
1093
|
-
});
|
|
1094
|
-
|
|
1095
|
-
test.skip("includes visual emphasis (emoji or markdown)", async () => {
|
|
1096
|
-
const { getPromptInsights } = await import("./swarm-prompts");
|
|
1097
|
-
const result = await getPromptInsights({ role: "coordinator" });
|
|
1098
|
-
|
|
1099
|
-
if (result.length > 0) {
|
|
1100
|
-
// Should have at least some formatting
|
|
1101
|
-
expect(result).toMatch(/##|📊|✅|❌|⚠️|\*\*/);
|
|
1102
|
-
}
|
|
1103
|
-
});
|
|
1104
|
-
});
|
|
1105
|
-
|
|
1106
|
-
describe("getPromptInsights integration with swarm-insights", () => {
|
|
1107
|
-
test("coordinator role uses swarm-insights data layer", async () => {
|
|
1108
|
-
const { getPromptInsights } = await import("./swarm-prompts");
|
|
1109
|
-
|
|
1110
|
-
// Should call new data layer, not old swarm-mail analytics
|
|
1111
|
-
const result = await getPromptInsights({ role: "coordinator" });
|
|
1112
|
-
|
|
1113
|
-
// If we have data, it should be formatted by formatInsightsForPrompt
|
|
1114
|
-
if (result.length > 0) {
|
|
1115
|
-
// New format has "Historical Insights" section
|
|
1116
|
-
expect(result).toMatch(/Historical Insights|Strategy Performance|Common Pitfalls/i);
|
|
1117
|
-
}
|
|
1118
|
-
});
|
|
1119
|
-
|
|
1120
|
-
test("coordinator insights have expected structure when data exists", async () => {
|
|
1121
|
-
const { getPromptInsights } = await import("./swarm-prompts");
|
|
1122
|
-
const result = await getPromptInsights({ role: "coordinator" });
|
|
1123
|
-
|
|
1124
|
-
// Should have Historical Insights header
|
|
1125
|
-
if (result.length > 0) {
|
|
1126
|
-
expect(result).toContain("📊 Historical Insights");
|
|
1127
|
-
expect(result).toContain("Use these learnings when selecting decomposition strategies");
|
|
1128
|
-
}
|
|
1129
|
-
});
|
|
1130
|
-
|
|
1131
|
-
test("coordinator insights use formatInsightsForPrompt output", async () => {
|
|
1132
|
-
const { getPromptInsights } = await import("./swarm-prompts");
|
|
1133
|
-
const result = await getPromptInsights({ role: "coordinator" });
|
|
1134
|
-
|
|
1135
|
-
// formatInsightsForPrompt produces specific markdown patterns
|
|
1136
|
-
if (result.length > 0 && result.includes("Strategy")) {
|
|
1137
|
-
// Should have Strategy Performance or Common Pitfalls sections
|
|
1138
|
-
const hasExpectedSections =
|
|
1139
|
-
result.includes("Strategy Performance") ||
|
|
1140
|
-
result.includes("Common Pitfalls");
|
|
1141
|
-
expect(hasExpectedSections).toBe(true);
|
|
1142
|
-
}
|
|
1143
|
-
});
|
|
1144
|
-
|
|
1145
|
-
test("coordinator insights are concise (<500 tokens)", async () => {
|
|
1146
|
-
const { getPromptInsights } = await import("./swarm-prompts");
|
|
1147
|
-
const result = await getPromptInsights({ role: "coordinator" });
|
|
1148
|
-
|
|
1149
|
-
// formatInsightsForPrompt enforces maxTokens=500 by default
|
|
1150
|
-
// Rough estimate: 4 chars per token = 2000 chars max
|
|
1151
|
-
if (result.length > 0) {
|
|
1152
|
-
expect(result.length).toBeLessThan(2000);
|
|
1153
|
-
}
|
|
1154
|
-
});
|
|
1155
|
-
|
|
1156
|
-
test("gracefully handles missing data", async () => {
|
|
1157
|
-
const { getPromptInsights } = await import("./swarm-prompts");
|
|
1158
|
-
|
|
1159
|
-
// Should not throw if database is empty or missing
|
|
1160
|
-
const result = await getPromptInsights({ role: "coordinator" });
|
|
1161
|
-
|
|
1162
|
-
// Empty string is acceptable when no data
|
|
1163
|
-
expect(typeof result).toBe("string");
|
|
1164
|
-
});
|
|
1165
|
-
|
|
1166
|
-
test("imports from swarm-insights module", async () => {
|
|
1167
|
-
// Verify the imports exist
|
|
1168
|
-
const insights = await import("./swarm-insights");
|
|
1169
|
-
|
|
1170
|
-
expect(insights.getStrategyInsights).toBeDefined();
|
|
1171
|
-
expect(insights.getPatternInsights).toBeDefined();
|
|
1172
|
-
expect(insights.formatInsightsForPrompt).toBeDefined();
|
|
1173
|
-
});
|
|
1174
|
-
});
|
|
1175
|
-
|
|
1176
|
-
describe("worker insights integration with swarm-insights", () => {
|
|
1177
|
-
test("worker role uses getFileInsights from swarm-insights data layer", async () => {
|
|
1178
|
-
const { getPromptInsights } = await import("./swarm-prompts");
|
|
1179
|
-
|
|
1180
|
-
// Should call getFileInsights for file-specific insights
|
|
1181
|
-
const result = await getPromptInsights({
|
|
1182
|
-
role: "worker",
|
|
1183
|
-
files: ["src/auth.ts", "src/db.ts"],
|
|
1184
|
-
domain: "authentication"
|
|
1185
|
-
});
|
|
1186
|
-
|
|
1187
|
-
// If we have data, it should be formatted by formatInsightsForPrompt
|
|
1188
|
-
if (result.length > 0) {
|
|
1189
|
-
// New format has "File-Specific Gotchas" or semantic memory learnings
|
|
1190
|
-
expect(result).toMatch(/File-Specific Gotchas|Relevant Learnings/i);
|
|
1191
|
-
}
|
|
1192
|
-
});
|
|
1193
|
-
|
|
1194
|
-
test("worker insights include file-specific gotchas when available", async () => {
|
|
1195
|
-
const { getPromptInsights } = await import("./swarm-prompts");
|
|
1196
|
-
const result = await getPromptInsights({
|
|
1197
|
-
role: "worker",
|
|
1198
|
-
files: ["src/test-file.ts"],
|
|
1199
|
-
});
|
|
1200
|
-
|
|
1201
|
-
// Should contain either file gotchas or semantic memory results
|
|
1202
|
-
if (result.length > 0) {
|
|
1203
|
-
const hasFileInsights =
|
|
1204
|
-
result.includes("File-Specific Gotchas") ||
|
|
1205
|
-
result.includes("Relevant Learnings");
|
|
1206
|
-
expect(hasFileInsights).toBe(true);
|
|
1207
|
-
}
|
|
1208
|
-
});
|
|
1209
|
-
|
|
1210
|
-
test("worker insights combine event store failures + semantic memory", async () => {
|
|
1211
|
-
const { getPromptInsights } = await import("./swarm-prompts");
|
|
1212
|
-
const result = await getPromptInsights({
|
|
1213
|
-
role: "worker",
|
|
1214
|
-
files: ["src/complex.ts"],
|
|
1215
|
-
domain: "complex feature"
|
|
1216
|
-
});
|
|
1217
|
-
|
|
1218
|
-
// Should potentially have both sources of insight
|
|
1219
|
-
// At minimum, should return string (empty if no data)
|
|
1220
|
-
expect(typeof result).toBe("string");
|
|
1221
|
-
});
|
|
1222
|
-
|
|
1223
|
-
test("worker insights are concise (<300 tokens per file)", async () => {
|
|
1224
|
-
const { getPromptInsights } = await import("./swarm-prompts");
|
|
1225
|
-
const result = await getPromptInsights({
|
|
1226
|
-
role: "worker",
|
|
1227
|
-
files: ["src/file1.ts", "src/file2.ts", "src/file3.ts"],
|
|
1228
|
-
});
|
|
1229
|
-
|
|
1230
|
-
// <300 tokens per file = 900 tokens max for 3 files
|
|
1231
|
-
// Rough estimate: 4 chars per token = 3600 chars max
|
|
1232
|
-
if (result.length > 0) {
|
|
1233
|
-
expect(result.length).toBeLessThan(3600);
|
|
1234
|
-
}
|
|
1235
|
-
});
|
|
1236
|
-
|
|
1237
|
-
test("formatSubtaskPromptV2 includes file insights in shared_context", async () => {
|
|
1238
|
-
const result = await formatSubtaskPromptV2({
|
|
1239
|
-
bead_id: "test-123",
|
|
1240
|
-
epic_id: "epic-456",
|
|
1241
|
-
subtask_title: "Implement auth",
|
|
1242
|
-
subtask_description: "Add authentication flow",
|
|
1243
|
-
files: ["src/auth.ts", "src/user.ts"],
|
|
1244
|
-
shared_context: "Original context from coordinator",
|
|
1245
|
-
});
|
|
1246
|
-
|
|
1247
|
-
// shared_context should be replaced and insights potentially included
|
|
1248
|
-
// At minimum, the original context should be in the prompt
|
|
1249
|
-
expect(result).toContain("Original context from coordinator");
|
|
1250
|
-
});
|
|
1251
|
-
|
|
1252
|
-
test("worker insights gracefully handle missing files parameter", async () => {
|
|
1253
|
-
const { getPromptInsights } = await import("./swarm-prompts");
|
|
1254
|
-
|
|
1255
|
-
// Should not throw with no files or domain
|
|
1256
|
-
const result = await getPromptInsights({ role: "worker" });
|
|
1257
|
-
|
|
1258
|
-
// Empty string is acceptable when no context to query
|
|
1259
|
-
expect(typeof result).toBe("string");
|
|
1260
|
-
});
|
|
1261
|
-
|
|
1262
|
-
test("worker insights use swarm-insights getFileInsights", async () => {
|
|
1263
|
-
// Verify the function is imported
|
|
1264
|
-
const insights = await import("./swarm-insights");
|
|
1265
|
-
|
|
1266
|
-
expect(insights.getFileInsights).toBeDefined();
|
|
1267
|
-
expect(typeof insights.getFileInsights).toBe("function");
|
|
1268
|
-
});
|
|
1269
|
-
});
|
|
1270
|
-
});
|