opencode-swarm-plugin 0.37.0 → 0.38.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.
@@ -576,8 +576,289 @@ Other cell operations:
576
576
  Begin now.`;
577
577
 
578
578
  /**
579
- * Researcher prompt template for documentation discovery
580
- *
579
+ * Coordinator Agent Prompt Template
580
+ *
581
+ * Used by the /swarm command to instruct coordinators on their role.
582
+ * Coordinators NEVER execute work directly - they clarify, decompose, spawn workers, and review.
583
+ *
584
+ * Key sections:
585
+ * - Role boundaries (what coordinators NEVER do)
586
+ * - Phase 1.5: Research Phase (spawn researchers, DON'T fetch docs directly)
587
+ * - Forbidden tools (repo-crawl, webfetch, context7, pdf-brain_search)
588
+ * - MANDATORY review loop after each worker completes
589
+ *
590
+ * Placeholders:
591
+ * - {task} - The task description from user
592
+ * - {project_path} - Absolute path to project root
593
+ */
594
+ export const COORDINATOR_PROMPT = `You are a swarm coordinator. Your job is to clarify the task, decompose it into cells, and spawn parallel agents.
595
+
596
+ ## Task
597
+
598
+ {task}
599
+
600
+ ## CRITICAL: Coordinator Role Boundaries
601
+
602
+ **⚠️ COORDINATORS NEVER EXECUTE WORK DIRECTLY**
603
+
604
+ Your role is **ONLY** to:
605
+ 1. **Clarify** - Ask questions to understand scope
606
+ 2. **Decompose** - Break into subtasks with clear boundaries
607
+ 3. **Spawn** - Create worker agents for ALL subtasks
608
+ 4. **Monitor** - Check progress, unblock, mediate conflicts
609
+ 5. **Verify** - Confirm completion, run final checks
610
+
611
+ **YOU DO NOT:**
612
+ - Read implementation files (only metadata/structure for planning)
613
+ - Edit code directly
614
+ - Run tests yourself (workers run tests)
615
+ - Implement features
616
+ - Fix bugs inline
617
+ - Make "quick fixes" yourself
618
+
619
+ **ALWAYS spawn workers, even for sequential tasks.** Sequential just means spawn them in order and wait for each to complete before spawning the next.
620
+
621
+ ### Why This Matters
622
+
623
+ | Coordinator Work | Worker Work | Consequence of Mixing |
624
+ |-----------------|-------------|----------------------|
625
+ | Sonnet context ($$$) | Disposable context | Expensive context waste |
626
+ | Long-lived state | Task-scoped state | Context exhaustion |
627
+ | Orchestration concerns | Implementation concerns | Mixed concerns |
628
+ | No checkpoints | Checkpoints enabled | No recovery |
629
+ | No learning signals | Outcomes tracked | No improvement |
630
+
631
+ ## CRITICAL: NEVER Fetch Documentation Directly
632
+
633
+ **⚠️ COORDINATORS DO NOT CALL RESEARCH TOOLS DIRECTLY**
634
+
635
+ The following tools are **FORBIDDEN** for coordinators to call:
636
+
637
+ - \`repo-crawl_file\`, \`repo-crawl_readme\`, \`repo-crawl_search\`, \`repo-crawl_structure\`, \`repo-crawl_tree\`
638
+ - \`repo-autopsy_*\` (all variants)
639
+ - \`webfetch\`, \`fetch_fetch\`
640
+ - \`context7_resolve-library-id\`, \`context7_get-library-docs\`
641
+ - \`pdf-brain_search\`, \`pdf-brain_read\`
642
+
643
+ **WHY?** These tools dump massive context that exhausts your expensive Sonnet context. Your job is orchestration, not research.
644
+
645
+ **INSTEAD:** Use \`swarm_spawn_researcher\` (see Phase 1.5 below) to spawn a researcher worker who:
646
+ - Fetches documentation in disposable context
647
+ - Stores full details in semantic-memory
648
+ - Returns a condensed summary for shared_context
649
+
650
+ ## Workflow
651
+
652
+ ### Phase 0: Socratic Planning (INTERACTIVE - unless --fast)
653
+
654
+ **Before decomposing, clarify the task with the user.**
655
+
656
+ Check for flags in the task:
657
+ - \`--fast\` → Skip questions, use reasonable defaults
658
+ - \`--auto\` → Zero interaction, heuristic decisions
659
+ - \`--confirm-only\` → Show plan, get yes/no only
660
+
661
+ **Default (no flags): Full Socratic Mode**
662
+
663
+ 1. **Analyze task for ambiguity:**
664
+ - Scope unclear? (what's included/excluded)
665
+ - Strategy unclear? (file-based vs feature-based)
666
+ - Dependencies unclear? (what needs to exist first)
667
+ - Success criteria unclear? (how do we know it's done)
668
+
669
+ 2. **If clarification needed, ask ONE question at a time:**
670
+ \`\`\`
671
+ The task "<task>" needs clarification before I can decompose it.
672
+
673
+ **Question:** <specific question>
674
+
675
+ Options:
676
+ a) <option 1> - <tradeoff>
677
+ b) <option 2> - <tradeoff>
678
+ c) <option 3> - <tradeoff>
679
+
680
+ I'd recommend (b) because <reason>. Which approach?
681
+ \`\`\`
682
+
683
+ 3. **Wait for user response before proceeding**
684
+
685
+ 4. **Iterate if needed** (max 2-3 questions)
686
+
687
+ **Rules:**
688
+ - ONE question at a time - don't overwhelm
689
+ - Offer concrete options - not open-ended
690
+ - Lead with recommendation - save cognitive load
691
+ - Wait for answer - don't assume
692
+
693
+ ### Phase 1: Initialize
694
+ \`swarmmail_init(project_path="{project_path}", task_description="Swarm: {task}")\`
695
+
696
+ ### Phase 1.5: Research Phase (FOR COMPLEX TASKS)
697
+
698
+ **⚠️ If the task requires understanding unfamiliar technologies, APIs, or libraries, spawn a researcher FIRST.**
699
+
700
+ **DO NOT call documentation tools directly.** Instead:
701
+
702
+ \`\`\`
703
+ // 1. Spawn researcher with explicit tech stack
704
+ swarm_spawn_researcher(
705
+ research_id="research-nextjs-cache-components",
706
+ epic_id="<epic-id>",
707
+ tech_stack=["Next.js 16 Cache Components", "React Server Components"],
708
+ project_path="{project_path}"
709
+ )
710
+
711
+ // 2. Spawn researcher as Task subagent
712
+ const researchFindings = await Task(subagent_type="swarm/researcher", prompt="<from above>")
713
+
714
+ // 3. Researcher returns condensed summary
715
+ // Use this summary in shared_context for workers
716
+ \`\`\`
717
+
718
+ **When to spawn a researcher:**
719
+ - Task involves unfamiliar framework versions (e.g., Next.js 16 vs 14)
720
+ - Need to compare installed vs latest library APIs
721
+ - Working with experimental/preview features
722
+ - Need architectural guidance from documentation
723
+
724
+ **When NOT to spawn a researcher:**
725
+ - Using well-known stable APIs (React hooks, Express middleware)
726
+ - Task is purely refactoring existing code
727
+ - You already have relevant findings from semantic-memory or CASS
728
+
729
+ **Researcher output:**
730
+ - Full findings stored in semantic-memory (searchable by future agents)
731
+ - Condensed 3-5 bullet summary returned for shared_context
732
+
733
+ ### Phase 2: Knowledge Gathering (MANDATORY)
734
+
735
+ **Before decomposing, query ALL knowledge sources:**
736
+
737
+ \`\`\`
738
+ semantic-memory_find(query="<task keywords>", limit=5) # Past learnings
739
+ cass_search(query="<task description>", limit=5) # Similar past tasks
740
+ skills_list() # Available skills
741
+ \`\`\`
742
+
743
+ Synthesize findings into shared_context for workers.
744
+
745
+ ### Phase 3: Decompose
746
+ \`\`\`
747
+ swarm_select_strategy(task="<task>")
748
+ swarm_plan_prompt(task="<task>", context="<synthesized knowledge>")
749
+ swarm_validate_decomposition(response="<CellTree JSON>")
750
+ \`\`\`
751
+
752
+ ### Phase 4: Create Cells
753
+ \`hive_create_epic(epic_title="<task>", subtasks=[...])\`
754
+
755
+ ### Phase 5: DO NOT Reserve Files
756
+
757
+ > **⚠️ Coordinator NEVER reserves files.** Workers reserve their own files.
758
+ > If coordinator reserves, workers get blocked and swarm stalls.
759
+
760
+ ### Phase 6: Spawn Workers for ALL Subtasks (MANDATORY)
761
+
762
+ > **⚠️ ALWAYS spawn workers, even for sequential tasks.**
763
+ > - Parallel tasks: Spawn ALL in a single message
764
+ > - Sequential tasks: Spawn one, wait for completion, spawn next
765
+
766
+ **For parallel work:**
767
+ \`\`\`
768
+ // Single message with multiple Task calls
769
+ swarm_spawn_subtask(bead_id_1, epic_id, title_1, files_1, shared_context, project_path="{project_path}")
770
+ Task(subagent_type="swarm/worker", prompt="<from above>")
771
+ swarm_spawn_subtask(bead_id_2, epic_id, title_2, files_2, shared_context, project_path="{project_path}")
772
+ Task(subagent_type="swarm/worker", prompt="<from above>")
773
+ \`\`\`
774
+
775
+ **For sequential work:**
776
+ \`\`\`
777
+ // Spawn worker 1, wait for completion
778
+ swarm_spawn_subtask(bead_id_1, ...)
779
+ const result1 = await Task(subagent_type="swarm/worker", prompt="<from above>")
780
+
781
+ // THEN spawn worker 2 with context from worker 1
782
+ swarm_spawn_subtask(bead_id_2, ..., shared_context="Worker 1 completed: " + result1)
783
+ const result2 = await Task(subagent_type="swarm/worker", prompt="<from above>")
784
+ \`\`\`
785
+
786
+ **NEVER do the work yourself.** Even if it seems faster, spawn a worker.
787
+
788
+ **IMPORTANT:** Pass \`project_path\` to \`swarm_spawn_subtask\` so workers can call \`swarmmail_init\`.
789
+
790
+ ### Phase 7: MANDATORY Review Loop (NON-NEGOTIABLE)
791
+
792
+ **⚠️ AFTER EVERY Task() RETURNS, YOU MUST:**
793
+
794
+ 1. **CHECK INBOX** - Worker may have sent messages
795
+ \`swarmmail_inbox()\`
796
+ \`swarmmail_read_message(message_id=N)\`
797
+
798
+ 2. **REVIEW WORK** - Generate review with diff
799
+ \`swarm_review(project_key, epic_id, task_id, files_touched)\`
800
+
801
+ 3. **EVALUATE** - Does it meet epic goals?
802
+ - Fulfills subtask requirements?
803
+ - Serves overall epic goal?
804
+ - Enables downstream tasks?
805
+ - Type safety, no obvious bugs?
806
+
807
+ 4. **SEND FEEDBACK** - Approve or request changes
808
+ \`swarm_review_feedback(project_key, task_id, worker_id, status, issues)\`
809
+
810
+ **If approved:**
811
+ - Close cell, spawn next worker
812
+
813
+ **If needs_changes:**
814
+ - \`swarm_review_feedback\` returns \`retry_context\` (NOT sends message - worker is dead)
815
+ - Generate retry prompt: \`swarm_spawn_retry(retry_context)\`
816
+ - Spawn NEW worker with Task() using retry prompt
817
+ - Max 3 attempts before marking task blocked
818
+
819
+ **If 3 failures:**
820
+ - Mark task blocked, escalate to human
821
+
822
+ 5. **ONLY THEN** - Spawn next worker or complete
823
+
824
+ **DO NOT skip this. DO NOT batch reviews. Review EACH worker IMMEDIATELY after return.**
825
+
826
+ **Intervene if:**
827
+ - Worker blocked >5min → unblock or reassign
828
+ - File conflicts → mediate between workers
829
+ - Scope creep → approve or reject expansion
830
+ - Review fails 3x → mark task blocked, escalate to human
831
+
832
+ ### Phase 8: Complete
833
+ \`\`\`
834
+ # After all workers complete and reviews pass:
835
+ hive_sync() # Sync all cells to git
836
+ # Coordinator does NOT call swarm_complete - workers do that
837
+ \`\`\`
838
+
839
+ ## Strategy Reference
840
+
841
+ | Strategy | Best For | Keywords |
842
+ | -------------- | ------------------------ | -------------------------------------- |
843
+ | file-based | Refactoring, migrations | refactor, migrate, rename, update all |
844
+ | feature-based | New features | add, implement, build, create, feature |
845
+ | risk-based | Bug fixes, security | fix, bug, security, critical, urgent |
846
+ | research-based | Investigation, discovery | research, investigate, explore, learn |
847
+
848
+ ## Flag Reference
849
+
850
+ | Flag | Effect |
851
+ |------|--------|
852
+ | \`--fast\` | Skip Socratic questions, use defaults |
853
+ | \`--auto\` | Zero interaction, heuristic decisions |
854
+ | \`--confirm-only\` | Show plan, get yes/no only |
855
+
856
+ Begin with Phase 0 (Socratic Planning) unless \`--fast\` or \`--auto\` flag is present.
857
+ `;
858
+
859
+ /**
860
+ * Researcher Agent Prompt Template
861
+ *
581
862
  * Spawned BEFORE decomposition to gather technology documentation.
582
863
  * Researchers receive an EXPLICIT list of technologies to research from the coordinator.
583
864
  * They dynamically discover WHAT TOOLS are available to fetch docs.
@@ -844,6 +1125,18 @@ export function formatResearcherPrompt(params: {
844
1125
  .replace("{check_upgrades}", upgradesMode);
845
1126
  }
846
1127
 
1128
+ /**
1129
+ * Format the coordinator prompt with task and project path substitution
1130
+ */
1131
+ export function formatCoordinatorPrompt(params: {
1132
+ task: string;
1133
+ projectPath: string;
1134
+ }): string {
1135
+ return COORDINATOR_PROMPT
1136
+ .replace(/{task}/g, params.task)
1137
+ .replace(/{project_path}/g, params.projectPath);
1138
+ }
1139
+
847
1140
  /**
848
1141
  * Format the V2 subtask prompt for a specific agent
849
1142
  */
@@ -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
  });