opencode-swarm-plugin 0.32.0 → 0.33.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.
@@ -8,7 +8,9 @@
8
8
  import { describe, expect, test } from "bun:test";
9
9
  import {
10
10
  formatSubtaskPromptV2,
11
+ formatResearcherPrompt,
11
12
  SUBTASK_PROMPT_V2,
13
+ RESEARCHER_PROMPT,
12
14
  } from "./swarm-prompts";
13
15
 
14
16
  describe("SUBTASK_PROMPT_V2", () => {
@@ -266,3 +268,390 @@ describe("swarm_spawn_subtask tool", () => {
266
268
  expect(instructions).toContain("DO THIS IMMEDIATELY");
267
269
  });
268
270
  });
271
+
272
+ describe("RESEARCHER_PROMPT", () => {
273
+ describe("required sections", () => {
274
+ test("includes IDENTITY section with research_id and epic_id", () => {
275
+ expect(RESEARCHER_PROMPT).toContain("## [IDENTITY]");
276
+ expect(RESEARCHER_PROMPT).toContain("{research_id}");
277
+ expect(RESEARCHER_PROMPT).toContain("{epic_id}");
278
+ });
279
+
280
+ test("includes MISSION section explaining the role", () => {
281
+ expect(RESEARCHER_PROMPT).toContain("## [MISSION]");
282
+ expect(RESEARCHER_PROMPT).toMatch(/gather.*documentation/i);
283
+ });
284
+
285
+ test("includes WORKFLOW section with numbered steps", () => {
286
+ expect(RESEARCHER_PROMPT).toContain("## [WORKFLOW]");
287
+ expect(RESEARCHER_PROMPT).toContain("### Step 1:");
288
+ expect(RESEARCHER_PROMPT).toContain("### Step 2:");
289
+ });
290
+
291
+ test("includes CRITICAL REQUIREMENTS section", () => {
292
+ expect(RESEARCHER_PROMPT).toContain("## [CRITICAL REQUIREMENTS]");
293
+ expect(RESEARCHER_PROMPT).toMatch(/NON-NEGOTIABLE/i);
294
+ });
295
+ });
296
+
297
+ describe("workflow steps", () => {
298
+ test("Step 1 is swarmmail_init (MANDATORY FIRST)", () => {
299
+ expect(RESEARCHER_PROMPT).toMatch(/### Step 1:.*Initialize/i);
300
+ expect(RESEARCHER_PROMPT).toContain("swarmmail_init");
301
+ expect(RESEARCHER_PROMPT).toContain("project_path");
302
+ });
303
+
304
+ test("Step 2 is discovering available documentation tools", () => {
305
+ const step2Match = RESEARCHER_PROMPT.match(/### Step 2:[\s\S]*?### Step 3:/);
306
+ expect(step2Match).not.toBeNull();
307
+ if (!step2Match) return;
308
+
309
+ const step2Content = step2Match[0];
310
+ expect(step2Content).toMatch(/discover.*tools/i);
311
+ expect(step2Content).toContain("nextjs_docs");
312
+ expect(step2Content).toContain("context7");
313
+ expect(step2Content).toContain("fetch");
314
+ expect(step2Content).toContain("pdf-brain");
315
+ });
316
+
317
+ test("Step 3 is reading installed versions", () => {
318
+ const step3Match = RESEARCHER_PROMPT.match(/### Step 3:[\s\S]*?### Step 4:/);
319
+ expect(step3Match).not.toBeNull();
320
+ if (!step3Match) return;
321
+
322
+ const step3Content = step3Match[0];
323
+ expect(step3Content).toMatch(/read.*installed.*version/i);
324
+ expect(step3Content).toContain("package.json");
325
+ });
326
+
327
+ test("Step 4 is fetching documentation", () => {
328
+ const step4Match = RESEARCHER_PROMPT.match(/### Step 4:[\s\S]*?### Step 5:/);
329
+ expect(step4Match).not.toBeNull();
330
+ if (!step4Match) return;
331
+
332
+ const step4Content = step4Match[0];
333
+ expect(step4Content).toMatch(/fetch.*documentation/i);
334
+ expect(step4Content).toContain("INSTALLED version");
335
+ });
336
+
337
+ test("Step 5 is storing detailed findings in semantic-memory", () => {
338
+ const step5Match = RESEARCHER_PROMPT.match(/### Step 5:[\s\S]*?### Step 6:/);
339
+ expect(step5Match).not.toBeNull();
340
+ if (!step5Match) return;
341
+
342
+ const step5Content = step5Match[0];
343
+ expect(step5Content).toContain("semantic-memory_store");
344
+ expect(step5Content).toMatch(/store.*individually/i);
345
+ });
346
+
347
+ test("Step 6 is broadcasting summary to coordinator", () => {
348
+ const step6Match = RESEARCHER_PROMPT.match(/### Step 6:[\s\S]*?### Step 7:/);
349
+ expect(step6Match).not.toBeNull();
350
+ if (!step6Match) return;
351
+
352
+ const step6Content = step6Match[0];
353
+ expect(step6Content).toContain("swarmmail_send");
354
+ expect(step6Content).toContain("coordinator");
355
+ });
356
+
357
+ test("Step 7 is returning structured JSON output", () => {
358
+ const step7Match = RESEARCHER_PROMPT.match(/### Step 7:[\s\S]*?(?=## \[|$)/);
359
+ expect(step7Match).not.toBeNull();
360
+ if (!step7Match) return;
361
+
362
+ const step7Content = step7Match[0];
363
+ expect(step7Content).toContain("JSON");
364
+ expect(step7Content).toContain("technologies");
365
+ expect(step7Content).toContain("summary");
366
+ });
367
+ });
368
+
369
+ describe("coordinator-provided tech stack", () => {
370
+ test("emphasizes that coordinator provides the tech list", () => {
371
+ expect(RESEARCHER_PROMPT).toMatch(/COORDINATOR PROVIDED.*TECHNOLOGIES/i);
372
+ expect(RESEARCHER_PROMPT).toContain("{tech_stack}");
373
+ });
374
+
375
+ test("clarifies researcher does NOT discover what to research", () => {
376
+ expect(RESEARCHER_PROMPT).toMatch(/NOT discover what to research/i);
377
+ expect(RESEARCHER_PROMPT).toMatch(/DO discover.*TOOLS/i);
378
+ });
379
+ });
380
+
381
+ describe("upgrade comparison mode", () => {
382
+ test("includes placeholder for check_upgrades mode", () => {
383
+ expect(RESEARCHER_PROMPT).toContain("{check_upgrades}");
384
+ });
385
+
386
+ test("mentions comparing installed vs latest when in upgrade mode", () => {
387
+ expect(RESEARCHER_PROMPT).toMatch(/check-upgrades/i);
388
+ expect(RESEARCHER_PROMPT).toMatch(/compare|latest.*version/i);
389
+ });
390
+ });
391
+
392
+ describe("output requirements", () => {
393
+ test("specifies TWO output destinations: semantic-memory and return JSON", () => {
394
+ expect(RESEARCHER_PROMPT).toMatch(/TWO places/i);
395
+ expect(RESEARCHER_PROMPT).toContain("semantic-memory");
396
+ expect(RESEARCHER_PROMPT).toContain("Return JSON");
397
+ });
398
+
399
+ test("explains semantic-memory is for detailed findings", () => {
400
+ expect(RESEARCHER_PROMPT).toMatch(/semantic-memory.*detailed/i);
401
+ });
402
+
403
+ test("explains return JSON is for condensed summary", () => {
404
+ expect(RESEARCHER_PROMPT).toMatch(/return.*condensed.*summary/i);
405
+ });
406
+ });
407
+ });
408
+
409
+ describe("formatResearcherPrompt", () => {
410
+ test("substitutes research_id placeholder", () => {
411
+ const result = formatResearcherPrompt({
412
+ research_id: "research-abc123",
413
+ epic_id: "epic-xyz789",
414
+ tech_stack: ["Next.js", "React"],
415
+ project_path: "/path/to/project",
416
+ check_upgrades: false,
417
+ });
418
+
419
+ expect(result).toContain("research-abc123");
420
+ expect(result).not.toContain("{research_id}");
421
+ });
422
+
423
+ test("substitutes epic_id placeholder", () => {
424
+ const result = formatResearcherPrompt({
425
+ research_id: "research-abc123",
426
+ epic_id: "epic-xyz789",
427
+ tech_stack: ["Next.js"],
428
+ project_path: "/path/to/project",
429
+ check_upgrades: false,
430
+ });
431
+
432
+ expect(result).toContain("epic-xyz789");
433
+ expect(result).not.toContain("{epic_id}");
434
+ });
435
+
436
+ test("formats tech_stack as bulleted list", () => {
437
+ const result = formatResearcherPrompt({
438
+ research_id: "research-abc123",
439
+ epic_id: "epic-xyz789",
440
+ tech_stack: ["Next.js", "React", "TypeScript"],
441
+ project_path: "/path/to/project",
442
+ check_upgrades: false,
443
+ });
444
+
445
+ expect(result).toContain("- Next.js");
446
+ expect(result).toContain("- React");
447
+ expect(result).toContain("- TypeScript");
448
+ });
449
+
450
+ test("substitutes project_path placeholder", () => {
451
+ const result = formatResearcherPrompt({
452
+ research_id: "research-abc123",
453
+ epic_id: "epic-xyz789",
454
+ tech_stack: ["Next.js"],
455
+ project_path: "/Users/joel/Code/my-project",
456
+ check_upgrades: false,
457
+ });
458
+
459
+ expect(result).toContain("/Users/joel/Code/my-project");
460
+ expect(result).not.toContain("{project_path}");
461
+ });
462
+
463
+ test("includes DEFAULT MODE text when check_upgrades=false", () => {
464
+ const result = formatResearcherPrompt({
465
+ research_id: "research-abc123",
466
+ epic_id: "epic-xyz789",
467
+ tech_stack: ["Next.js"],
468
+ project_path: "/path/to/project",
469
+ check_upgrades: false,
470
+ });
471
+
472
+ expect(result).toContain("DEFAULT MODE");
473
+ expect(result).toContain("INSTALLED versions only");
474
+ });
475
+
476
+ test("includes UPGRADE COMPARISON MODE text when check_upgrades=true", () => {
477
+ const result = formatResearcherPrompt({
478
+ research_id: "research-abc123",
479
+ epic_id: "epic-xyz789",
480
+ tech_stack: ["Next.js"],
481
+ project_path: "/path/to/project",
482
+ check_upgrades: true,
483
+ });
484
+
485
+ expect(result).toContain("UPGRADE COMPARISON MODE");
486
+ expect(result).toContain("BOTH installed AND latest");
487
+ expect(result).toContain("breaking changes");
488
+ });
489
+ });
490
+
491
+ describe("on-demand research section", () => {
492
+ test("includes ON-DEMAND RESEARCH section after Step 9", () => {
493
+ // Find Step 9 and the section after it
494
+ const step9Pos = SUBTASK_PROMPT_V2.indexOf("### Step 9:");
495
+ const swarmMailPos = SUBTASK_PROMPT_V2.indexOf("## [SWARM MAIL COMMUNICATION]");
496
+
497
+ expect(step9Pos).toBeGreaterThan(0);
498
+ expect(swarmMailPos).toBeGreaterThan(step9Pos);
499
+
500
+ // Extract the section between Step 9 and SWARM MAIL
501
+ const betweenSection = SUBTASK_PROMPT_V2.substring(step9Pos, swarmMailPos);
502
+
503
+ expect(betweenSection).toContain("## [ON-DEMAND RESEARCH]");
504
+ });
505
+
506
+ test("research section instructs to check semantic-memory first", () => {
507
+ const researchMatch = SUBTASK_PROMPT_V2.match(/## \[ON-DEMAND RESEARCH\][\s\S]*?## \[SWARM MAIL/);
508
+ expect(researchMatch).not.toBeNull();
509
+ if (!researchMatch) return;
510
+
511
+ const researchContent = researchMatch[0];
512
+ expect(researchContent).toContain("semantic-memory_find");
513
+ expect(researchContent).toMatch(/check.*semantic-memory.*first/i);
514
+ });
515
+
516
+ test("research section includes swarm_spawn_researcher tool usage", () => {
517
+ const researchMatch = SUBTASK_PROMPT_V2.match(/## \[ON-DEMAND RESEARCH\][\s\S]*?## \[SWARM MAIL/);
518
+ expect(researchMatch).not.toBeNull();
519
+ if (!researchMatch) return;
520
+
521
+ const researchContent = researchMatch[0];
522
+ expect(researchContent).toContain("swarm_spawn_researcher");
523
+ });
524
+
525
+ test("research section lists specific research triggers", () => {
526
+ const researchMatch = SUBTASK_PROMPT_V2.match(/## \[ON-DEMAND RESEARCH\][\s\S]*?## \[SWARM MAIL/);
527
+ expect(researchMatch).not.toBeNull();
528
+ if (!researchMatch) return;
529
+
530
+ const researchContent = researchMatch[0];
531
+
532
+ // Should list when TO research
533
+ expect(researchContent).toMatch(/triggers|when to research/i);
534
+ expect(researchContent).toMatch(/API.*works|breaking changes|outdated/i);
535
+ });
536
+
537
+ test("research section lists when NOT to research", () => {
538
+ const researchMatch = SUBTASK_PROMPT_V2.match(/## \[ON-DEMAND RESEARCH\][\s\S]*?## \[SWARM MAIL/);
539
+ expect(researchMatch).not.toBeNull();
540
+ if (!researchMatch) return;
541
+
542
+ const researchContent = researchMatch[0];
543
+
544
+ // Should list when to SKIP research
545
+ expect(researchContent).toMatch(/don't research|skip research/i);
546
+ expect(researchContent).toMatch(/standard patterns|well-documented|obvious/i);
547
+ });
548
+
549
+ test("research section includes 3-step workflow", () => {
550
+ const researchMatch = SUBTASK_PROMPT_V2.match(/## \[ON-DEMAND RESEARCH\][\s\S]*?## \[SWARM MAIL/);
551
+ expect(researchMatch).not.toBeNull();
552
+ if (!researchMatch) return;
553
+
554
+ const researchContent = researchMatch[0];
555
+
556
+ // Should have numbered steps
557
+ expect(researchContent).toMatch(/1\.\s*.*Check semantic-memory/i);
558
+ expect(researchContent).toMatch(/2\.\s*.*spawn researcher/i);
559
+ expect(researchContent).toMatch(/3\.\s*.*wait.*continue/i);
560
+ });
561
+ });
562
+
563
+ describe("swarm_spawn_researcher tool", () => {
564
+ test("returns JSON with prompt field", async () => {
565
+ const { swarm_spawn_researcher } = await import("./swarm-prompts");
566
+
567
+ const result = await swarm_spawn_researcher.execute({
568
+ research_id: "research-abc123",
569
+ epic_id: "epic-xyz789",
570
+ tech_stack: ["Next.js", "React"],
571
+ project_path: "/Users/joel/Code/project",
572
+ });
573
+
574
+ const parsed = JSON.parse(result);
575
+ expect(parsed).toHaveProperty("prompt");
576
+ expect(typeof parsed.prompt).toBe("string");
577
+ expect(parsed.prompt.length).toBeGreaterThan(100);
578
+ });
579
+
580
+ test("returns subagent_type field as 'swarm/researcher'", async () => {
581
+ const { swarm_spawn_researcher } = await import("./swarm-prompts");
582
+
583
+ const result = await swarm_spawn_researcher.execute({
584
+ research_id: "research-abc123",
585
+ epic_id: "epic-xyz789",
586
+ tech_stack: ["Next.js"],
587
+ project_path: "/Users/joel/Code/project",
588
+ });
589
+
590
+ const parsed = JSON.parse(result);
591
+ expect(parsed.subagent_type).toBe("swarm/researcher");
592
+ });
593
+
594
+ test("returns expected_output schema", async () => {
595
+ const { swarm_spawn_researcher } = await import("./swarm-prompts");
596
+
597
+ const result = await swarm_spawn_researcher.execute({
598
+ research_id: "research-abc123",
599
+ epic_id: "epic-xyz789",
600
+ tech_stack: ["Next.js"],
601
+ project_path: "/Users/joel/Code/project",
602
+ });
603
+
604
+ const parsed = JSON.parse(result);
605
+ expect(parsed).toHaveProperty("expected_output");
606
+ expect(parsed.expected_output).toHaveProperty("technologies");
607
+ expect(parsed.expected_output).toHaveProperty("summary");
608
+ });
609
+
610
+ test("defaults check_upgrades to false when not provided", async () => {
611
+ const { swarm_spawn_researcher } = await import("./swarm-prompts");
612
+
613
+ const result = await swarm_spawn_researcher.execute({
614
+ research_id: "research-abc123",
615
+ epic_id: "epic-xyz789",
616
+ tech_stack: ["Next.js"],
617
+ project_path: "/Users/joel/Code/project",
618
+ });
619
+
620
+ const parsed = JSON.parse(result);
621
+ expect(parsed.check_upgrades).toBe(false);
622
+ });
623
+
624
+ test("respects check_upgrades when provided as true", async () => {
625
+ const { swarm_spawn_researcher } = await import("./swarm-prompts");
626
+
627
+ const result = await swarm_spawn_researcher.execute({
628
+ research_id: "research-abc123",
629
+ epic_id: "epic-xyz789",
630
+ tech_stack: ["Next.js"],
631
+ project_path: "/Users/joel/Code/project",
632
+ check_upgrades: true,
633
+ });
634
+
635
+ const parsed = JSON.parse(result);
636
+ expect(parsed.check_upgrades).toBe(true);
637
+ });
638
+
639
+ test("includes all input parameters in returned JSON", async () => {
640
+ const { swarm_spawn_researcher } = await import("./swarm-prompts");
641
+
642
+ const result = await swarm_spawn_researcher.execute({
643
+ research_id: "research-abc123",
644
+ epic_id: "epic-xyz789",
645
+ tech_stack: ["Next.js", "React", "TypeScript"],
646
+ project_path: "/Users/joel/Code/project",
647
+ check_upgrades: true,
648
+ });
649
+
650
+ const parsed = JSON.parse(result);
651
+ expect(parsed.research_id).toBe("research-abc123");
652
+ expect(parsed.epic_id).toBe("epic-xyz789");
653
+ expect(parsed.tech_stack).toEqual(["Next.js", "React", "TypeScript"]);
654
+ expect(parsed.project_path).toBe("/Users/joel/Code/project");
655
+ expect(parsed.check_upgrades).toBe(true);
656
+ });
657
+ });
@@ -464,6 +464,29 @@ swarm_complete(
464
464
 
465
465
  **DO NOT manually close the cell with hive_close.** Use swarm_complete.
466
466
 
467
+ ## [ON-DEMAND RESEARCH]
468
+
469
+ If you encounter unknown API behavior or version-specific issues:
470
+
471
+ 1. **Check semantic-memory first:**
472
+ \`semantic-memory_find(query="<library> <version> <topic>", limit=3, expand=true)\`
473
+
474
+ 2. **If not found, spawn researcher:**
475
+ \`swarm_spawn_researcher(research_id="{bead_id}-research", epic_id="{epic_id}", tech_stack=["<library>"], project_path="{project_path}")\`
476
+ Then spawn with Task tool: \`Task(subagent_type="swarm/researcher", prompt="<from above>")\`
477
+
478
+ 3. **Wait for research, then continue**
479
+
480
+ **Research triggers:**
481
+ - "I'm not sure how this API works in version X"
482
+ - "This might have breaking changes"
483
+ - "The docs I remember might be outdated"
484
+
485
+ **Don't research:**
486
+ - Standard patterns you're confident about
487
+ - Well-documented, stable APIs
488
+ - Obvious implementations
489
+
467
490
  ## [SWARM MAIL COMMUNICATION]
468
491
 
469
492
  ### Check Inbox Regularly
@@ -551,6 +574,124 @@ Other cell operations:
551
574
 
552
575
  Begin now.`;
553
576
 
577
+ /**
578
+ * Researcher prompt template for documentation discovery
579
+ *
580
+ * Spawned BEFORE decomposition to gather technology documentation.
581
+ * Researchers receive an EXPLICIT list of technologies to research from the coordinator.
582
+ * They dynamically discover WHAT TOOLS are available to fetch docs.
583
+ * Output: condensed summary for shared_context + detailed findings in semantic-memory.
584
+ */
585
+ export const RESEARCHER_PROMPT = `You are a swarm researcher gathering documentation for: **{research_id}**
586
+
587
+ ## [IDENTITY]
588
+ Agent: (assigned at spawn)
589
+ Research Task: {research_id}
590
+ Epic: {epic_id}
591
+
592
+ ## [MISSION]
593
+ Gather comprehensive documentation for the specified technologies to inform task decomposition.
594
+
595
+ **COORDINATOR PROVIDED THESE TECHNOLOGIES TO RESEARCH:**
596
+ {tech_stack}
597
+
598
+ You do NOT discover what to research - the coordinator already decided that.
599
+ You DO discover what TOOLS are available to fetch documentation.
600
+
601
+ ## [OUTPUT MODE]
602
+ {check_upgrades}
603
+
604
+ ## [WORKFLOW]
605
+
606
+ ### Step 1: Initialize (MANDATORY FIRST)
607
+ \`\`\`
608
+ swarmmail_init(project_path="{project_path}", task_description="{research_id}: Documentation research")
609
+ \`\`\`
610
+
611
+ ### Step 2: Discover Available Documentation Tools
612
+ Check what's available for fetching docs:
613
+ - **next-devtools**: \`nextjs_docs\` for Next.js documentation
614
+ - **context7**: Library documentation lookup (\`use context7\` in prompts)
615
+ - **fetch**: General web fetching for official docs sites
616
+ - **pdf-brain**: Internal knowledge base search
617
+
618
+ **Don't assume** - check which tools exist in your environment.
619
+
620
+ ### Step 3: Read Installed Versions
621
+ For each technology in the tech stack:
622
+ 1. Check package.json (or equivalent) for installed version
623
+ 2. Record exact version numbers
624
+ 3. Note any version constraints (^, ~, etc.)
625
+
626
+ ### Step 4: Fetch Documentation
627
+ For EACH technology in the list:
628
+ - Use the most appropriate tool (Next.js → nextjs_docs, libraries → context7, others → fetch)
629
+ - Fetch documentation for the INSTALLED version (not latest, unless --check-upgrades)
630
+ - Focus on: API changes, breaking changes, migration guides, best practices
631
+ - Extract key patterns, gotchas, and compatibility notes
632
+
633
+ **If --check-upgrades mode:**
634
+ - ALSO fetch docs for the LATEST version
635
+ - Compare installed vs latest
636
+ - Note breaking changes, new features, migration complexity
637
+
638
+ ### Step 5: Store Detailed Findings
639
+ For EACH technology, store in semantic-memory:
640
+ \`\`\`
641
+ semantic-memory_store(
642
+ information="<technology-name> <version>: <key patterns, gotchas, API changes, compatibility notes>",
643
+ tags="research, <tech-name>, documentation, {epic_id}"
644
+ )
645
+ \`\`\`
646
+
647
+ **Why store individually?** Future agents can search by technology name.
648
+
649
+ ### Step 6: Broadcast Summary
650
+ Send condensed findings to coordinator:
651
+ \`\`\`
652
+ swarmmail_send(
653
+ to=["coordinator"],
654
+ subject="Research Complete: {research_id}",
655
+ body="<brief summary - see semantic-memory for details>",
656
+ thread_id="{epic_id}"
657
+ )
658
+ \`\`\`
659
+
660
+ ### Step 7: Return Structured Output
661
+ Output JSON with:
662
+ \`\`\`json
663
+ {
664
+ "technologies": [
665
+ {
666
+ "name": "string",
667
+ "installed_version": "string",
668
+ "latest_version": "string | null", // Only if --check-upgrades
669
+ "key_patterns": ["string"],
670
+ "gotchas": ["string"],
671
+ "breaking_changes": ["string"], // Only if --check-upgrades
672
+ "memory_id": "string" // ID of semantic-memory entry
673
+ }
674
+ ],
675
+ "summary": "string" // Condensed summary for shared_context
676
+ }
677
+ \`\`\`
678
+
679
+ ## [CRITICAL REQUIREMENTS]
680
+
681
+ **NON-NEGOTIABLE:**
682
+ 1. Step 1 (swarmmail_init) MUST be first
683
+ 2. Research ONLY the technologies the coordinator specified
684
+ 3. Fetch docs for INSTALLED versions (unless --check-upgrades)
685
+ 4. Store detailed findings in semantic-memory (one per technology)
686
+ 5. Return condensed summary for coordinator (full details in memory)
687
+ 6. Use appropriate doc tools (nextjs_docs for Next.js, context7 for libraries, etc.)
688
+
689
+ **Output goes TWO places:**
690
+ - **semantic-memory**: Detailed findings (searchable by future agents)
691
+ - **Return JSON**: Condensed summary (for coordinator's shared_context)
692
+
693
+ Begin research now.`;
694
+
554
695
  /**
555
696
  * Coordinator post-worker checklist - MANDATORY review loop
556
697
  *
@@ -656,6 +797,30 @@ should describe what needs to be fixed.`;
656
797
  // Helper Functions
657
798
  // ============================================================================
658
799
 
800
+ /**
801
+ * Format the researcher prompt for a documentation research task
802
+ */
803
+ export function formatResearcherPrompt(params: {
804
+ research_id: string;
805
+ epic_id: string;
806
+ tech_stack: string[];
807
+ project_path: string;
808
+ check_upgrades: boolean;
809
+ }): string {
810
+ const techList = params.tech_stack.map((t) => `- ${t}`).join("\n");
811
+
812
+ const upgradesMode = params.check_upgrades
813
+ ? "**UPGRADE COMPARISON MODE**: Fetch docs for BOTH installed AND latest versions. Compare and note breaking changes."
814
+ : "**DEFAULT MODE**: Fetch docs for INSTALLED versions only (from lockfiles).";
815
+
816
+ return RESEARCHER_PROMPT
817
+ .replace(/{research_id}/g, params.research_id)
818
+ .replace(/{epic_id}/g, params.epic_id)
819
+ .replace("{tech_stack}", techList)
820
+ .replace("{project_path}", params.project_path)
821
+ .replace("{check_upgrades}", upgradesMode);
822
+ }
823
+
659
824
  /**
660
825
  * Format the V2 subtask prompt for a specific agent
661
826
  */
@@ -937,6 +1102,68 @@ export const swarm_spawn_subtask = tool({
937
1102
  },
938
1103
  });
939
1104
 
1105
+ /**
1106
+ * Prepare a researcher task for spawning with Task tool
1107
+ *
1108
+ * Generates a prompt that tells the researcher to fetch documentation for specific technologies.
1109
+ * Returns JSON that can be directly used with Task tool.
1110
+ */
1111
+ export const swarm_spawn_researcher = tool({
1112
+ description:
1113
+ "Prepare a research task for spawning. Returns prompt for gathering technology documentation. Researcher fetches docs and stores findings in semantic-memory.",
1114
+ args: {
1115
+ research_id: tool.schema.string().describe("Unique ID for this research task"),
1116
+ epic_id: tool.schema.string().describe("Parent epic ID"),
1117
+ tech_stack: tool.schema
1118
+ .array(tool.schema.string())
1119
+ .describe("Explicit list of technologies to research (from coordinator)"),
1120
+ project_path: tool.schema
1121
+ .string()
1122
+ .describe("Absolute project path for swarmmail_init"),
1123
+ check_upgrades: tool.schema
1124
+ .boolean()
1125
+ .optional()
1126
+ .describe("If true, compare installed vs latest versions (default: false)"),
1127
+ },
1128
+ async execute(args) {
1129
+ const prompt = formatResearcherPrompt({
1130
+ research_id: args.research_id,
1131
+ epic_id: args.epic_id,
1132
+ tech_stack: args.tech_stack,
1133
+ project_path: args.project_path,
1134
+ check_upgrades: args.check_upgrades ?? false,
1135
+ });
1136
+
1137
+ return JSON.stringify(
1138
+ {
1139
+ prompt,
1140
+ research_id: args.research_id,
1141
+ epic_id: args.epic_id,
1142
+ tech_stack: args.tech_stack,
1143
+ project_path: args.project_path,
1144
+ check_upgrades: args.check_upgrades ?? false,
1145
+ subagent_type: "swarm/researcher",
1146
+ expected_output: {
1147
+ technologies: [
1148
+ {
1149
+ name: "string",
1150
+ installed_version: "string",
1151
+ latest_version: "string | null",
1152
+ key_patterns: ["string"],
1153
+ gotchas: ["string"],
1154
+ breaking_changes: ["string"],
1155
+ memory_id: "string",
1156
+ },
1157
+ ],
1158
+ summary: "string",
1159
+ },
1160
+ },
1161
+ null,
1162
+ 2,
1163
+ );
1164
+ },
1165
+ });
1166
+
940
1167
  /**
941
1168
  * Generate self-evaluation prompt
942
1169
  */
@@ -1125,6 +1352,7 @@ export const swarm_plan_prompt = tool({
1125
1352
  export const promptTools = {
1126
1353
  swarm_subtask_prompt,
1127
1354
  swarm_spawn_subtask,
1355
+ swarm_spawn_researcher,
1128
1356
  swarm_evaluation_prompt,
1129
1357
  swarm_plan_prompt,
1130
1358
  };