opencode-swarm-plugin 0.37.0 → 0.39.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 (79) hide show
  1. package/.env +2 -0
  2. package/.hive/eval-results.json +26 -0
  3. package/.hive/issues.jsonl +20 -5
  4. package/.hive/memories.jsonl +35 -1
  5. package/.opencode/eval-history.jsonl +12 -0
  6. package/.turbo/turbo-build.log +4 -4
  7. package/.turbo/turbo-test.log +319 -319
  8. package/CHANGELOG.md +258 -0
  9. package/README.md +50 -0
  10. package/bin/swarm.test.ts +475 -0
  11. package/bin/swarm.ts +385 -208
  12. package/dist/compaction-hook.d.ts +1 -1
  13. package/dist/compaction-hook.d.ts.map +1 -1
  14. package/dist/compaction-prompt-scoring.d.ts +124 -0
  15. package/dist/compaction-prompt-scoring.d.ts.map +1 -0
  16. package/dist/eval-capture.d.ts +81 -1
  17. package/dist/eval-capture.d.ts.map +1 -1
  18. package/dist/eval-gates.d.ts +84 -0
  19. package/dist/eval-gates.d.ts.map +1 -0
  20. package/dist/eval-history.d.ts +117 -0
  21. package/dist/eval-history.d.ts.map +1 -0
  22. package/dist/eval-learning.d.ts +216 -0
  23. package/dist/eval-learning.d.ts.map +1 -0
  24. package/dist/hive.d.ts +59 -0
  25. package/dist/hive.d.ts.map +1 -1
  26. package/dist/index.d.ts +87 -0
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +823 -131
  29. package/dist/plugin.js +655 -131
  30. package/dist/post-compaction-tracker.d.ts +133 -0
  31. package/dist/post-compaction-tracker.d.ts.map +1 -0
  32. package/dist/swarm-decompose.d.ts +30 -0
  33. package/dist/swarm-decompose.d.ts.map +1 -1
  34. package/dist/swarm-orchestrate.d.ts +23 -0
  35. package/dist/swarm-orchestrate.d.ts.map +1 -1
  36. package/dist/swarm-prompts.d.ts +25 -1
  37. package/dist/swarm-prompts.d.ts.map +1 -1
  38. package/dist/swarm.d.ts +19 -0
  39. package/dist/swarm.d.ts.map +1 -1
  40. package/evals/README.md +595 -94
  41. package/evals/compaction-prompt.eval.ts +149 -0
  42. package/evals/coordinator-behavior.eval.ts +8 -8
  43. package/evals/fixtures/compaction-prompt-cases.ts +305 -0
  44. package/evals/lib/compaction-loader.test.ts +248 -0
  45. package/evals/lib/compaction-loader.ts +320 -0
  46. package/evals/lib/data-loader.test.ts +345 -0
  47. package/evals/lib/data-loader.ts +107 -6
  48. package/evals/scorers/compaction-prompt-scorers.ts +145 -0
  49. package/evals/scorers/compaction-scorers.ts +13 -13
  50. package/evals/scorers/coordinator-discipline.evalite-test.ts +3 -2
  51. package/evals/scorers/coordinator-discipline.ts +13 -13
  52. package/examples/plugin-wrapper-template.ts +177 -8
  53. package/package.json +7 -2
  54. package/scripts/migrate-unknown-sessions.ts +349 -0
  55. package/src/compaction-capture.integration.test.ts +257 -0
  56. package/src/compaction-hook.test.ts +139 -2
  57. package/src/compaction-hook.ts +113 -2
  58. package/src/compaction-prompt-scorers.test.ts +299 -0
  59. package/src/compaction-prompt-scoring.ts +298 -0
  60. package/src/eval-capture.test.ts +422 -0
  61. package/src/eval-capture.ts +94 -2
  62. package/src/eval-gates.test.ts +306 -0
  63. package/src/eval-gates.ts +218 -0
  64. package/src/eval-history.test.ts +508 -0
  65. package/src/eval-history.ts +214 -0
  66. package/src/eval-learning.test.ts +378 -0
  67. package/src/eval-learning.ts +360 -0
  68. package/src/index.ts +61 -1
  69. package/src/post-compaction-tracker.test.ts +251 -0
  70. package/src/post-compaction-tracker.ts +237 -0
  71. package/src/swarm-decompose.test.ts +40 -47
  72. package/src/swarm-decompose.ts +2 -2
  73. package/src/swarm-orchestrate.test.ts +270 -7
  74. package/src/swarm-orchestrate.ts +100 -13
  75. package/src/swarm-prompts.test.ts +121 -0
  76. package/src/swarm-prompts.ts +297 -4
  77. package/src/swarm-research.integration.test.ts +157 -0
  78. package/src/swarm-review.ts +3 -3
  79. /package/evals/{evalite.config.ts → evalite.config.ts.bak} +0 -0
@@ -495,6 +495,158 @@ describe("End-to-end research workflow", () => {
495
495
  });
496
496
  });
497
497
 
498
+ describe("Research spawn instructions (NEW)", () => {
499
+ let testProjectPath: string;
500
+
501
+ beforeEach(() => {
502
+ testProjectPath = join(tmpdir(), `spawn-test-${Date.now()}`);
503
+ mkdirSync(testProjectPath, { recursive: true });
504
+ });
505
+
506
+ afterEach(() => {
507
+ rmSync(testProjectPath, { recursive: true, force: true });
508
+ });
509
+
510
+ test("runResearchPhase generates spawn instructions for each technology", async () => {
511
+ // Create package.json with dependencies
512
+ const packageJson = {
513
+ dependencies: {
514
+ zod: "^3.22.4",
515
+ typescript: "^5.3.3",
516
+ },
517
+ };
518
+
519
+ writeFileSync(
520
+ join(testProjectPath, "package.json"),
521
+ JSON.stringify(packageJson, null, 2),
522
+ );
523
+
524
+ // Run research phase
525
+ const result = await runResearchPhase(
526
+ "Add Zod validation to TypeScript API",
527
+ testProjectPath,
528
+ );
529
+
530
+ // Should have spawn_instructions array
531
+ expect(result.spawn_instructions).toBeDefined();
532
+ expect(Array.isArray(result.spawn_instructions)).toBe(true);
533
+
534
+ // Should have one instruction per technology
535
+ expect(result.spawn_instructions.length).toBe(result.tech_stack.length);
536
+
537
+ // Each instruction should have required fields
538
+ for (const instruction of result.spawn_instructions) {
539
+ expect(instruction.research_id).toBeDefined();
540
+ expect(instruction.research_id).toMatch(/^research-/); // Should start with "research-"
541
+ expect(instruction.tech).toBeDefined();
542
+ expect(result.tech_stack).toContain(instruction.tech); // Tech should be from tech_stack
543
+ expect(instruction.prompt).toBeDefined();
544
+ expect(typeof instruction.prompt).toBe("string");
545
+ expect(instruction.prompt.length).toBeGreaterThan(0);
546
+ expect(instruction.subagent_type).toBe("swarm/researcher");
547
+ }
548
+ });
549
+
550
+ test("runResearchPhase prompts contain correct technology", async () => {
551
+ const packageJson = {
552
+ dependencies: {
553
+ zod: "^3.22.4",
554
+ },
555
+ };
556
+
557
+ writeFileSync(
558
+ join(testProjectPath, "package.json"),
559
+ JSON.stringify(packageJson, null, 2),
560
+ );
561
+
562
+ const result = await runResearchPhase("Use Zod", testProjectPath);
563
+
564
+ // Should have exactly one spawn instruction (one tech)
565
+ expect(result.spawn_instructions.length).toBe(1);
566
+
567
+ const instruction = result.spawn_instructions[0];
568
+ expect(instruction.tech).toBe("zod");
569
+ expect(instruction.prompt).toContain("zod");
570
+ expect(instruction.prompt).toContain(testProjectPath);
571
+ });
572
+
573
+ test("runResearchPhase with multiple technologies generates multiple instructions", async () => {
574
+ const packageJson = {
575
+ dependencies: {
576
+ zod: "^3.22.4",
577
+ typescript: "^5.3.3",
578
+ react: "^18.2.0",
579
+ },
580
+ };
581
+
582
+ writeFileSync(
583
+ join(testProjectPath, "package.json"),
584
+ JSON.stringify(packageJson, null, 2),
585
+ );
586
+
587
+ const result = await runResearchPhase(
588
+ "Build React app with Zod and TypeScript",
589
+ testProjectPath,
590
+ );
591
+
592
+ // Should extract 3 technologies
593
+ expect(result.tech_stack.length).toBe(3);
594
+
595
+ // Should have 3 spawn instructions
596
+ expect(result.spawn_instructions.length).toBe(3);
597
+
598
+ // Each tech should have one instruction
599
+ const techs = result.spawn_instructions.map((i) => i.tech);
600
+ expect(techs).toContain("zod");
601
+ expect(techs).toContain("typescript");
602
+ expect(techs).toContain("react");
603
+
604
+ // Research IDs should be unique
605
+ const researchIds = result.spawn_instructions.map((i) => i.research_id);
606
+ const uniqueIds = new Set(researchIds);
607
+ expect(uniqueIds.size).toBe(researchIds.length);
608
+ });
609
+
610
+ test("runResearchPhase with empty tech_stack returns empty spawn_instructions", async () => {
611
+ // Don't create package.json - no dependencies
612
+
613
+ const result = await runResearchPhase(
614
+ "Implement something with FooBarBaz",
615
+ testProjectPath,
616
+ );
617
+
618
+ // Should have empty tech_stack (no known technologies)
619
+ expect(result.tech_stack).toEqual([]);
620
+
621
+ // Should have empty spawn_instructions
622
+ expect(result.spawn_instructions).toEqual([]);
623
+
624
+ // Other fields should be empty
625
+ expect(result.summaries).toEqual({});
626
+ expect(result.memory_ids).toEqual([]);
627
+ });
628
+
629
+ test("spawn instruction prompts include swarmmail_init", async () => {
630
+ const packageJson = {
631
+ dependencies: {
632
+ zod: "^3.22.4",
633
+ },
634
+ };
635
+
636
+ writeFileSync(
637
+ join(testProjectPath, "package.json"),
638
+ JSON.stringify(packageJson, null, 2),
639
+ );
640
+
641
+ const result = await runResearchPhase("Use Zod", testProjectPath);
642
+
643
+ // Prompt should include swarmmail_init (researcher workers need this)
644
+ const instruction = result.spawn_instructions[0];
645
+ expect(instruction.prompt).toContain("swarmmail_init");
646
+ expect(instruction.prompt).toContain("semantic-memory_store");
647
+ });
648
+ });
649
+
498
650
  describe("Real-world fixture: this repo", () => {
499
651
  test("discovers tools and versions from actual repo", async () => {
500
652
  // Use the plugin package directory, not monorepo root
@@ -540,5 +692,10 @@ describe("Real-world fixture: this repo", () => {
540
692
  expect(result.summaries).toBeDefined();
541
693
  expect(result.memory_ids).toBeDefined();
542
694
  expect(Array.isArray(result.memory_ids)).toBe(true);
695
+
696
+ // NEW: Should have spawn_instructions
697
+ expect(result.spawn_instructions).toBeDefined();
698
+ expect(Array.isArray(result.spawn_instructions)).toBe(true);
699
+ expect(result.spawn_instructions.length).toBeGreaterThan(0);
543
700
  });
544
701
  });
@@ -470,7 +470,7 @@ export const swarm_review_feedback = tool({
470
470
  .optional()
471
471
  .describe("JSON array of ReviewIssue objects (for needs_changes)"),
472
472
  },
473
- async execute(args): Promise<string> {
473
+ async execute(args, _ctx): Promise<string> {
474
474
  // Parse issues if provided
475
475
  let parsedIssues: ReviewIssue[] = [];
476
476
  if (args.issues) {
@@ -512,7 +512,7 @@ export const swarm_review_feedback = tool({
512
512
  // Capture review approval decision
513
513
  try {
514
514
  captureCoordinatorEvent({
515
- session_id: process.env.OPENCODE_SESSION_ID || "unknown",
515
+ session_id: _ctx.sessionID || "unknown",
516
516
  epic_id: epicId,
517
517
  timestamp: new Date().toISOString(),
518
518
  event_type: "DECISION",
@@ -562,7 +562,7 @@ You may now complete the task with \`swarm_complete\`.`,
562
562
  // Capture review rejection decision
563
563
  try {
564
564
  captureCoordinatorEvent({
565
- session_id: process.env.OPENCODE_SESSION_ID || "unknown",
565
+ session_id: _ctx.sessionID || "unknown",
566
566
  epic_id: epicId,
567
567
  timestamp: new Date().toISOString(),
568
568
  event_type: "DECISION",