claude-code-swarm 0.3.3 → 0.3.5

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 (273) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +22 -1
  3. package/.claude-plugin/run-agent-inbox-mcp.sh +76 -0
  4. package/.claude-plugin/run-minimem-mcp.sh +98 -0
  5. package/.claude-plugin/run-opentasks-mcp.sh +65 -0
  6. package/CLAUDE.md +200 -36
  7. package/README.md +65 -0
  8. package/e2e/helpers/cleanup.mjs +17 -3
  9. package/e2e/helpers/map-mock-server.mjs +201 -25
  10. package/e2e/helpers/sidecar.mjs +222 -0
  11. package/e2e/helpers/workspace.mjs +2 -1
  12. package/e2e/tier5-sidecar-inbox.test.mjs +900 -0
  13. package/e2e/tier6-inbox-mcp.test.mjs +173 -0
  14. package/e2e/tier6-live-agent.test.mjs +759 -0
  15. package/e2e/vitest.config.e2e.mjs +1 -1
  16. package/hooks/hooks.json +15 -8
  17. package/package.json +13 -1
  18. package/references/agent-inbox/CLAUDE.md +151 -0
  19. package/references/agent-inbox/README.md +238 -0
  20. package/references/agent-inbox/docs/CLAUDE-CODE-SWARM-PROPOSAL.md +137 -0
  21. package/references/agent-inbox/docs/DESIGN.md +1156 -0
  22. package/references/agent-inbox/hooks/inbox-hook.mjs +119 -0
  23. package/references/agent-inbox/hooks/register-hook.mjs +69 -0
  24. package/references/agent-inbox/package-lock.json +3347 -0
  25. package/references/agent-inbox/package.json +58 -0
  26. package/references/agent-inbox/rules/agent-inbox.md +78 -0
  27. package/references/agent-inbox/src/federation/address.ts +61 -0
  28. package/references/agent-inbox/src/federation/connection-manager.ts +573 -0
  29. package/references/agent-inbox/src/federation/delivery-queue.ts +222 -0
  30. package/references/agent-inbox/src/federation/index.ts +6 -0
  31. package/references/agent-inbox/src/federation/routing-engine.ts +188 -0
  32. package/references/agent-inbox/src/federation/trust.ts +71 -0
  33. package/references/agent-inbox/src/index.ts +390 -0
  34. package/references/agent-inbox/src/ipc/ipc-server.ts +207 -0
  35. package/references/agent-inbox/src/jsonrpc/mail-server.ts +382 -0
  36. package/references/agent-inbox/src/map/map-client.ts +414 -0
  37. package/references/agent-inbox/src/mcp/mcp-server.ts +272 -0
  38. package/references/agent-inbox/src/mesh/delivery-bridge.ts +110 -0
  39. package/references/agent-inbox/src/mesh/mesh-connector.ts +41 -0
  40. package/references/agent-inbox/src/mesh/mesh-transport.ts +157 -0
  41. package/references/agent-inbox/src/mesh/type-mapper.ts +239 -0
  42. package/references/agent-inbox/src/push/notifier.ts +233 -0
  43. package/references/agent-inbox/src/registry/warm-registry.ts +255 -0
  44. package/references/agent-inbox/src/router/message-router.ts +175 -0
  45. package/references/agent-inbox/src/storage/interface.ts +48 -0
  46. package/references/agent-inbox/src/storage/memory.ts +145 -0
  47. package/references/agent-inbox/src/storage/sqlite.ts +671 -0
  48. package/references/agent-inbox/src/traceability/traceability.ts +183 -0
  49. package/references/agent-inbox/src/types.ts +303 -0
  50. package/references/agent-inbox/test/federation/address.test.ts +101 -0
  51. package/references/agent-inbox/test/federation/connection-manager.test.ts +546 -0
  52. package/references/agent-inbox/test/federation/delivery-queue.test.ts +159 -0
  53. package/references/agent-inbox/test/federation/integration.test.ts +857 -0
  54. package/references/agent-inbox/test/federation/routing-engine.test.ts +117 -0
  55. package/references/agent-inbox/test/federation/sdk-integration.test.ts +744 -0
  56. package/references/agent-inbox/test/federation/trust.test.ts +89 -0
  57. package/references/agent-inbox/test/ipc-jsonrpc.test.ts +113 -0
  58. package/references/agent-inbox/test/ipc-server.test.ts +197 -0
  59. package/references/agent-inbox/test/mail-server.test.ts +285 -0
  60. package/references/agent-inbox/test/map-client.test.ts +408 -0
  61. package/references/agent-inbox/test/mesh/delivery-bridge.test.ts +178 -0
  62. package/references/agent-inbox/test/mesh/e2e-mesh.test.ts +527 -0
  63. package/references/agent-inbox/test/mesh/e2e-real-meshpeer.test.ts +629 -0
  64. package/references/agent-inbox/test/mesh/federation-mesh.test.ts +269 -0
  65. package/references/agent-inbox/test/mesh/mesh-connector.test.ts +66 -0
  66. package/references/agent-inbox/test/mesh/mesh-transport.test.ts +191 -0
  67. package/references/agent-inbox/test/mesh/meshpeer-integration.test.ts +442 -0
  68. package/references/agent-inbox/test/mesh/mock-mesh.ts +125 -0
  69. package/references/agent-inbox/test/mesh/mock-meshpeer.ts +266 -0
  70. package/references/agent-inbox/test/mesh/type-mapper.test.ts +226 -0
  71. package/references/agent-inbox/test/message-router.test.ts +184 -0
  72. package/references/agent-inbox/test/push-notifier.test.ts +139 -0
  73. package/references/agent-inbox/test/registry/warm-registry.test.ts +171 -0
  74. package/references/agent-inbox/test/sqlite-prefix.test.ts +192 -0
  75. package/references/agent-inbox/test/sqlite-storage.test.ts +243 -0
  76. package/references/agent-inbox/test/storage.test.ts +196 -0
  77. package/references/agent-inbox/test/traceability.test.ts +123 -0
  78. package/references/agent-inbox/test/wake.test.ts +330 -0
  79. package/references/agent-inbox/tsconfig.json +20 -0
  80. package/references/agent-inbox/tsup.config.ts +10 -0
  81. package/references/agent-inbox/vitest.config.ts +8 -0
  82. package/references/minimem/.claude/settings.json +7 -0
  83. package/references/minimem/.sudocode/issues.jsonl +18 -0
  84. package/references/minimem/.sudocode/specs.jsonl +1 -0
  85. package/references/minimem/CLAUDE.md +329 -0
  86. package/references/minimem/README.md +565 -0
  87. package/references/minimem/claude-plugin/.claude-plugin/plugin.json +10 -0
  88. package/references/minimem/claude-plugin/.mcp.json +7 -0
  89. package/references/minimem/claude-plugin/README.md +158 -0
  90. package/references/minimem/claude-plugin/commands/recall.md +47 -0
  91. package/references/minimem/claude-plugin/commands/remember.md +41 -0
  92. package/references/minimem/claude-plugin/hooks/__tests__/hooks.test.ts +272 -0
  93. package/references/minimem/claude-plugin/hooks/hooks.json +27 -0
  94. package/references/minimem/claude-plugin/hooks/session-end.sh +86 -0
  95. package/references/minimem/claude-plugin/hooks/session-start.sh +85 -0
  96. package/references/minimem/claude-plugin/skills/memory/SKILL.md +108 -0
  97. package/references/minimem/media/banner.png +0 -0
  98. package/references/minimem/package-lock.json +5373 -0
  99. package/references/minimem/package.json +76 -0
  100. package/references/minimem/scripts/postbuild.js +49 -0
  101. package/references/minimem/src/__tests__/edge-cases.test.ts +371 -0
  102. package/references/minimem/src/__tests__/errors.test.ts +265 -0
  103. package/references/minimem/src/__tests__/helpers.ts +199 -0
  104. package/references/minimem/src/__tests__/internal.test.ts +407 -0
  105. package/references/minimem/src/__tests__/knowledge-frontmatter.test.ts +148 -0
  106. package/references/minimem/src/__tests__/knowledge.test.ts +148 -0
  107. package/references/minimem/src/__tests__/minimem.integration.test.ts +1127 -0
  108. package/references/minimem/src/__tests__/session.test.ts +190 -0
  109. package/references/minimem/src/cli/__tests__/commands.test.ts +760 -0
  110. package/references/minimem/src/cli/__tests__/contained-layout.test.ts +286 -0
  111. package/references/minimem/src/cli/commands/__tests__/conflicts.test.ts +141 -0
  112. package/references/minimem/src/cli/commands/append.ts +76 -0
  113. package/references/minimem/src/cli/commands/config.ts +262 -0
  114. package/references/minimem/src/cli/commands/conflicts.ts +415 -0
  115. package/references/minimem/src/cli/commands/daemon.ts +169 -0
  116. package/references/minimem/src/cli/commands/index.ts +12 -0
  117. package/references/minimem/src/cli/commands/init.ts +166 -0
  118. package/references/minimem/src/cli/commands/mcp.ts +221 -0
  119. package/references/minimem/src/cli/commands/push-pull.ts +213 -0
  120. package/references/minimem/src/cli/commands/search.ts +223 -0
  121. package/references/minimem/src/cli/commands/status.ts +84 -0
  122. package/references/minimem/src/cli/commands/store.ts +189 -0
  123. package/references/minimem/src/cli/commands/sync-init.ts +290 -0
  124. package/references/minimem/src/cli/commands/sync.ts +70 -0
  125. package/references/minimem/src/cli/commands/upsert.ts +197 -0
  126. package/references/minimem/src/cli/config.ts +611 -0
  127. package/references/minimem/src/cli/index.ts +299 -0
  128. package/references/minimem/src/cli/shared.ts +189 -0
  129. package/references/minimem/src/cli/sync/__tests__/central.test.ts +152 -0
  130. package/references/minimem/src/cli/sync/__tests__/conflicts.test.ts +209 -0
  131. package/references/minimem/src/cli/sync/__tests__/daemon.test.ts +118 -0
  132. package/references/minimem/src/cli/sync/__tests__/detection.test.ts +207 -0
  133. package/references/minimem/src/cli/sync/__tests__/integration.test.ts +476 -0
  134. package/references/minimem/src/cli/sync/__tests__/registry.test.ts +363 -0
  135. package/references/minimem/src/cli/sync/__tests__/state.test.ts +255 -0
  136. package/references/minimem/src/cli/sync/__tests__/validation.test.ts +193 -0
  137. package/references/minimem/src/cli/sync/__tests__/watcher.test.ts +178 -0
  138. package/references/minimem/src/cli/sync/central.ts +292 -0
  139. package/references/minimem/src/cli/sync/conflicts.ts +205 -0
  140. package/references/minimem/src/cli/sync/daemon.ts +407 -0
  141. package/references/minimem/src/cli/sync/detection.ts +138 -0
  142. package/references/minimem/src/cli/sync/index.ts +107 -0
  143. package/references/minimem/src/cli/sync/operations.ts +373 -0
  144. package/references/minimem/src/cli/sync/registry.ts +279 -0
  145. package/references/minimem/src/cli/sync/state.ts +358 -0
  146. package/references/minimem/src/cli/sync/validation.ts +206 -0
  147. package/references/minimem/src/cli/sync/watcher.ts +237 -0
  148. package/references/minimem/src/cli/version.ts +34 -0
  149. package/references/minimem/src/core/index.ts +9 -0
  150. package/references/minimem/src/core/indexer.ts +628 -0
  151. package/references/minimem/src/core/searcher.ts +221 -0
  152. package/references/minimem/src/db/schema.ts +183 -0
  153. package/references/minimem/src/db/sqlite-vec.ts +24 -0
  154. package/references/minimem/src/embeddings/__tests__/embeddings.test.ts +431 -0
  155. package/references/minimem/src/embeddings/batch-gemini.ts +392 -0
  156. package/references/minimem/src/embeddings/batch-openai.ts +409 -0
  157. package/references/minimem/src/embeddings/embeddings.ts +434 -0
  158. package/references/minimem/src/index.ts +132 -0
  159. package/references/minimem/src/internal.ts +299 -0
  160. package/references/minimem/src/minimem.ts +1291 -0
  161. package/references/minimem/src/search/__tests__/hybrid.test.ts +247 -0
  162. package/references/minimem/src/search/graph.ts +234 -0
  163. package/references/minimem/src/search/hybrid.ts +151 -0
  164. package/references/minimem/src/search/search.ts +256 -0
  165. package/references/minimem/src/server/__tests__/mcp.test.ts +347 -0
  166. package/references/minimem/src/server/__tests__/tools.test.ts +364 -0
  167. package/references/minimem/src/server/mcp.ts +326 -0
  168. package/references/minimem/src/server/tools.ts +720 -0
  169. package/references/minimem/src/session.ts +460 -0
  170. package/references/minimem/src/store/__tests__/manifest.test.ts +177 -0
  171. package/references/minimem/src/store/__tests__/materialize.test.ts +52 -0
  172. package/references/minimem/src/store/__tests__/store-graph.test.ts +228 -0
  173. package/references/minimem/src/store/index.ts +27 -0
  174. package/references/minimem/src/store/manifest.ts +203 -0
  175. package/references/minimem/src/store/materialize.ts +185 -0
  176. package/references/minimem/src/store/store-graph.ts +252 -0
  177. package/references/minimem/tsconfig.json +19 -0
  178. package/references/minimem/tsup.config.ts +26 -0
  179. package/references/minimem/vitest.config.ts +29 -0
  180. package/references/openteams/src/cli/generate.ts +23 -1
  181. package/references/openteams/src/generators/agent-prompt-generator.test.ts +94 -0
  182. package/references/openteams/src/generators/agent-prompt-generator.ts +42 -13
  183. package/references/openteams/src/generators/package-generator.ts +9 -1
  184. package/references/openteams/src/generators/skill-generator.test.ts +28 -0
  185. package/references/openteams/src/generators/skill-generator.ts +10 -4
  186. package/references/skill-tree/.claude/settings.json +6 -0
  187. package/references/skill-tree/.sudocode/issues.jsonl +19 -0
  188. package/references/skill-tree/.sudocode/specs.jsonl +3 -0
  189. package/references/skill-tree/CLAUDE.md +132 -0
  190. package/references/skill-tree/README.md +396 -0
  191. package/references/skill-tree/docs/GAPS_v1.md +221 -0
  192. package/references/skill-tree/docs/INTEGRATION_PLAN.md +467 -0
  193. package/references/skill-tree/docs/TODOS.md +91 -0
  194. package/references/skill-tree/docs/anthropic_skill_guide.md +1364 -0
  195. package/references/skill-tree/docs/design/federated-skill-trees.md +524 -0
  196. package/references/skill-tree/docs/design/multi-agent-sync.md +759 -0
  197. package/references/skill-tree/docs/scraper/BRAINSTORM.md +583 -0
  198. package/references/skill-tree/docs/scraper/POC_PLAN.md +420 -0
  199. package/references/skill-tree/docs/scraper/README.md +170 -0
  200. package/references/skill-tree/examples/basic-usage.ts +157 -0
  201. package/references/skill-tree/package-lock.json +1852 -0
  202. package/references/skill-tree/package.json +66 -0
  203. package/references/skill-tree/plan.md +78 -0
  204. package/references/skill-tree/scraper/README.md +123 -0
  205. package/references/skill-tree/scraper/docs/DESIGN.md +683 -0
  206. package/references/skill-tree/scraper/docs/PLAN.md +336 -0
  207. package/references/skill-tree/scraper/drizzle.config.ts +10 -0
  208. package/references/skill-tree/scraper/package-lock.json +6329 -0
  209. package/references/skill-tree/scraper/package.json +68 -0
  210. package/references/skill-tree/scraper/test/fixtures/invalid-skill/missing-description.md +7 -0
  211. package/references/skill-tree/scraper/test/fixtures/invalid-skill/missing-name.md +7 -0
  212. package/references/skill-tree/scraper/test/fixtures/minimal-skill/SKILL.md +27 -0
  213. package/references/skill-tree/scraper/test/fixtures/skill-json/SKILL.json +21 -0
  214. package/references/skill-tree/scraper/test/fixtures/skill-with-meta/SKILL.md +54 -0
  215. package/references/skill-tree/scraper/test/fixtures/skill-with-meta/_meta.json +24 -0
  216. package/references/skill-tree/scraper/test/fixtures/valid-skill/SKILL.md +93 -0
  217. package/references/skill-tree/scraper/test/fixtures/valid-skill/_meta.json +22 -0
  218. package/references/skill-tree/scraper/tsup.config.ts +14 -0
  219. package/references/skill-tree/scraper/vitest.config.ts +17 -0
  220. package/references/skill-tree/scripts/convert-to-vitest.ts +166 -0
  221. package/references/skill-tree/skills/skill-writer/SKILL.md +339 -0
  222. package/references/skill-tree/skills/skill-writer/references/examples.md +326 -0
  223. package/references/skill-tree/skills/skill-writer/references/patterns.md +210 -0
  224. package/references/skill-tree/skills/skill-writer/references/quality-checklist.md +123 -0
  225. package/references/skill-tree/test/run-all.ts +106 -0
  226. package/references/skill-tree/test/utils.ts +128 -0
  227. package/references/skill-tree/vitest.config.ts +16 -0
  228. package/references/swarmkit/src/commands/init/phases/configure.ts +0 -22
  229. package/references/swarmkit/src/commands/init/phases/global-setup.ts +5 -3
  230. package/references/swarmkit/src/commands/init/wizard.ts +2 -2
  231. package/references/swarmkit/src/packages/setup.test.ts +53 -7
  232. package/references/swarmkit/src/packages/setup.ts +37 -1
  233. package/scripts/bootstrap.mjs +26 -1
  234. package/scripts/generate-agents.mjs +5 -1
  235. package/scripts/map-hook.mjs +97 -64
  236. package/scripts/map-sidecar.mjs +179 -25
  237. package/scripts/team-loader.mjs +12 -41
  238. package/skills/swarm/SKILL.md +89 -25
  239. package/src/__tests__/agent-generator.test.mjs +6 -13
  240. package/src/__tests__/bootstrap.test.mjs +124 -1
  241. package/src/__tests__/config.test.mjs +200 -27
  242. package/src/__tests__/e2e-live-map.test.mjs +536 -0
  243. package/src/__tests__/e2e-mesh-sidecar.test.mjs +570 -0
  244. package/src/__tests__/e2e-native-task-hooks.test.mjs +376 -0
  245. package/src/__tests__/e2e-sidecar-bridge.test.mjs +477 -0
  246. package/src/__tests__/helpers.mjs +13 -0
  247. package/src/__tests__/inbox.test.mjs +22 -89
  248. package/src/__tests__/index.test.mjs +35 -9
  249. package/src/__tests__/integration.test.mjs +513 -0
  250. package/src/__tests__/map-events.test.mjs +514 -150
  251. package/src/__tests__/mesh-connection.test.mjs +308 -0
  252. package/src/__tests__/opentasks-client.test.mjs +517 -0
  253. package/src/__tests__/paths.test.mjs +185 -41
  254. package/src/__tests__/sidecar-client.test.mjs +35 -0
  255. package/src/__tests__/sidecar-server.test.mjs +124 -0
  256. package/src/__tests__/skilltree-client.test.mjs +80 -0
  257. package/src/agent-generator.mjs +104 -33
  258. package/src/bootstrap.mjs +150 -10
  259. package/src/config.mjs +81 -17
  260. package/src/context-output.mjs +58 -8
  261. package/src/inbox.mjs +9 -54
  262. package/src/index.mjs +39 -8
  263. package/src/map-connection.mjs +4 -3
  264. package/src/map-events.mjs +350 -80
  265. package/src/mesh-connection.mjs +148 -0
  266. package/src/opentasks-client.mjs +269 -0
  267. package/src/paths.mjs +182 -27
  268. package/src/sessionlog.mjs +14 -9
  269. package/src/sidecar-client.mjs +81 -27
  270. package/src/sidecar-server.mjs +175 -16
  271. package/src/skilltree-client.mjs +173 -0
  272. package/src/template.mjs +68 -4
  273. package/vitest.config.mjs +1 -0
@@ -45,24 +45,23 @@ export function parseBasicYaml(content) {
45
45
  /**
46
46
  * Determine which tools an agent needs based on role name, manifest, and position.
47
47
  */
48
- export function determineTools(roleName, manifest, position) {
48
+ export function determineTools(roleName, manifest, position, options = {}) {
49
49
  const tools = ["Read", "Glob", "Grep", "Bash"];
50
50
 
51
- // All team agents get native team coordination tools
52
- tools.push("TaskList", "TaskUpdate", "SendMessage");
53
-
54
- // Root and companions get full tool access + Agent spawning + task creation
55
- if (position === "root" || position === "companion") {
56
- tools.push("Write", "Edit", "Agent", "TaskCreate");
51
+ if (options.opentasksEnabled) {
52
+ tools.push("SendMessage");
53
+ } else {
54
+ tools.push("TaskList", "TaskUpdate", "SendMessage");
55
+ if (position === "root" || position === "companion") {
56
+ tools.push("TaskCreate");
57
+ }
57
58
  }
58
59
 
59
- // Check spawn rules — if this role can spawn others, it needs Agent tool
60
- const spawnRules = manifest.topology?.spawn_rules?.[roleName];
61
- if (spawnRules && spawnRules.length > 0 && !tools.includes("Agent")) {
62
- tools.push("Agent");
60
+ if (position === "root" || position === "companion") {
61
+ tools.push("Write", "Edit");
63
62
  }
64
63
 
65
- // Roles that sound like they write code get write tools
64
+ // Keep existing writeRoles logic for code-writing roles
66
65
  const writeRoles = [
67
66
  "executor",
68
67
  "developer",
@@ -89,6 +88,9 @@ export function generateAgentMd({
89
88
  tools,
90
89
  skillContent,
91
90
  manifest,
91
+ opentasksEnabled,
92
+ minimemEnabled,
93
+ skillLoadout,
92
94
  }) {
93
95
  const lines = [];
94
96
 
@@ -177,26 +179,69 @@ export function generateAgentMd({
177
179
  lines.push("");
178
180
  }
179
181
 
180
- // Task management via native tools
181
- lines.push("### Task Management");
182
- lines.push("");
183
- lines.push("Use Claude Code's native task tools:");
184
- lines.push(
185
- "- **TaskList** — check available tasks and their status"
186
- );
187
- lines.push(
188
- "- **TaskUpdate** — claim tasks (set owner to your name), update status to in_progress or completed"
189
- );
190
- if (position === "root" || position === "companion") {
182
+ // Task management section
183
+ if (opentasksEnabled) {
184
+ lines.push("### Task Management (via opentasks)");
185
+ lines.push("");
186
+ lines.push("Use **opentasks MCP tools** for all task management:");
187
+ lines.push(
188
+ "- **opentasks__list_tasks** — check available tasks and their status"
189
+ );
190
+ lines.push(
191
+ "- **opentasks__update_task** — claim tasks (set assignee to your name), update status"
192
+ );
193
+ if (position === "root" || position === "companion") {
194
+ lines.push(
195
+ "- **opentasks__create_task** — create new tasks for the team when you identify additional work"
196
+ );
197
+ }
198
+ lines.push("");
199
+ lines.push(
200
+ "After completing a task, mark it completed via opentasks__update_task, then check opentasks__list_tasks for your next assignment."
201
+ );
202
+ lines.push("");
203
+ } else {
204
+ lines.push("### Task Management");
205
+ lines.push("");
206
+ lines.push("Use Claude Code's native task tools:");
191
207
  lines.push(
192
- "- **TaskCreate** — create new tasks for the team when you identify additional work"
208
+ "- **TaskList** — check available tasks and their status"
193
209
  );
210
+ lines.push(
211
+ "- **TaskUpdate** — claim tasks (set owner to your name), update status to in_progress or completed"
212
+ );
213
+ if (position === "root" || position === "companion") {
214
+ lines.push(
215
+ "- **TaskCreate** — create new tasks for the team when you identify additional work"
216
+ );
217
+ }
218
+ lines.push("");
219
+ lines.push(
220
+ "After completing a task, mark it completed with TaskUpdate, then check TaskList for your next assignment."
221
+ );
222
+ lines.push("");
223
+ }
224
+
225
+ // Memory section (minimem)
226
+ if (minimemEnabled) {
227
+ lines.push("### Memory");
228
+ lines.push("");
229
+ lines.push("Use **minimem MCP tools** to recall and store knowledge:");
230
+ lines.push("- **minimem__memory_search** — search past decisions, context, patterns");
231
+ lines.push("- **minimem__memory_get_details** — get full text for a search result");
232
+ lines.push("- **minimem__knowledge_search** — search with domain/entity filters");
233
+ lines.push("");
234
+ lines.push("Before starting major work, search memory for relevant prior context.");
235
+ lines.push("");
236
+ }
237
+
238
+ // Skills section (skill-tree loadout)
239
+ if (skillLoadout) {
240
+ lines.push("## Skills");
241
+ lines.push("");
242
+ lines.push(skillLoadout);
243
+ lines.push("");
194
244
  }
195
- lines.push("");
196
- lines.push(
197
- "After completing a task, mark it completed with TaskUpdate, then check TaskList for your next assignment."
198
- );
199
- lines.push("");
200
245
 
201
246
  // Optional MAP note
202
247
  lines.push("### External Observability (MAP)");
@@ -216,7 +261,7 @@ export function generateAgentMd({
216
261
  * Uses openteams TemplateLoader when available, falls back to basic YAML parsing.
217
262
  * Returns { success, roles: string[], error? }.
218
263
  */
219
- export async function generateAllAgents(templateDir, outputDir) {
264
+ export async function generateAllAgents(templateDir, outputDir, options = {}) {
220
265
  const absTemplateDir = path.resolve(templateDir);
221
266
  const absOutputDir = path.resolve(outputDir);
222
267
 
@@ -236,13 +281,26 @@ export async function generateAllAgents(templateDir, outputDir) {
236
281
  TemplateLoader = null;
237
282
  }
238
283
 
284
+ // Load skill loadouts if available (compiled by skilltree-client during team loading)
285
+ let skillLoadouts = {};
286
+ const loadoutsPath = path.join(absOutputDir, "..", "skill-loadouts.json");
287
+ if (fs.existsSync(loadoutsPath)) {
288
+ try {
289
+ skillLoadouts = JSON.parse(fs.readFileSync(loadoutsPath, "utf-8"));
290
+ } catch { /* ignore */ }
291
+ }
292
+
239
293
  const generatedRoles = [];
240
294
 
241
295
  if (TemplateLoader) {
242
296
  const template = TemplateLoader.load(absTemplateDir);
243
297
  const teamName = template.manifest.name;
244
298
  const manifest = template.manifest;
245
- const roleSkillMds = generateAllRoleSkillMds(template, { teamName });
299
+ const roleSkillMds = generateAllRoleSkillMds(template, {
300
+ teamName,
301
+ includeSpawnSection: false,
302
+ includeCliSection: false,
303
+ });
246
304
 
247
305
  for (const roleSkill of roleSkillMds) {
248
306
  const roleName = roleSkill.role;
@@ -261,7 +319,7 @@ export async function generateAllAgents(templateDir, outputDir) {
261
319
  model = comp?.config?.model;
262
320
  }
263
321
 
264
- const tools = determineTools(roleName, manifest, position);
322
+ const tools = determineTools(roleName, manifest, position, options);
265
323
 
266
324
  const agentMd = generateAgentMd({
267
325
  roleName,
@@ -272,6 +330,9 @@ export async function generateAllAgents(templateDir, outputDir) {
272
330
  tools,
273
331
  skillContent: roleSkill.content,
274
332
  manifest,
333
+ opentasksEnabled: options.opentasksEnabled,
334
+ minimemEnabled: options.minimemEnabled,
335
+ skillLoadout: skillLoadouts[roleName] || "",
275
336
  });
276
337
 
277
338
  const agentDir = path.join(absOutputDir, roleName);
@@ -302,12 +363,22 @@ export async function generateAllAgents(templateDir, outputDir) {
302
363
  prompt = fs.readFileSync(promptFile, "utf-8");
303
364
  }
304
365
 
366
+ const fallbackTools = ["Read", "Write", "Edit", "Bash", "Glob", "Grep", "Agent"];
367
+ if (options.opentasksEnabled) {
368
+ fallbackTools.push("SendMessage");
369
+ } else {
370
+ fallbackTools.push("TaskList", "TaskUpdate", "TaskCreate", "SendMessage");
371
+ }
372
+
305
373
  const agentMd = generateAgentMd({
306
374
  roleName,
307
375
  teamName: manifest.name,
308
376
  position: "spawned",
309
377
  description: `${roleName} agent in the ${manifest.name} team`,
310
- tools: ["Read", "Write", "Edit", "Bash", "Glob", "Grep", "Agent", "TaskList", "TaskUpdate", "TaskCreate", "SendMessage"],
378
+ tools: fallbackTools,
379
+ opentasksEnabled: options.opentasksEnabled,
380
+ minimemEnabled: options.minimemEnabled,
381
+ skillLoadout: skillLoadouts[roleName] || "",
311
382
  skillContent: prompt
312
383
  ? `# Role: ${roleName}\n\nMember of the **${manifest.name}** team.\n\n## Instructions\n\n${prompt}`
313
384
  : `# Role: ${roleName}\n\nMember of the **${manifest.name}** team.`,
package/src/bootstrap.mjs CHANGED
@@ -5,12 +5,17 @@
5
5
  * packages via swarmkit (openteams, MAP SDK, sessionlog), starts MAP
6
6
  * sidecar if configured, runs initial sessionlog sync.
7
7
  * Returns context object for formatting.
8
+ *
9
+ * Supports per-session sidecars: when sessionId is provided, each session
10
+ * gets its own sidecar process with isolated socket/pid/inbox paths.
8
11
  */
9
12
 
10
13
  import fs from "fs";
11
14
  import { execSync } from "child_process";
12
15
  import { readConfig, resolveScope } from "./config.mjs";
13
- import { SOCKET_PATH, PID_PATH, MAP_DIR, SIDECAR_LOG_PATH, pluginDir, ensureSwarmDir } from "./paths.mjs";
16
+ import { SOCKET_PATH, MAP_DIR, pluginDir, ensureSwarmDir, ensureOpentasksDir, ensureSessionDir, listSessionDirs } from "./paths.mjs";
17
+ import { findSocketPath, isDaemonAlive, ensureDaemon } from "./opentasks-client.mjs";
18
+ import { loadTeam } from "./template.mjs";
14
19
  import { killSidecar, startSidecar } from "./sidecar-client.mjs";
15
20
  import { checkSessionlogStatus, syncSessionlog } from "./sessionlog.mjs";
16
21
  import { resolveSwarmkit, configureNodePath } from "./swarmkit-resolver.mjs";
@@ -47,6 +52,21 @@ function getRequiredGlobalPackages(config) {
47
52
  if (config.sessionlog.enabled) {
48
53
  packages.push("sessionlog");
49
54
  }
55
+ if (config.opentasks?.enabled) {
56
+ packages.push("opentasks");
57
+ }
58
+ if (config.inbox?.enabled) {
59
+ packages.push("agent-inbox");
60
+ }
61
+ if (config.minimem?.enabled) {
62
+ packages.push("minimem");
63
+ }
64
+ if (config.skilltree?.enabled) {
65
+ packages.push("skill-tree");
66
+ }
67
+ if (config.mesh?.enabled) {
68
+ packages.push("agentic-mesh");
69
+ }
50
70
  return packages;
51
71
  }
52
72
 
@@ -142,6 +162,33 @@ async function initSwarmProject(config) {
142
162
  }
143
163
  }
144
164
 
165
+ // Init opentasks project dir (.swarm/opentasks/) if enabled
166
+ if (config.opentasks?.enabled && !swarmkit.isProjectInit(cwd, "opentasks")) {
167
+ try {
168
+ await swarmkit.initProjectPackage("opentasks", ctx);
169
+ } catch {
170
+ // best-effort
171
+ }
172
+ }
173
+
174
+ // Init minimem project dir (.swarm/minimem/) if enabled
175
+ if (config.minimem?.enabled && !swarmkit.isProjectInit(cwd, "minimem")) {
176
+ try {
177
+ await swarmkit.initProjectPackage("minimem", ctx);
178
+ } catch {
179
+ // best-effort
180
+ }
181
+ }
182
+
183
+ // Init skill-tree project dir (.swarm/skill-tree/) if enabled
184
+ if (config.skilltree?.enabled && !swarmkit.isProjectInit(cwd, "skill-tree")) {
185
+ try {
186
+ await swarmkit.initProjectPackage("skill-tree", ctx);
187
+ } catch {
188
+ // best-effort
189
+ }
190
+ }
191
+
145
192
  // Init claude-code-swarm project dir (.swarm/claude-swarm/)
146
193
  if (!swarmkit.isProjectInit(cwd, "claude-code-swarm")) {
147
194
  try {
@@ -153,16 +200,42 @@ async function initSwarmProject(config) {
153
200
  }
154
201
  }
155
202
 
203
+ /**
204
+ * Clean up stale session directories whose sidecar processes have died.
205
+ * Scans MAP_DIR/sessions/ and removes directories with dead PIDs.
206
+ * Best-effort: never throws.
207
+ */
208
+ function cleanupStaleSessions() {
209
+ try {
210
+ for (const { dir, pidPath } of listSessionDirs()) {
211
+ try {
212
+ const pid = parseInt(fs.readFileSync(pidPath, "utf-8").trim());
213
+ process.kill(pid, 0); // throws if process is dead
214
+ // Process is alive — leave this session alone
215
+ } catch {
216
+ // Process is dead or PID file unreadable — clean up
217
+ try {
218
+ fs.rmSync(dir, { recursive: true, force: true });
219
+ } catch {
220
+ // ignore cleanup failures
221
+ }
222
+ }
223
+ }
224
+ } catch {
225
+ // Non-critical
226
+ }
227
+ }
228
+
156
229
  /**
157
230
  * Start the MAP sidecar in session mode.
158
- * Kills any existing sidecar first, then starts a new one.
231
+ * Only kills this session's sidecar (if any), not other sessions'.
159
232
  * Returns status string.
160
233
  */
161
- async function startSessionSidecar(config, scope, dir) {
162
- // Kill any existing sidecar from a previous session
163
- killSidecar();
234
+ async function startSessionSidecar(config, scope, dir, sessionId) {
235
+ // Kill only this session's sidecar (if somehow already running)
236
+ killSidecar(sessionId);
164
237
 
165
- const ok = await startSidecar(config, dir);
238
+ const ok = await startSidecar(config, dir, sessionId);
166
239
  if (ok) {
167
240
  return `connected (scope: ${scope})`;
168
241
  }
@@ -182,8 +255,9 @@ function checkPersistentSidecar(scope) {
182
255
 
183
256
  /**
184
257
  * Full bootstrap flow. Returns context object for formatting.
258
+ * When sessionId is provided, uses per-session sidecar paths.
185
259
  */
186
- export async function bootstrap(pluginDirOverride) {
260
+ export async function bootstrap(pluginDirOverride, sessionId) {
187
261
  const dir = pluginDirOverride || pluginDir();
188
262
 
189
263
  // 0. Install local dependencies (js-yaml, swarmkit)
@@ -203,6 +277,21 @@ export async function bootstrap(pluginDirOverride) {
203
277
 
204
278
  const scope = resolveScope(config);
205
279
 
280
+ // 1e. Load team template if configured (resolve, generate/cache, write roles.json)
281
+ let team = null;
282
+ if (config.template) {
283
+ try {
284
+ const result = await loadTeam(config.template);
285
+ if (result.success) {
286
+ team = result;
287
+ } else {
288
+ process.stderr.write(`[bootstrap] Warning: failed to load template '${config.template}': ${result.error}\n`);
289
+ }
290
+ } catch (err) {
291
+ process.stderr.write(`[bootstrap] Warning: template loading failed: ${err.message}\n`);
292
+ }
293
+ }
294
+
206
295
  // 2. Check sessionlog status
207
296
  let sessionlogStatus = "not installed";
208
297
  if (config.sessionlog.enabled) {
@@ -212,10 +301,16 @@ export async function bootstrap(pluginDirOverride) {
212
301
  // 3. Start MAP sidecar if configured
213
302
  let mapStatus = "disabled";
214
303
  if (config.map.enabled) {
215
- fs.mkdirSync(MAP_DIR, { recursive: true });
304
+ if (sessionId) {
305
+ ensureSessionDir(sessionId);
306
+ } else {
307
+ fs.mkdirSync(MAP_DIR, { recursive: true });
308
+ }
216
309
 
217
310
  if (config.map.sidecar === "session") {
218
- mapStatus = await startSessionSidecar(config, scope, dir);
311
+ // Clean up stale sessions before starting new one
312
+ cleanupStaleSessions();
313
+ mapStatus = await startSessionSidecar(config, scope, dir, sessionId);
219
314
  } else if (config.map.sidecar === "persistent") {
220
315
  mapStatus = checkPersistentSidecar(scope);
221
316
  }
@@ -223,15 +318,60 @@ export async function bootstrap(pluginDirOverride) {
223
318
 
224
319
  // 3b. Initial sessionlog sync (fire and forget)
225
320
  if (config.map.enabled && config.sessionlog.sync !== "off") {
226
- syncSessionlog(config).catch(() => {});
321
+ syncSessionlog(config, sessionId).catch(() => {});
322
+ }
323
+
324
+ // 4. Start opentasks daemon if configured
325
+ let opentasksStatus = "disabled";
326
+ if (config.opentasks?.enabled) {
327
+ ensureOpentasksDir();
328
+ if (config.opentasks?.autoStart) {
329
+ const ok = await ensureDaemon(config);
330
+ opentasksStatus = ok ? "connected" : "starting";
331
+ } else {
332
+ const alive = await isDaemonAlive(findSocketPath());
333
+ opentasksStatus = alive ? "connected" : "not running (autoStart: false)";
334
+ }
335
+ }
336
+
337
+ // 5. Check minimem status if enabled
338
+ let minimemStatus = "disabled";
339
+ if (config.minimem?.enabled) {
340
+ try {
341
+ execSync("minimem status", { stdio: "pipe", timeout: 5000 });
342
+ minimemStatus = "ready";
343
+ } catch {
344
+ minimemStatus = "installed";
345
+ }
346
+ }
347
+
348
+ // 6. Check skill-tree status if enabled
349
+ let skilltreeStatus = "disabled";
350
+ if (config.skilltree?.enabled) {
351
+ try {
352
+ execSync("skill-tree list --json", { stdio: "pipe", timeout: 5000 });
353
+ skilltreeStatus = "ready";
354
+ } catch {
355
+ skilltreeStatus = "installed";
356
+ }
227
357
  }
228
358
 
229
359
  return {
230
360
  template: config.template,
361
+ team,
231
362
  mapEnabled: config.map.enabled,
232
363
  mapStatus,
364
+ meshEnabled: config.mesh?.enabled,
233
365
  sessionlogEnabled: config.sessionlog.enabled,
234
366
  sessionlogStatus,
235
367
  sessionlogSync: config.sessionlog.sync,
368
+ opentasksEnabled: config.opentasks?.enabled,
369
+ opentasksStatus,
370
+ inboxEnabled: config.inbox?.enabled,
371
+ minimemEnabled: config.minimem?.enabled,
372
+ minimemStatus,
373
+ skilltreeEnabled: config.skilltree?.enabled,
374
+ skilltreeStatus,
375
+ sessionId,
236
376
  };
237
377
  }
package/src/config.mjs CHANGED
@@ -1,12 +1,14 @@
1
1
  /**
2
2
  * config.mjs — Shared configuration parsing for claude-code-swarm
3
3
  *
4
- * Reads .swarm/claude/config.json and provides normalized config with defaults.
4
+ * Reads config with priority: env vars > project config > global config > defaults.
5
+ * Project config: .swarm/claude-swarm/config.json (in cwd)
6
+ * Global config: ~/.claude-swarm/config.json
5
7
  * Used by bootstrap, hooks, sidecar, and team-loader.
6
8
  */
7
9
 
8
10
  import fs from "fs";
9
- import { CONFIG_PATH } from "./paths.mjs";
11
+ import { CONFIG_PATH, GLOBAL_CONFIG_PATH } from "./paths.mjs";
10
12
 
11
13
  export const DEFAULTS = {
12
14
  mapServer: "ws://localhost:8080",
@@ -34,40 +36,102 @@ function envStr(name) {
34
36
  }
35
37
 
36
38
  /**
37
- * Read and normalize .swarm/claude/config.json config.
38
- * Priority: SWARM_* env vars > config file > defaults.
39
- * Never throws — returns defaults on any error.
39
+ * Read a JSON config file. Returns empty object on any error.
40
40
  */
41
- export function readConfig(configPath = CONFIG_PATH) {
42
- let raw = {};
41
+ function readJsonFile(filePath) {
43
42
  try {
44
- raw = JSON.parse(fs.readFileSync(configPath, "utf-8"));
43
+ return JSON.parse(fs.readFileSync(filePath, "utf-8"));
45
44
  } catch {
46
- // Missing or invalid config file — raw stays empty, defaults apply
45
+ return {};
47
46
  }
47
+ }
48
48
 
49
- const server = envStr("SWARM_MAP_SERVER") ?? raw.map?.server ?? undefined;
50
- const explicitEnabled = envBool("SWARM_MAP_ENABLED") ?? (raw.map?.enabled === true ? true : undefined);
49
+ /**
50
+ * Read and normalize config with tiered resolution.
51
+ * Priority: SWARM_* env vars > project config file > global config file > defaults.
52
+ * Never throws — returns defaults on any error.
53
+ */
54
+ export function readConfig(configPath = CONFIG_PATH, globalConfigPath = GLOBAL_CONFIG_PATH) {
55
+ const global = readJsonFile(globalConfigPath);
56
+ const project = readJsonFile(configPath);
57
+
58
+ // Project overrides global for each field (not deep merge — per-field fallthrough)
59
+ const server = envStr("SWARM_MAP_SERVER") ?? project.map?.server ?? global.map?.server ?? undefined;
60
+ const explicitEnabled = envBool("SWARM_MAP_ENABLED") ?? (project.map?.enabled === true ? true : undefined) ?? (global.map?.enabled === true ? true : undefined);
51
61
 
52
62
  // MAP is enabled if explicitly set OR if a server is configured
53
63
  const mapEnabled = explicitEnabled ?? (server !== undefined);
54
64
 
55
65
  return {
56
- template: envStr("SWARM_TEMPLATE") ?? raw.template ?? "",
66
+ template: envStr("SWARM_TEMPLATE") ?? project.template ?? global.template ?? "",
57
67
  map: {
58
68
  enabled: mapEnabled,
59
69
  server: server || DEFAULTS.mapServer,
60
- scope: envStr("SWARM_MAP_SCOPE") ?? raw.map?.scope ?? "",
61
- systemId: envStr("SWARM_MAP_SYSTEM_ID") ?? raw.map?.systemId ?? DEFAULTS.mapSystemId,
62
- sidecar: envStr("SWARM_MAP_SIDECAR") ?? raw.map?.sidecar ?? DEFAULTS.mapSidecar,
70
+ scope: envStr("SWARM_MAP_SCOPE") ?? project.map?.scope ?? global.map?.scope ?? "",
71
+ systemId: envStr("SWARM_MAP_SYSTEM_ID") ?? project.map?.systemId ?? global.map?.systemId ?? DEFAULTS.mapSystemId,
72
+ sidecar: envStr("SWARM_MAP_SIDECAR") ?? project.map?.sidecar ?? global.map?.sidecar ?? DEFAULTS.mapSidecar,
73
+ auth: {
74
+ token: envStr("SWARM_MAP_AUTH_TOKEN") ?? project.map?.auth?.token ?? global.map?.auth?.token ?? "",
75
+ param: envStr("SWARM_MAP_AUTH_PARAM") ?? project.map?.auth?.param ?? global.map?.auth?.param ?? "token",
76
+ },
63
77
  },
64
78
  sessionlog: {
65
- enabled: envBool("SWARM_SESSIONLOG_ENABLED") ?? Boolean(raw.sessionlog?.enabled),
66
- sync: envStr("SWARM_SESSIONLOG_SYNC") ?? raw.sessionlog?.sync ?? DEFAULTS.sessionlogSync,
79
+ enabled: envBool("SWARM_SESSIONLOG_ENABLED") ?? Boolean(project.sessionlog?.enabled ?? global.sessionlog?.enabled),
80
+ sync: envStr("SWARM_SESSIONLOG_SYNC") ?? project.sessionlog?.sync ?? global.sessionlog?.sync ?? DEFAULTS.sessionlogSync,
81
+ },
82
+ opentasks: {
83
+ enabled: envBool("SWARM_OPENTASKS_ENABLED") ?? Boolean(project.opentasks?.enabled ?? global.opentasks?.enabled),
84
+ autoStart: envBool("SWARM_OPENTASKS_AUTOSTART") ?? (project.opentasks?.autoStart ?? global.opentasks?.autoStart) !== false,
85
+ scope: envStr("SWARM_OPENTASKS_SCOPE") ?? project.opentasks?.scope ?? global.opentasks?.scope ?? "tasks",
86
+ },
87
+ inbox: {
88
+ enabled: envBool("SWARM_INBOX_ENABLED") ?? project.inbox?.enabled ?? global.inbox?.enabled ?? mapEnabled,
89
+ sqlite: envStr("SWARM_INBOX_SQLITE") ?? project.inbox?.sqlite ?? global.inbox?.sqlite ?? "",
90
+ httpPort: parseInt(envStr("SWARM_INBOX_HTTP_PORT") ?? project.inbox?.httpPort ?? "0", 10) || 0,
91
+ webhooks: project.inbox?.webhooks ?? global.inbox?.webhooks ?? [],
92
+ federation: {
93
+ peers: project.inbox?.federation?.peers ?? global.inbox?.federation?.peers ?? [],
94
+ routing: project.inbox?.federation?.routing ?? global.inbox?.federation?.routing ?? undefined,
95
+ trust: project.inbox?.federation?.trust ?? global.inbox?.federation?.trust ?? undefined,
96
+ },
97
+ },
98
+ minimem: {
99
+ enabled: envBool("SWARM_MINIMEM_ENABLED") ?? Boolean(project.minimem?.enabled ?? global.minimem?.enabled),
100
+ provider: envStr("SWARM_MINIMEM_PROVIDER") ?? project.minimem?.provider ?? global.minimem?.provider ?? "auto",
101
+ global: envBool("SWARM_MINIMEM_GLOBAL") ?? Boolean(project.minimem?.global ?? global.minimem?.global),
102
+ dir: envStr("SWARM_MINIMEM_DIR") ?? project.minimem?.dir ?? global.minimem?.dir ?? "",
103
+ },
104
+ skilltree: {
105
+ enabled: envBool("SWARM_SKILLTREE_ENABLED") ?? Boolean(project.skilltree?.enabled ?? global.skilltree?.enabled),
106
+ basePath: envStr("SWARM_SKILLTREE_BASE_PATH") ?? project.skilltree?.basePath ?? global.skilltree?.basePath ?? "",
107
+ defaultProfile: envStr("SWARM_SKILLTREE_DEFAULT_PROFILE") ?? project.skilltree?.defaultProfile ?? global.skilltree?.defaultProfile ?? "",
108
+ },
109
+ mesh: {
110
+ enabled: envBool("SWARM_MESH_ENABLED") ?? Boolean(project.mesh?.enabled ?? global.mesh?.enabled),
111
+ peerId: envStr("SWARM_MESH_PEER_ID") ?? project.mesh?.peerId ?? global.mesh?.peerId ?? "",
112
+ mapServer: envStr("SWARM_MESH_MAP_SERVER") ?? project.mesh?.mapServer ?? global.mesh?.mapServer ?? "",
67
113
  },
68
114
  };
69
115
  }
70
116
 
117
+ /**
118
+ * Build the MAP server URL with auth query param if configured.
119
+ * If the token is already in the server URL, returns as-is.
120
+ */
121
+ export function resolveMapServer(config) {
122
+ const server = config.map?.server || DEFAULTS.mapServer;
123
+ const token = config.map?.auth?.token;
124
+ if (!token) return server;
125
+
126
+ // Don't double-add if token is already in the URL
127
+ const url = new URL(server);
128
+ const param = config.map?.auth?.param || "token";
129
+ if (url.searchParams.has(param)) return server;
130
+
131
+ url.searchParams.set(param, token);
132
+ return url.toString();
133
+ }
134
+
71
135
  /**
72
136
  * Derive MAP scope from config.
73
137
  * Priority: explicit scope > swarm:<template> > swarm:default