opencode-swarm-plugin 0.44.0 → 0.44.1

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