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.
- package/.hive/issues.jsonl +9 -5
- package/.hive/memories.jsonl +13 -1
- package/.turbo/turbo-build.log +4 -4
- package/.turbo/turbo-test.log +319 -319
- package/CHANGELOG.md +128 -0
- package/README.md +33 -0
- package/bin/swarm.ts +2 -208
- package/dist/hive.d.ts +59 -0
- package/dist/hive.d.ts.map +1 -1
- package/dist/index.d.ts +43 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +453 -118
- package/dist/plugin.js +452 -118
- package/dist/swarm-decompose.d.ts +30 -0
- package/dist/swarm-decompose.d.ts.map +1 -1
- package/dist/swarm.d.ts +15 -0
- package/dist/swarm.d.ts.map +1 -1
- package/evals/README.md +27 -10
- package/examples/plugin-wrapper-template.ts +60 -8
- package/package.json +4 -1
- package/src/compaction-hook.test.ts +97 -2
- package/src/compaction-hook.ts +32 -2
- package/src/swarm-decompose.test.ts +40 -47
- package/src/swarm-orchestrate.test.ts +270 -7
- package/src/swarm-orchestrate.ts +98 -11
- package/src/swarm-prompts.test.ts +121 -0
- package/src/swarm-prompts.ts +295 -2
- package/src/swarm-research.integration.test.ts +157 -0
package/src/swarm-prompts.ts
CHANGED
|
@@ -576,8 +576,289 @@ Other cell operations:
|
|
|
576
576
|
Begin now.`;
|
|
577
577
|
|
|
578
578
|
/**
|
|
579
|
-
*
|
|
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
|
});
|