opencode-swarm-plugin 0.32.0 → 0.34.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 (55) hide show
  1. package/.hive/issues.jsonl +12 -0
  2. package/.hive/memories.jsonl +255 -1
  3. package/.turbo/turbo-build.log +9 -10
  4. package/.turbo/turbo-test.log +343 -337
  5. package/CHANGELOG.md +358 -0
  6. package/README.md +152 -179
  7. package/bin/swarm.test.ts +303 -1
  8. package/bin/swarm.ts +473 -16
  9. package/dist/compaction-hook.d.ts +1 -1
  10. package/dist/compaction-hook.d.ts.map +1 -1
  11. package/dist/index.d.ts +112 -0
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +12380 -131
  14. package/dist/logger.d.ts +34 -0
  15. package/dist/logger.d.ts.map +1 -0
  16. package/dist/observability-tools.d.ts +116 -0
  17. package/dist/observability-tools.d.ts.map +1 -0
  18. package/dist/plugin.js +12254 -119
  19. package/dist/skills.d.ts.map +1 -1
  20. package/dist/swarm-orchestrate.d.ts +105 -0
  21. package/dist/swarm-orchestrate.d.ts.map +1 -1
  22. package/dist/swarm-prompts.d.ts +113 -2
  23. package/dist/swarm-prompts.d.ts.map +1 -1
  24. package/dist/swarm-research.d.ts +127 -0
  25. package/dist/swarm-research.d.ts.map +1 -0
  26. package/dist/swarm-review.d.ts.map +1 -1
  27. package/dist/swarm.d.ts +73 -1
  28. package/dist/swarm.d.ts.map +1 -1
  29. package/evals/compaction-resumption.eval.ts +289 -0
  30. package/evals/coordinator-behavior.eval.ts +307 -0
  31. package/evals/fixtures/compaction-cases.ts +350 -0
  32. package/evals/scorers/compaction-scorers.ts +305 -0
  33. package/evals/scorers/index.ts +12 -0
  34. package/examples/plugin-wrapper-template.ts +297 -8
  35. package/package.json +6 -2
  36. package/src/compaction-hook.test.ts +617 -1
  37. package/src/compaction-hook.ts +291 -18
  38. package/src/index.ts +54 -1
  39. package/src/logger.test.ts +189 -0
  40. package/src/logger.ts +135 -0
  41. package/src/observability-tools.test.ts +346 -0
  42. package/src/observability-tools.ts +594 -0
  43. package/src/skills.integration.test.ts +137 -1
  44. package/src/skills.test.ts +42 -1
  45. package/src/skills.ts +8 -4
  46. package/src/swarm-orchestrate.test.ts +123 -0
  47. package/src/swarm-orchestrate.ts +183 -0
  48. package/src/swarm-prompts.test.ts +553 -1
  49. package/src/swarm-prompts.ts +406 -4
  50. package/src/swarm-research.integration.test.ts +544 -0
  51. package/src/swarm-research.test.ts +698 -0
  52. package/src/swarm-research.ts +472 -0
  53. package/src/swarm-review.test.ts +177 -0
  54. package/src/swarm-review.ts +12 -47
  55. package/src/swarm.ts +6 -3
@@ -16,7 +16,7 @@
16
16
  * - skills_execute
17
17
  */
18
18
 
19
- import { describe, expect, it, afterAll, beforeEach } from "vitest";
19
+ import { describe, expect, it, afterAll, beforeEach, vi } from "vitest";
20
20
  import { chmodSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
21
21
  import { join } from "node:path";
22
22
  import {
@@ -1054,3 +1054,139 @@ describe("skills_execute tool", () => {
1054
1054
  expect(result).toContain("60 seconds");
1055
1055
  }, 65000); // Allow 65s for test itself
1056
1056
  });
1057
+
1058
+ // =============================================================================
1059
+ // Deprecation Warnings Tests
1060
+ // =============================================================================
1061
+
1062
+ describe("deprecation warnings", () => {
1063
+ beforeEach(() => {
1064
+ setupTestDir();
1065
+ });
1066
+
1067
+ afterAll(() => {
1068
+ cleanupTestDir();
1069
+ });
1070
+
1071
+ it("skills_list emits deprecation warning", async () => {
1072
+ const warnSpy = vi.spyOn(console, "warn");
1073
+
1074
+ await skills_list.execute({});
1075
+
1076
+ expect(warnSpy).toHaveBeenCalledWith(
1077
+ expect.stringContaining("[DEPRECATED] skills_list")
1078
+ );
1079
+ expect(warnSpy).toHaveBeenCalledWith(
1080
+ expect.stringContaining("OpenCode now provides native skills support")
1081
+ );
1082
+ warnSpy.mockRestore();
1083
+ });
1084
+
1085
+ it("skills_use emits deprecation warning", async () => {
1086
+ await skills_create.execute({
1087
+ name: "test-skill",
1088
+ description: "Use when testing",
1089
+ body: "Instructions",
1090
+ });
1091
+
1092
+ invalidateSkillsCache();
1093
+
1094
+ const warnSpy = vi.spyOn(console, "warn");
1095
+
1096
+ await skills_use.execute({ name: "test-skill" });
1097
+
1098
+ expect(warnSpy).toHaveBeenCalledWith(
1099
+ expect.stringContaining("[DEPRECATED] skills_use")
1100
+ );
1101
+ expect(warnSpy).toHaveBeenCalledWith(
1102
+ expect.stringContaining("OpenCode now provides native skills support")
1103
+ );
1104
+ warnSpy.mockRestore();
1105
+ });
1106
+
1107
+ it("skills_read emits deprecation warning", async () => {
1108
+ await skills_create.execute({
1109
+ name: "test-skill",
1110
+ description: "Use when testing",
1111
+ body: "Instructions",
1112
+ });
1113
+
1114
+ const skillDir = join(SKILLS_DIR, "test-skill");
1115
+ writeFileSync(join(skillDir, "example.md"), "# Example");
1116
+
1117
+ invalidateSkillsCache();
1118
+
1119
+ const warnSpy = vi.spyOn(console, "warn");
1120
+
1121
+ await skills_read.execute({
1122
+ skill: "test-skill",
1123
+ file: "example.md",
1124
+ });
1125
+
1126
+ expect(warnSpy).toHaveBeenCalledWith(
1127
+ expect.stringContaining("[DEPRECATED] skills_read")
1128
+ );
1129
+ expect(warnSpy).toHaveBeenCalledWith(
1130
+ expect.stringContaining("OpenCode now provides native skills support")
1131
+ );
1132
+ warnSpy.mockRestore();
1133
+ });
1134
+
1135
+ it("skills_execute emits deprecation warning", async () => {
1136
+ await skills_create.execute({
1137
+ name: "test-skill",
1138
+ description: "Use when testing",
1139
+ body: "Instructions",
1140
+ });
1141
+
1142
+ await skills_add_script.execute({
1143
+ skill: "test-skill",
1144
+ script_name: "test.sh",
1145
+ content: '#!/bin/bash\necho "test"',
1146
+ });
1147
+
1148
+ const scriptPath = join(SKILLS_DIR, "test-skill", "scripts", "test.sh");
1149
+ chmodSync(scriptPath, 0o755);
1150
+
1151
+ invalidateSkillsCache();
1152
+
1153
+ const warnSpy = vi.spyOn(console, "warn");
1154
+
1155
+ await skills_execute.execute({
1156
+ skill: "test-skill",
1157
+ script: "test.sh",
1158
+ });
1159
+
1160
+ expect(warnSpy).toHaveBeenCalledWith(
1161
+ expect.stringContaining("[DEPRECATED] skills_execute")
1162
+ );
1163
+ expect(warnSpy).toHaveBeenCalledWith(
1164
+ expect.stringContaining("OpenCode now provides native skills support")
1165
+ );
1166
+ warnSpy.mockRestore();
1167
+ });
1168
+
1169
+ it("deprecated tools still function correctly (soft deprecation)", async () => {
1170
+ // Create a skill
1171
+ await skills_create.execute({
1172
+ name: "functional-test",
1173
+ description: "Use when testing functionality",
1174
+ body: "# Test\n\nStill works!",
1175
+ });
1176
+
1177
+ invalidateSkillsCache();
1178
+
1179
+ // Suppress console.warn for this test
1180
+ const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
1181
+
1182
+ // skills_list should still list skills
1183
+ const listResult = await skills_list.execute({});
1184
+ expect(listResult).toContain("functional-test");
1185
+
1186
+ // skills_use should still return content
1187
+ const useResult = await skills_use.execute({ name: "functional-test" });
1188
+ expect(useResult).toContain("Still works!");
1189
+
1190
+ warnSpy.mockRestore();
1191
+ });
1192
+ });
@@ -8,7 +8,7 @@
8
8
  * - ES module compatibility
9
9
  */
10
10
 
11
- import { describe, expect, it, beforeEach, afterEach } from "vitest";
11
+ import { describe, expect, it, beforeEach, afterEach, vi } from "vitest";
12
12
  import { join, resolve, relative } from "path";
13
13
  import { mkdirSync, writeFileSync, rmSync, existsSync } from "fs";
14
14
  import {
@@ -551,6 +551,47 @@ describe("validateCSOCompliance", () => {
551
551
  });
552
552
  });
553
553
 
554
+ // ============================================================================
555
+ // Tests: Deprecation Warnings
556
+ // ============================================================================
557
+
558
+ describe("deprecation warnings", () => {
559
+ beforeEach(() => {
560
+ cleanupTestSkillsDir();
561
+ setupTestSkillsDir();
562
+ setSkillsProjectDirectory(TEST_DIR);
563
+ invalidateSkillsCache();
564
+ });
565
+
566
+ afterEach(() => {
567
+ cleanupTestSkillsDir();
568
+ invalidateSkillsCache();
569
+ });
570
+
571
+ it("listSkills emits deprecation warning", async () => {
572
+ const warnSpy = vi.spyOn(console, "warn");
573
+
574
+ await listSkills();
575
+
576
+ // Verify warning was emitted (for listSkills internal function)
577
+ // The actual tool warning happens in skills_list tool execute
578
+ warnSpy.mockRestore();
579
+ });
580
+
581
+ it("getSkill does NOT emit deprecation warning (internal function)", async () => {
582
+ const warnSpy = vi.spyOn(console, "warn");
583
+
584
+ await getSkill("test-skill");
585
+
586
+ // getSkill is internal, should not have DEPRECATED warnings
587
+ const deprecationCalls = warnSpy.mock.calls.filter(call =>
588
+ call.some(arg => String(arg).includes("[DEPRECATED]"))
589
+ );
590
+ expect(deprecationCalls.length).toBe(0);
591
+ warnSpy.mockRestore();
592
+ });
593
+ });
594
+
554
595
  // ============================================================================
555
596
  // Tests: Edge Cases
556
597
  // ============================================================================
package/src/skills.ts CHANGED
@@ -399,7 +399,7 @@ export function invalidateSkillsCache(): void {
399
399
  * which skills are relevant to the current task.
400
400
  */
401
401
  export const skills_list = tool({
402
- description: `List all available skills in the project.
402
+ description: `[DEPRECATED] List all available skills in the project.
403
403
 
404
404
  Skills are specialized instructions that help with specific domains or tasks.
405
405
  Use this tool to discover what skills are available, then use skills_use to
@@ -413,6 +413,7 @@ Returns skill names, descriptions, and whether they have executable scripts.`,
413
413
  .describe("Optional tag to filter skills by"),
414
414
  },
415
415
  async execute(args) {
416
+ console.warn('[DEPRECATED] skills_list is deprecated. OpenCode now provides native skills support. This tool will be removed in a future version.');
416
417
  const skills = await discoverSkills();
417
418
  let refs = Array.from(skills.values());
418
419
 
@@ -448,7 +449,7 @@ Returns skill names, descriptions, and whether they have executable scripts.`,
448
449
  * The skill's instructions become available for the model to follow.
449
450
  */
450
451
  export const skills_use = tool({
451
- description: `Activate a skill by loading its full instructions.
452
+ description: `[DEPRECATED] Activate a skill by loading its full instructions.
452
453
 
453
454
  After calling this tool, follow the skill's instructions for the current task.
454
455
  Skills provide domain-specific guidance and best practices.
@@ -462,6 +463,7 @@ If the skill has scripts, you can run them with skills_execute.`,
462
463
  .describe("Also list available scripts (default: true)"),
463
464
  },
464
465
  async execute(args) {
466
+ console.warn('[DEPRECATED] skills_use is deprecated. OpenCode now provides native skills support. This tool will be removed in a future version.');
465
467
  const skill = await getSkill(args.name);
466
468
 
467
469
  if (!skill) {
@@ -492,7 +494,7 @@ If the skill has scripts, you can run them with skills_execute.`,
492
494
  * This tool runs them with appropriate context.
493
495
  */
494
496
  export const skills_execute = tool({
495
- description: `Execute a script from a skill's scripts/ directory.
497
+ description: `[DEPRECATED] Execute a script from a skill's scripts/ directory.
496
498
 
497
499
  Some skills include helper scripts for common operations.
498
500
  Use skills_use first to see available scripts, then execute them here.
@@ -507,6 +509,7 @@ Scripts run in the skill's directory with the project directory as an argument.`
507
509
  .describe("Additional arguments to pass to the script"),
508
510
  },
509
511
  async execute(args, ctx) {
512
+ console.warn('[DEPRECATED] skills_execute is deprecated. OpenCode now provides native skills support. This tool will be removed in a future version.');
510
513
  const skill = await getSkill(args.skill);
511
514
 
512
515
  if (!skill) {
@@ -571,7 +574,7 @@ Scripts run in the skill's directory with the project directory as an argument.`
571
574
  * Skills can include additional resources like examples, templates, or reference docs.
572
575
  */
573
576
  export const skills_read = tool({
574
- description: `Read a resource file from a skill's directory.
577
+ description: `[DEPRECATED] Read a resource file from a skill's directory.
575
578
 
576
579
  Skills may include additional files like:
577
580
  - examples.md - Example usage
@@ -586,6 +589,7 @@ Use this to access supplementary skill resources.`,
586
589
  .describe("Relative path to the file within the skill directory"),
587
590
  },
588
591
  async execute(args) {
592
+ console.warn('[DEPRECATED] skills_read is deprecated. OpenCode now provides native skills support. This tool will be removed in a future version.');
589
593
  const skill = await getSkill(args.skill);
590
594
 
591
595
  if (!skill) {
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Tests for swarm orchestration research phase
3
+ *
4
+ * Validates:
5
+ * - Tech stack extraction from task descriptions
6
+ * - Researcher spawning for identified technologies
7
+ * - Summary collection from semantic-memory
8
+ * - Research result aggregation
9
+ */
10
+
11
+ import { describe, test, expect, beforeEach } from "bun:test";
12
+ import { runResearchPhase, extractTechStack } from "./swarm-orchestrate";
13
+
14
+ describe("extractTechStack", () => {
15
+ test("extracts Next.js from task description", () => {
16
+ const task = "Add authentication to the Next.js app";
17
+ const techStack = extractTechStack(task);
18
+
19
+ expect(techStack).toContain("next");
20
+ });
21
+
22
+ test("extracts React from task description", () => {
23
+ const task = "Build a React component for user profiles";
24
+ const techStack = extractTechStack(task);
25
+
26
+ expect(techStack).toContain("react");
27
+ });
28
+
29
+ test("extracts multiple technologies", () => {
30
+ const task = "Build a Zod schema for validating Next.js API routes with TypeScript";
31
+ const techStack = extractTechStack(task);
32
+
33
+ expect(techStack).toContain("zod");
34
+ expect(techStack).toContain("next");
35
+ expect(techStack).toContain("typescript");
36
+ });
37
+
38
+ test("returns empty array for generic tasks", () => {
39
+ const task = "Refactor the authentication module";
40
+ const techStack = extractTechStack(task);
41
+
42
+ // Might extract some keywords but should be minimal
43
+ expect(Array.isArray(techStack)).toBe(true);
44
+ });
45
+
46
+ test("handles case-insensitive matching", () => {
47
+ const task = "Add NEXT.JS and REACT hooks";
48
+ const techStack = extractTechStack(task);
49
+
50
+ expect(techStack).toContain("next");
51
+ expect(techStack).toContain("react");
52
+ });
53
+
54
+ test("deduplicates repeated mentions", () => {
55
+ const task = "Use Zod for Zod schemas with Zod validation";
56
+ const techStack = extractTechStack(task);
57
+
58
+ // Should only appear once
59
+ const zodCount = techStack.filter(t => t === "zod").length;
60
+ expect(zodCount).toBe(1);
61
+ });
62
+ });
63
+
64
+ describe("runResearchPhase", () => {
65
+ const testProjectPath = "/Users/joel/Code/joelhooks/opencode-swarm-plugin";
66
+
67
+ test("returns research result with tech stack", async () => {
68
+ const task = "Add Next.js API routes with Zod validation";
69
+
70
+ const result = await runResearchPhase(task, testProjectPath);
71
+
72
+ expect(result).toHaveProperty("tech_stack");
73
+ expect(result.tech_stack).toBeInstanceOf(Array);
74
+ });
75
+
76
+ test("returns summaries keyed by technology", async () => {
77
+ const task = "Add Next.js API routes";
78
+
79
+ const result = await runResearchPhase(task, testProjectPath);
80
+
81
+ expect(result).toHaveProperty("summaries");
82
+ expect(typeof result.summaries).toBe("object");
83
+ });
84
+
85
+ test("returns memory IDs for stored research", async () => {
86
+ const task = "Add Zod schemas";
87
+
88
+ const result = await runResearchPhase(task, testProjectPath);
89
+
90
+ expect(result).toHaveProperty("memory_ids");
91
+ expect(result.memory_ids).toBeInstanceOf(Array);
92
+ });
93
+
94
+ test("skips research for tasks with no tech mentions", async () => {
95
+ const task = "Refactor the authentication module";
96
+
97
+ const result = await runResearchPhase(task, testProjectPath);
98
+
99
+ // Should return empty result quickly
100
+ expect(result.tech_stack).toHaveLength(0);
101
+ expect(result.summaries).toEqual({});
102
+ expect(result.memory_ids).toHaveLength(0);
103
+ });
104
+
105
+ test("handles check_upgrades option", async () => {
106
+ const task = "Add Next.js caching";
107
+
108
+ const result = await runResearchPhase(task, testProjectPath, {
109
+ checkUpgrades: true,
110
+ });
111
+
112
+ // Should still return valid result
113
+ expect(result).toHaveProperty("tech_stack");
114
+ expect(result).toHaveProperty("summaries");
115
+ });
116
+ });
117
+
118
+ describe("swarm_research_phase tool", () => {
119
+ test.todo("exposes research phase as plugin tool");
120
+ test.todo("validates task parameter");
121
+ test.todo("validates project_path parameter");
122
+ test.todo("returns JSON string with research results");
123
+ });
@@ -1994,6 +1994,188 @@ export const swarm_record_outcome = tool({
1994
1994
  },
1995
1995
  });
1996
1996
 
1997
+ // ============================================================================
1998
+ // Research Phase
1999
+ // ============================================================================
2000
+
2001
+ /**
2002
+ * Known technology patterns for extraction from task descriptions
2003
+ * Maps common mentions to normalized package names
2004
+ */
2005
+ const TECH_PATTERNS: Record<string, RegExp> = {
2006
+ next: /next\.?js|nextjs/i,
2007
+ react: /react(?!ive)/i,
2008
+ zod: /zod/i,
2009
+ typescript: /typescript|ts(?!\w)/i,
2010
+ tailwind: /tailwind(?:css)?/i,
2011
+ prisma: /prisma/i,
2012
+ drizzle: /drizzle(?:-orm)?/i,
2013
+ trpc: /trpc/i,
2014
+ "react-query": /react-query|tanstack.*query/i,
2015
+ axios: /axios/i,
2016
+ "node-fetch": /node-fetch|fetch api/i,
2017
+ };
2018
+
2019
+ /**
2020
+ * Extract technology stack from task description
2021
+ *
2022
+ * Searches for common framework/library mentions and returns
2023
+ * a deduplicated array of normalized names.
2024
+ *
2025
+ * @param task - Task description
2026
+ * @returns Array of detected technology names (normalized, lowercase)
2027
+ *
2028
+ * @example
2029
+ * ```typescript
2030
+ * extractTechStack("Add Next.js API routes with Zod validation")
2031
+ * // => ["next", "zod"]
2032
+ * ```
2033
+ */
2034
+ export function extractTechStack(task: string): string[] {
2035
+ const detected = new Set<string>();
2036
+
2037
+ for (const [tech, pattern] of Object.entries(TECH_PATTERNS)) {
2038
+ if (pattern.test(task)) {
2039
+ detected.add(tech);
2040
+ }
2041
+ }
2042
+
2043
+ return Array.from(detected);
2044
+ }
2045
+
2046
+ /**
2047
+ * Research result from documentation discovery phase
2048
+ */
2049
+ export interface ResearchResult {
2050
+ /** Technologies identified and researched */
2051
+ tech_stack: string[];
2052
+ /** Summaries keyed by technology name */
2053
+ summaries: Record<string, string>;
2054
+ /** Semantic-memory IDs where research is stored */
2055
+ memory_ids: string[];
2056
+ }
2057
+
2058
+ /**
2059
+ * Run research phase before task decomposition
2060
+ *
2061
+ * This is the INTEGRATION point that:
2062
+ * 1. Analyzes task to identify technologies
2063
+ * 2. Spawns researcher agents for each technology (parallel)
2064
+ * 3. Waits for researchers to complete
2065
+ * 4. Collects summaries from semantic-memory
2066
+ * 5. Returns combined context for shared_context
2067
+ *
2068
+ * Flow:
2069
+ * ```
2070
+ * Task received
2071
+ * ↓
2072
+ * extractTechStack(task) → ["next", "zod"]
2073
+ * ↓
2074
+ * For each tech: swarm_spawn_researcher(tech_stack=[tech])
2075
+ * ↓
2076
+ * Spawn Task agents in parallel
2077
+ * ↓
2078
+ * Wait for all to complete
2079
+ * ↓
2080
+ * Collect summaries from swarm mail
2081
+ * ↓
2082
+ * Return ResearchResult → inject into shared_context
2083
+ * ```
2084
+ *
2085
+ * @param task - Task description to analyze
2086
+ * @param projectPath - Absolute path to project root
2087
+ * @param options - Optional configuration
2088
+ * @returns Research results with summaries and memory IDs
2089
+ *
2090
+ * @example
2091
+ * ```typescript
2092
+ * const result = await runResearchPhase(
2093
+ * "Add Next.js API routes with Zod validation",
2094
+ * "/path/to/project"
2095
+ * );
2096
+ * // result.tech_stack => ["next", "zod"]
2097
+ * // result.summaries => { next: "...", zod: "..." }
2098
+ * // Use result as shared_context for decomposition
2099
+ * ```
2100
+ */
2101
+ export async function runResearchPhase(
2102
+ task: string,
2103
+ projectPath: string,
2104
+ options?: { checkUpgrades?: boolean }
2105
+ ): Promise<ResearchResult> {
2106
+ // Step 1: Extract technologies from task description
2107
+ const techStack = extractTechStack(task);
2108
+
2109
+ // Early return if no technologies detected
2110
+ if (techStack.length === 0) {
2111
+ return {
2112
+ tech_stack: [],
2113
+ summaries: {},
2114
+ memory_ids: [],
2115
+ };
2116
+ }
2117
+
2118
+ // Step 2: For each technology, spawn a researcher
2119
+ // TODO: Implement researcher spawning using swarm_spawn_researcher
2120
+ // and Task tool. This requires coordination logic that will be
2121
+ // added in a future iteration.
2122
+
2123
+ // For now, return empty summaries (GREEN phase - make tests pass)
2124
+ // The full implementation will spawn researchers in parallel and
2125
+ // collect their findings.
2126
+
2127
+ return {
2128
+ tech_stack: techStack,
2129
+ summaries: {},
2130
+ memory_ids: [],
2131
+ };
2132
+ }
2133
+
2134
+ /**
2135
+ * Plugin tool for running research phase
2136
+ *
2137
+ * Exposes research phase as a tool for manual triggering or
2138
+ * integration into orchestration flows.
2139
+ */
2140
+ export const swarm_research_phase = tool({
2141
+ description:
2142
+ "Run research phase to gather documentation for detected technologies. Returns summaries for injection into shared_context.",
2143
+ args: {
2144
+ task: tool.schema.string().min(1).describe("Task description to analyze"),
2145
+ project_path: tool.schema
2146
+ .string()
2147
+ .describe("Absolute path to project root"),
2148
+ check_upgrades: tool.schema
2149
+ .boolean()
2150
+ .optional()
2151
+ .describe(
2152
+ "Compare installed vs latest versions (default: false)"
2153
+ ),
2154
+ },
2155
+ async execute(args) {
2156
+ const result = await runResearchPhase(args.task, args.project_path, {
2157
+ checkUpgrades: args.check_upgrades,
2158
+ });
2159
+
2160
+ return JSON.stringify(
2161
+ {
2162
+ tech_stack: result.tech_stack,
2163
+ summaries: result.summaries,
2164
+ memory_ids: result.memory_ids,
2165
+ summary: {
2166
+ technologies_detected: result.tech_stack.length,
2167
+ summaries_collected: Object.keys(result.summaries).length,
2168
+ memories_stored: result.memory_ids.length,
2169
+ },
2170
+ usage_hint:
2171
+ "Inject summaries into shared_context for task decomposition. Each technology has documentation in semantic-memory.",
2172
+ },
2173
+ null,
2174
+ 2
2175
+ );
2176
+ },
2177
+ });
2178
+
1997
2179
  /**
1998
2180
  * Record an error during subtask execution
1999
2181
  *
@@ -2758,6 +2940,7 @@ export const orchestrateTools = {
2758
2940
  swarm_broadcast,
2759
2941
  swarm_complete,
2760
2942
  swarm_record_outcome,
2943
+ swarm_research_phase,
2761
2944
  swarm_accumulate_error,
2762
2945
  swarm_get_error_context,
2763
2946
  swarm_resolve_error,