opencode-swarm-plugin 0.20.0 → 0.22.0

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 (41) hide show
  1. package/.beads/issues.jsonl +213 -0
  2. package/INTEGRATION_EXAMPLE.md +66 -0
  3. package/README.md +352 -522
  4. package/dist/index.js +2046 -984
  5. package/dist/plugin.js +2051 -1017
  6. package/docs/analysis/subagent-coordination-patterns.md +2 -0
  7. package/docs/semantic-memory-cli-syntax.md +123 -0
  8. package/docs/swarm-mail-architecture.md +1147 -0
  9. package/evals/README.md +116 -0
  10. package/evals/evalite.config.ts +15 -0
  11. package/evals/example.eval.ts +32 -0
  12. package/evals/fixtures/decomposition-cases.ts +105 -0
  13. package/evals/lib/data-loader.test.ts +288 -0
  14. package/evals/lib/data-loader.ts +111 -0
  15. package/evals/lib/llm.ts +115 -0
  16. package/evals/scorers/index.ts +200 -0
  17. package/evals/scorers/outcome-scorers.test.ts +27 -0
  18. package/evals/scorers/outcome-scorers.ts +349 -0
  19. package/evals/swarm-decomposition.eval.ts +112 -0
  20. package/package.json +8 -1
  21. package/scripts/cleanup-test-memories.ts +346 -0
  22. package/src/beads.ts +49 -0
  23. package/src/eval-capture.ts +487 -0
  24. package/src/index.ts +45 -3
  25. package/src/learning.integration.test.ts +19 -4
  26. package/src/output-guardrails.test.ts +438 -0
  27. package/src/output-guardrails.ts +381 -0
  28. package/src/schemas/index.ts +18 -0
  29. package/src/schemas/swarm-context.ts +115 -0
  30. package/src/storage.ts +117 -5
  31. package/src/streams/events.test.ts +296 -0
  32. package/src/streams/events.ts +93 -0
  33. package/src/streams/migrations.test.ts +24 -20
  34. package/src/streams/migrations.ts +51 -0
  35. package/src/streams/projections.ts +187 -0
  36. package/src/streams/store.ts +275 -0
  37. package/src/swarm-orchestrate.ts +771 -189
  38. package/src/swarm-prompts.ts +84 -12
  39. package/src/swarm.integration.test.ts +124 -0
  40. package/vitest.integration.config.ts +6 -0
  41. package/vitest.integration.setup.ts +48 -0
@@ -273,20 +273,29 @@ Only modify these files. Need others? Message the coordinator.
273
273
 
274
274
  {error_context}
275
275
 
276
- ## [MANDATORY: SWARM MAIL]
276
+ ## [MANDATORY: SWARM MAIL INITIALIZATION]
277
277
 
278
- **YOU MUST USE SWARM MAIL FOR ALL COORDINATION.** This is non-negotiable.
278
+ **CRITICAL: YOU MUST INITIALIZE SWARM MAIL BEFORE DOING ANY WORK.**
279
279
 
280
- ### Initialize FIRST (before any work)
281
- \`\`\`
282
- swarmmail_init(project_path="$PWD", task_description="{subtask_title}")
283
- \`\`\`
280
+ This is your FIRST step - before reading files, before planning, before ANY other action.
284
281
 
285
- ### Reserve Files (if not already reserved by coordinator)
282
+ ### Step 1: Initialize (REQUIRED - DO THIS FIRST)
286
283
  \`\`\`
287
- swarmmail_reserve(paths=[...files...], reason="{bead_id}: {subtask_title}")
284
+ swarmmail_init(project_path="{project_path}", task_description="{bead_id}: {subtask_title}")
288
285
  \`\`\`
289
286
 
287
+ **This registers you with the coordination system and enables:**
288
+ - File reservation tracking
289
+ - Inter-agent communication
290
+ - Progress monitoring
291
+ - Conflict detection
292
+
293
+ **If you skip this step, your work will not be tracked and swarm_complete will fail.**
294
+
295
+ ## [SWARM MAIL USAGE]
296
+
297
+ After initialization, use Swarm Mail for coordination:
298
+
290
299
  ### Check Inbox Regularly
291
300
  \`\`\`
292
301
  swarmmail_inbox() # Check for coordinator messages
@@ -340,14 +349,17 @@ As you work, note reusable patterns, best practices, or domain insights:
340
349
  - Skills make swarms smarter over time
341
350
 
342
351
  ## [WORKFLOW]
343
- 1. **swarmmail_init** - Initialize session FIRST
352
+ 1. **swarmmail_init** - Initialize session (MANDATORY FIRST STEP)
344
353
  2. Read assigned files
345
354
  3. Implement changes
346
355
  4. **swarmmail_send** - Report progress to coordinator
347
356
  5. Verify (typecheck)
348
357
  6. **swarm_complete** - Mark done, release reservations
349
358
 
350
- **CRITICAL: Never work silently. Send progress updates via swarmmail_send every significant milestone.**
359
+ **CRITICAL REQUIREMENTS:**
360
+ - Step 1 (swarmmail_init) is NON-NEGOTIABLE - do it before anything else
361
+ - Never work silently - send progress updates via swarmmail_send every significant milestone
362
+ - If you complete without initializing, swarm_complete will detect this and warn/fail
351
363
 
352
364
  Begin now.`;
353
365
 
@@ -409,6 +421,12 @@ export function formatSubtaskPromptV2(params: {
409
421
  shared_context?: string;
410
422
  compressed_context?: string;
411
423
  error_context?: string;
424
+ project_path?: string;
425
+ recovery_context?: {
426
+ shared_context?: string;
427
+ skills_to_load?: string[];
428
+ coordinator_notes?: string;
429
+ };
412
430
  }): string {
413
431
  const fileList =
414
432
  params.files.length > 0
@@ -421,8 +439,40 @@ export function formatSubtaskPromptV2(params: {
421
439
 
422
440
  const errorSection = params.error_context ? params.error_context : "";
423
441
 
442
+ // Build recovery context section
443
+ let recoverySection = "";
444
+ if (params.recovery_context) {
445
+ const sections: string[] = [];
446
+
447
+ if (params.recovery_context.shared_context) {
448
+ sections.push(
449
+ `### Recovery Context\n${params.recovery_context.shared_context}`,
450
+ );
451
+ }
452
+
453
+ if (
454
+ params.recovery_context.skills_to_load &&
455
+ params.recovery_context.skills_to_load.length > 0
456
+ ) {
457
+ sections.push(
458
+ `### Skills to Load\nBefore starting work, load these skills for specialized guidance:\n${params.recovery_context.skills_to_load.map((s) => `- skills_use(name="${s}")`).join("\n")}`,
459
+ );
460
+ }
461
+
462
+ if (params.recovery_context.coordinator_notes) {
463
+ sections.push(
464
+ `### Coordinator Notes\n${params.recovery_context.coordinator_notes}`,
465
+ );
466
+ }
467
+
468
+ if (sections.length > 0) {
469
+ recoverySection = `\n## [RECOVERY CONTEXT]\n\n${sections.join("\n\n")}`;
470
+ }
471
+ }
472
+
424
473
  return SUBTASK_PROMPT_V2.replace(/{bead_id}/g, params.bead_id)
425
474
  .replace(/{epic_id}/g, params.epic_id)
475
+ .replace(/{project_path}/g, params.project_path || "$PWD")
426
476
  .replace("{subtask_title}", params.subtask_title)
427
477
  .replace(
428
478
  "{subtask_description}",
@@ -431,7 +481,7 @@ export function formatSubtaskPromptV2(params: {
431
481
  .replace("{file_list}", fileList)
432
482
  .replace("{shared_context}", params.shared_context || "(none)")
433
483
  .replace("{compressed_context}", compressedSection)
434
- .replace("{error_context}", errorSection);
484
+ .replace("{error_context}", errorSection + recoverySection);
435
485
  }
436
486
 
437
487
  /**
@@ -497,6 +547,10 @@ export const swarm_subtask_prompt = tool({
497
547
  .string()
498
548
  .optional()
499
549
  .describe("Context shared across all agents"),
550
+ project_path: tool.schema
551
+ .string()
552
+ .optional()
553
+ .describe("Absolute project path for swarmmail_init"),
500
554
  },
501
555
  async execute(args) {
502
556
  const prompt = formatSubtaskPrompt({
@@ -521,7 +575,7 @@ export const swarm_subtask_prompt = tool({
521
575
  */
522
576
  export const swarm_spawn_subtask = tool({
523
577
  description:
524
- "Prepare a subtask for spawning. Returns prompt with Agent Mail/beads instructions.",
578
+ "Prepare a subtask for spawning. Returns prompt with Agent Mail/beads instructions. IMPORTANT: Pass project_path for swarmmail_init.",
525
579
  args: {
526
580
  bead_id: tool.schema.string().describe("Subtask bead ID"),
527
581
  epic_id: tool.schema.string().describe("Parent epic bead ID"),
@@ -537,6 +591,20 @@ export const swarm_spawn_subtask = tool({
537
591
  .string()
538
592
  .optional()
539
593
  .describe("Context shared across all agents"),
594
+ project_path: tool.schema
595
+ .string()
596
+ .optional()
597
+ .describe(
598
+ "Absolute project path for swarmmail_init (REQUIRED for tracking)",
599
+ ),
600
+ recovery_context: tool.schema
601
+ .object({
602
+ shared_context: tool.schema.string().optional(),
603
+ skills_to_load: tool.schema.array(tool.schema.string()).optional(),
604
+ coordinator_notes: tool.schema.string().optional(),
605
+ })
606
+ .optional()
607
+ .describe("Recovery context from checkpoint compaction"),
540
608
  },
541
609
  async execute(args) {
542
610
  const prompt = formatSubtaskPromptV2({
@@ -546,6 +614,8 @@ export const swarm_spawn_subtask = tool({
546
614
  subtask_description: args.subtask_description || "",
547
615
  files: args.files,
548
616
  shared_context: args.shared_context,
617
+ project_path: args.project_path,
618
+ recovery_context: args.recovery_context,
549
619
  });
550
620
 
551
621
  return JSON.stringify(
@@ -554,6 +624,8 @@ export const swarm_spawn_subtask = tool({
554
624
  bead_id: args.bead_id,
555
625
  epic_id: args.epic_id,
556
626
  files: args.files,
627
+ project_path: args.project_path,
628
+ recovery_context: args.recovery_context,
557
629
  },
558
630
  null,
559
631
  2,
@@ -1356,4 +1356,128 @@ describe("Swarm Prompt V2 (with Swarm Mail/Beads)", () => {
1356
1356
  expect(SUBTASK_PROMPT_V2).toContain("CRITICAL");
1357
1357
  });
1358
1358
  });
1359
+
1360
+ describe("swarm_complete automatic memory capture", () => {
1361
+ let beadsAvailable = false;
1362
+
1363
+ beforeAll(async () => {
1364
+ beadsAvailable = await isBeadsAvailable();
1365
+ });
1366
+
1367
+ it.skipIf(!beadsAvailable)(
1368
+ "includes memory_capture object in response",
1369
+ async () => {
1370
+ // Create a real bead for the test
1371
+ const createResult =
1372
+ await Bun.$`bd create "Test memory capture" -t task --json`
1373
+ .quiet()
1374
+ .nothrow();
1375
+
1376
+ if (createResult.exitCode !== 0) {
1377
+ console.warn(
1378
+ "Could not create bead:",
1379
+ createResult.stderr.toString(),
1380
+ );
1381
+ return;
1382
+ }
1383
+
1384
+ const bead = JSON.parse(createResult.stdout.toString());
1385
+
1386
+ try {
1387
+ const result = await swarm_complete.execute(
1388
+ {
1389
+ project_key: "/tmp/test-memory-capture",
1390
+ agent_name: "test-agent",
1391
+ bead_id: bead.id,
1392
+ summary: "Implemented auto-capture feature",
1393
+ files_touched: ["src/swarm-orchestrate.ts"],
1394
+ skip_verification: true,
1395
+ },
1396
+ mockContext,
1397
+ );
1398
+
1399
+ const parsed = JSON.parse(result);
1400
+
1401
+ // Verify memory capture was attempted
1402
+ expect(parsed).toHaveProperty("memory_capture");
1403
+ expect(parsed.memory_capture).toHaveProperty("attempted", true);
1404
+ expect(parsed.memory_capture).toHaveProperty("stored");
1405
+ expect(parsed.memory_capture).toHaveProperty("information");
1406
+ expect(parsed.memory_capture).toHaveProperty("metadata");
1407
+
1408
+ // Information should contain bead ID and summary
1409
+ expect(parsed.memory_capture.information).toContain(bead.id);
1410
+ expect(parsed.memory_capture.information).toContain(
1411
+ "Implemented auto-capture feature",
1412
+ );
1413
+
1414
+ // Metadata should contain relevant tags
1415
+ expect(parsed.memory_capture.metadata).toContain("swarm");
1416
+ expect(parsed.memory_capture.metadata).toContain("success");
1417
+ } catch (error) {
1418
+ // Clean up bead if test fails
1419
+ await Bun.$`bd close ${bead.id} --reason "Test cleanup"`
1420
+ .quiet()
1421
+ .nothrow();
1422
+ throw error;
1423
+ }
1424
+ },
1425
+ );
1426
+
1427
+ it.skipIf(!beadsAvailable)(
1428
+ "attempts to store in semantic-memory when available",
1429
+ async () => {
1430
+ const createResult =
1431
+ await Bun.$`bd create "Test semantic-memory storage" -t task --json`
1432
+ .quiet()
1433
+ .nothrow();
1434
+
1435
+ if (createResult.exitCode !== 0) {
1436
+ console.warn(
1437
+ "Could not create bead:",
1438
+ createResult.stderr.toString(),
1439
+ );
1440
+ return;
1441
+ }
1442
+
1443
+ const bead = JSON.parse(createResult.stdout.toString());
1444
+
1445
+ try {
1446
+ const result = await swarm_complete.execute(
1447
+ {
1448
+ project_key: "/tmp/test-memory-storage",
1449
+ agent_name: "test-agent",
1450
+ bead_id: bead.id,
1451
+ summary: "Fixed critical bug in auth flow",
1452
+ files_touched: ["src/auth.ts", "src/middleware.ts"],
1453
+ skip_verification: true,
1454
+ },
1455
+ mockContext,
1456
+ );
1457
+
1458
+ const parsed = JSON.parse(result);
1459
+
1460
+ // If semantic-memory is available, stored should be true
1461
+ // If not, error should explain why
1462
+ if (parsed.memory_capture.stored) {
1463
+ expect(parsed.memory_capture.note).toContain(
1464
+ "automatically stored in semantic-memory",
1465
+ );
1466
+ } else {
1467
+ expect(parsed.memory_capture.error).toBeDefined();
1468
+ expect(
1469
+ parsed.memory_capture.error.includes("not available") ||
1470
+ parsed.memory_capture.error.includes("failed"),
1471
+ ).toBe(true);
1472
+ }
1473
+ } catch (error) {
1474
+ // Clean up bead if test fails
1475
+ await Bun.$`bd close ${bead.id} --reason "Test cleanup"`
1476
+ .quiet()
1477
+ .nothrow();
1478
+ throw error;
1479
+ }
1480
+ },
1481
+ );
1482
+ });
1359
1483
  });
@@ -9,5 +9,11 @@ export default defineConfig({
9
9
  sequence: {
10
10
  concurrent: false,
11
11
  },
12
+ env: {
13
+ // Enable test-specific collections to isolate test data from production
14
+ TEST_MEMORY_COLLECTIONS: "true",
15
+ },
16
+ // Global setup/teardown hooks
17
+ globalSetup: "./vitest.integration.setup.ts",
12
18
  },
13
19
  });
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Global setup/teardown for integration tests
3
+ *
4
+ * Ensures test-specific semantic-memory collections are cleaned up
5
+ * after all integration tests complete.
6
+ */
7
+
8
+ export async function setup() {
9
+ console.log("[vitest] Integration test setup: TEST_MEMORY_COLLECTIONS=true");
10
+ // Setup runs before tests - environment variables are already set via vitest config
11
+ }
12
+
13
+ export async function teardown() {
14
+ console.log(
15
+ "[vitest] Integration test teardown: cleaning up test collections",
16
+ );
17
+
18
+ // Clean up test collections
19
+ const testCollections = [
20
+ "swarm-feedback-test",
21
+ "swarm-patterns-test",
22
+ "swarm-maturity-test",
23
+ "swarm-maturity-test-feedback",
24
+ ];
25
+
26
+ for (const collection of testCollections) {
27
+ try {
28
+ // Attempt to remove test collection data
29
+ // Note: semantic-memory doesn't have a built-in "delete collection" command,
30
+ // so we'll use the remove command with a wildcard or rely on TTL/manual cleanup
31
+ console.log(`[vitest] Attempting to clean collection: ${collection}`);
32
+
33
+ // List items and remove them (semantic-memory may not support bulk delete)
34
+ // This is a best-effort cleanup - some backends may require manual cleanup
35
+ // For now, we'll just log that cleanup should happen
36
+ console.log(
37
+ `[vitest] Note: Collection "${collection}" may need manual cleanup via semantic-memory CLI`,
38
+ );
39
+ } catch (error) {
40
+ console.warn(
41
+ `[vitest] Failed to clean collection ${collection}:`,
42
+ error instanceof Error ? error.message : String(error),
43
+ );
44
+ }
45
+ }
46
+
47
+ console.log("[vitest] Integration test teardown complete");
48
+ }