macro-agent 0.1.1 → 0.1.2

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 (406) hide show
  1. package/.sudocode/issues.jsonl +28 -0
  2. package/.sudocode/specs.jsonl +4 -0
  3. package/CLAUDE.md +9 -3
  4. package/dist/agent/agent-manager.d.ts.map +1 -1
  5. package/dist/agent/agent-manager.js +111 -48
  6. package/dist/agent/agent-manager.js.map +1 -1
  7. package/dist/agent/types.d.ts +7 -0
  8. package/dist/agent/types.d.ts.map +1 -1
  9. package/dist/agent/types.js.map +1 -1
  10. package/dist/api/server.d.ts +5 -1
  11. package/dist/api/server.d.ts.map +1 -1
  12. package/dist/api/server.js +100 -3
  13. package/dist/api/server.js.map +1 -1
  14. package/dist/api/types.d.ts +1 -1
  15. package/dist/api/types.d.ts.map +1 -1
  16. package/dist/cli/acp.d.ts.map +1 -1
  17. package/dist/cli/acp.js +71 -1
  18. package/dist/cli/acp.js.map +1 -1
  19. package/dist/cli/index.js +5 -1
  20. package/dist/cli/index.js.map +1 -1
  21. package/dist/cli/mcp.js +27 -8
  22. package/dist/cli/mcp.js.map +1 -1
  23. package/dist/config/project-config.d.ts +13 -2
  24. package/dist/config/project-config.d.ts.map +1 -1
  25. package/dist/config/project-config.js +12 -2
  26. package/dist/config/project-config.js.map +1 -1
  27. package/dist/index.d.ts +1 -0
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +2 -0
  30. package/dist/index.js.map +1 -1
  31. package/dist/lifecycle/handlers/index.d.ts +7 -3
  32. package/dist/lifecycle/handlers/index.d.ts.map +1 -1
  33. package/dist/lifecycle/handlers/index.js +25 -8
  34. package/dist/lifecycle/handlers/index.js.map +1 -1
  35. package/dist/lifecycle/types.d.ts +2 -0
  36. package/dist/lifecycle/types.d.ts.map +1 -1
  37. package/dist/lifecycle/types.js.map +1 -1
  38. package/dist/map/adapter/extensions/index.d.ts +4 -1
  39. package/dist/map/adapter/extensions/index.d.ts.map +1 -1
  40. package/dist/map/adapter/extensions/index.js +27 -0
  41. package/dist/map/adapter/extensions/index.js.map +1 -1
  42. package/dist/map/adapter/extensions/streams.d.ts +95 -0
  43. package/dist/map/adapter/extensions/streams.d.ts.map +1 -0
  44. package/dist/map/adapter/extensions/streams.js +515 -0
  45. package/dist/map/adapter/extensions/streams.js.map +1 -0
  46. package/dist/map/adapter/index.d.ts +1 -1
  47. package/dist/map/adapter/index.d.ts.map +1 -1
  48. package/dist/map/adapter/index.js +3 -1
  49. package/dist/map/adapter/index.js.map +1 -1
  50. package/dist/map/adapter/types.d.ts +1 -1
  51. package/dist/map/adapter/types.d.ts.map +1 -1
  52. package/dist/mcp/mcp-server.d.ts +2 -0
  53. package/dist/mcp/mcp-server.d.ts.map +1 -1
  54. package/dist/mcp/mcp-server.js +12 -3
  55. package/dist/mcp/mcp-server.js.map +1 -1
  56. package/dist/mcp/tools/done.d.ts.map +1 -1
  57. package/dist/mcp/tools/done.js +18 -0
  58. package/dist/mcp/tools/done.js.map +1 -1
  59. package/dist/roles/builtin/coordinator.d.ts.map +1 -1
  60. package/dist/roles/builtin/coordinator.js +2 -1
  61. package/dist/roles/builtin/coordinator.js.map +1 -1
  62. package/dist/roles/builtin/integrator.d.ts.map +1 -1
  63. package/dist/roles/builtin/integrator.js +2 -1
  64. package/dist/roles/builtin/integrator.js.map +1 -1
  65. package/dist/roles/builtin/worker.d.ts.map +1 -1
  66. package/dist/roles/builtin/worker.js +3 -1
  67. package/dist/roles/builtin/worker.js.map +1 -1
  68. package/dist/roles/capabilities.d.ts +6 -0
  69. package/dist/roles/capabilities.d.ts.map +1 -1
  70. package/dist/roles/capabilities.js +10 -0
  71. package/dist/roles/capabilities.js.map +1 -1
  72. package/dist/roles/config-loader.d.ts +1 -1
  73. package/dist/roles/config-loader.d.ts.map +1 -1
  74. package/dist/roles/config-loader.js +3 -2
  75. package/dist/roles/config-loader.js.map +1 -1
  76. package/dist/roles/types.d.ts +3 -1
  77. package/dist/roles/types.d.ts.map +1 -1
  78. package/dist/server/combined-server.d.ts +8 -1
  79. package/dist/server/combined-server.d.ts.map +1 -1
  80. package/dist/server/combined-server.js +6 -2
  81. package/dist/server/combined-server.js.map +1 -1
  82. package/dist/store/event-store.d.ts.map +1 -1
  83. package/dist/store/event-store.js +12 -5
  84. package/dist/store/event-store.js.map +1 -1
  85. package/dist/store/instance.d.ts +1 -1
  86. package/dist/store/instance.d.ts.map +1 -1
  87. package/dist/store/instance.js +2 -2
  88. package/dist/store/instance.js.map +1 -1
  89. package/dist/store/types/agents.d.ts +5 -0
  90. package/dist/store/types/agents.d.ts.map +1 -1
  91. package/dist/task/backend/opentasks/daemon-manager.d.ts.map +1 -1
  92. package/dist/task/backend/opentasks/daemon-manager.js +1 -1
  93. package/dist/task/backend/opentasks/daemon-manager.js.map +1 -1
  94. package/dist/teams/index.d.ts +3 -1
  95. package/dist/teams/index.d.ts.map +1 -1
  96. package/dist/teams/index.js +2 -0
  97. package/dist/teams/index.js.map +1 -1
  98. package/dist/teams/seed-defaults.d.ts +20 -0
  99. package/dist/teams/seed-defaults.d.ts.map +1 -0
  100. package/dist/teams/seed-defaults.js +71 -0
  101. package/dist/teams/seed-defaults.js.map +1 -0
  102. package/dist/teams/team-loader.d.ts +6 -2
  103. package/dist/teams/team-loader.d.ts.map +1 -1
  104. package/dist/teams/team-loader.js +154 -162
  105. package/dist/teams/team-loader.js.map +1 -1
  106. package/dist/teams/team-manager.d.ts +112 -0
  107. package/dist/teams/team-manager.d.ts.map +1 -0
  108. package/dist/teams/team-manager.js +305 -0
  109. package/dist/teams/team-manager.js.map +1 -0
  110. package/dist/teams/team-runtime.d.ts +125 -19
  111. package/dist/teams/team-runtime.d.ts.map +1 -1
  112. package/dist/teams/team-runtime.js +527 -119
  113. package/dist/teams/team-runtime.js.map +1 -1
  114. package/dist/teams/types.d.ts +41 -151
  115. package/dist/teams/types.d.ts.map +1 -1
  116. package/dist/teams/types.js +2 -3
  117. package/dist/teams/types.js.map +1 -1
  118. package/docs/teams.md +73 -0
  119. package/package.json +2 -1
  120. package/references/minimem/.claude/settings.json +7 -0
  121. package/references/minimem/.sudocode/issues.jsonl +18 -0
  122. package/references/minimem/.sudocode/specs.jsonl +1 -0
  123. package/references/minimem/CLAUDE.md +310 -0
  124. package/references/minimem/README.md +562 -0
  125. package/references/minimem/claude-plugin/.claude-plugin/plugin.json +10 -0
  126. package/references/minimem/claude-plugin/.mcp.json +7 -0
  127. package/references/minimem/claude-plugin/README.md +158 -0
  128. package/references/minimem/claude-plugin/commands/recall.md +47 -0
  129. package/references/minimem/claude-plugin/commands/remember.md +41 -0
  130. package/references/minimem/claude-plugin/hooks/__tests__/hooks.test.ts +272 -0
  131. package/references/minimem/claude-plugin/hooks/hooks.json +27 -0
  132. package/references/minimem/claude-plugin/hooks/session-end.sh +86 -0
  133. package/references/minimem/claude-plugin/hooks/session-start.sh +85 -0
  134. package/references/minimem/claude-plugin/skills/memory/SKILL.md +108 -0
  135. package/references/minimem/media/banner.png +0 -0
  136. package/references/minimem/package-lock.json +5373 -0
  137. package/references/minimem/package.json +72 -0
  138. package/references/minimem/scripts/postbuild.js +35 -0
  139. package/references/minimem/src/__tests__/edge-cases.test.ts +371 -0
  140. package/references/minimem/src/__tests__/errors.test.ts +265 -0
  141. package/references/minimem/src/__tests__/helpers.ts +199 -0
  142. package/references/minimem/src/__tests__/internal.test.ts +407 -0
  143. package/references/minimem/src/__tests__/knowledge.test.ts +287 -0
  144. package/references/minimem/src/__tests__/minimem.integration.test.ts +1127 -0
  145. package/references/minimem/src/__tests__/session.test.ts +190 -0
  146. package/references/minimem/src/cli/__tests__/commands.test.ts +759 -0
  147. package/references/minimem/src/cli/commands/__tests__/conflicts.test.ts +141 -0
  148. package/references/minimem/src/cli/commands/append.ts +76 -0
  149. package/references/minimem/src/cli/commands/config.ts +262 -0
  150. package/references/minimem/src/cli/commands/conflicts.ts +413 -0
  151. package/references/minimem/src/cli/commands/daemon.ts +169 -0
  152. package/references/minimem/src/cli/commands/index.ts +12 -0
  153. package/references/minimem/src/cli/commands/init.ts +88 -0
  154. package/references/minimem/src/cli/commands/mcp.ts +177 -0
  155. package/references/minimem/src/cli/commands/push-pull.ts +213 -0
  156. package/references/minimem/src/cli/commands/search.ts +158 -0
  157. package/references/minimem/src/cli/commands/status.ts +84 -0
  158. package/references/minimem/src/cli/commands/sync-init.ts +290 -0
  159. package/references/minimem/src/cli/commands/sync.ts +70 -0
  160. package/references/minimem/src/cli/commands/upsert.ts +197 -0
  161. package/references/minimem/src/cli/config.ts +584 -0
  162. package/references/minimem/src/cli/index.ts +264 -0
  163. package/references/minimem/src/cli/shared.ts +161 -0
  164. package/references/minimem/src/cli/sync/__tests__/central.test.ts +152 -0
  165. package/references/minimem/src/cli/sync/__tests__/conflicts.test.ts +209 -0
  166. package/references/minimem/src/cli/sync/__tests__/daemon.test.ts +118 -0
  167. package/references/minimem/src/cli/sync/__tests__/detection.test.ts +207 -0
  168. package/references/minimem/src/cli/sync/__tests__/integration.test.ts +476 -0
  169. package/references/minimem/src/cli/sync/__tests__/registry.test.ts +363 -0
  170. package/references/minimem/src/cli/sync/__tests__/state.test.ts +255 -0
  171. package/references/minimem/src/cli/sync/__tests__/validation.test.ts +193 -0
  172. package/references/minimem/src/cli/sync/__tests__/watcher.test.ts +178 -0
  173. package/references/minimem/src/cli/sync/central.ts +292 -0
  174. package/references/minimem/src/cli/sync/conflicts.ts +204 -0
  175. package/references/minimem/src/cli/sync/daemon.ts +407 -0
  176. package/references/minimem/src/cli/sync/detection.ts +138 -0
  177. package/references/minimem/src/cli/sync/index.ts +107 -0
  178. package/references/minimem/src/cli/sync/operations.ts +373 -0
  179. package/references/minimem/src/cli/sync/registry.ts +279 -0
  180. package/references/minimem/src/cli/sync/state.ts +355 -0
  181. package/references/minimem/src/cli/sync/validation.ts +206 -0
  182. package/references/minimem/src/cli/sync/watcher.ts +234 -0
  183. package/references/minimem/src/cli/version.ts +34 -0
  184. package/references/minimem/src/core/index.ts +9 -0
  185. package/references/minimem/src/core/indexer.ts +628 -0
  186. package/references/minimem/src/core/searcher.ts +221 -0
  187. package/references/minimem/src/db/schema.ts +183 -0
  188. package/references/minimem/src/db/sqlite-vec.ts +24 -0
  189. package/references/minimem/src/embeddings/__tests__/embeddings.test.ts +431 -0
  190. package/references/minimem/src/embeddings/batch-gemini.ts +392 -0
  191. package/references/minimem/src/embeddings/batch-openai.ts +409 -0
  192. package/references/minimem/src/embeddings/embeddings.ts +434 -0
  193. package/references/minimem/src/index.ts +109 -0
  194. package/references/minimem/src/internal.ts +299 -0
  195. package/references/minimem/src/minimem.ts +1276 -0
  196. package/references/minimem/src/search/__tests__/hybrid.test.ts +247 -0
  197. package/references/minimem/src/search/graph.ts +234 -0
  198. package/references/minimem/src/search/hybrid.ts +151 -0
  199. package/references/minimem/src/search/search.ts +256 -0
  200. package/references/minimem/src/server/__tests__/mcp.test.ts +341 -0
  201. package/references/minimem/src/server/__tests__/tools.test.ts +364 -0
  202. package/references/minimem/src/server/mcp.ts +326 -0
  203. package/references/minimem/src/server/tools.ts +720 -0
  204. package/references/minimem/src/session.ts +460 -0
  205. package/references/minimem/tsconfig.json +19 -0
  206. package/references/minimem/tsup.config.ts +26 -0
  207. package/references/minimem/vitest.config.ts +24 -0
  208. package/references/openteams/.claude/settings.json +6 -0
  209. package/references/openteams/README.md +1 -0
  210. package/references/openteams/SKILL.md +341 -0
  211. package/references/openteams/design.md +411 -0
  212. package/references/openteams/examples/bmad-method/prompts/analyst/ROLE.md +16 -0
  213. package/references/openteams/examples/bmad-method/prompts/analyst/SOUL.md +5 -0
  214. package/references/openteams/examples/bmad-method/prompts/architect/ROLE.md +24 -0
  215. package/references/openteams/examples/bmad-method/prompts/architect/SOUL.md +5 -0
  216. package/references/openteams/examples/bmad-method/prompts/developer/ROLE.md +25 -0
  217. package/references/openteams/examples/bmad-method/prompts/developer/SOUL.md +5 -0
  218. package/references/openteams/examples/bmad-method/prompts/master/ROLE.md +21 -0
  219. package/references/openteams/examples/bmad-method/prompts/master/SOUL.md +5 -0
  220. package/references/openteams/examples/bmad-method/prompts/pm/ROLE.md +20 -0
  221. package/references/openteams/examples/bmad-method/prompts/pm/SOUL.md +5 -0
  222. package/references/openteams/examples/bmad-method/prompts/qa/ROLE.md +17 -0
  223. package/references/openteams/examples/bmad-method/prompts/qa/SOUL.md +5 -0
  224. package/references/openteams/examples/bmad-method/prompts/quick-flow-dev/ROLE.md +23 -0
  225. package/references/openteams/examples/bmad-method/prompts/quick-flow-dev/SOUL.md +5 -0
  226. package/references/openteams/examples/bmad-method/prompts/scrum-master/ROLE.md +27 -0
  227. package/references/openteams/examples/bmad-method/prompts/scrum-master/SOUL.md +5 -0
  228. package/references/openteams/examples/bmad-method/prompts/tech-writer/ROLE.md +21 -0
  229. package/references/openteams/examples/bmad-method/prompts/tech-writer/SOUL.md +5 -0
  230. package/references/openteams/examples/bmad-method/prompts/ux-designer/ROLE.md +16 -0
  231. package/references/openteams/examples/bmad-method/prompts/ux-designer/SOUL.md +5 -0
  232. package/references/openteams/examples/bmad-method/roles/analyst.yaml +9 -0
  233. package/references/openteams/examples/bmad-method/roles/architect.yaml +9 -0
  234. package/references/openteams/examples/bmad-method/roles/developer.yaml +8 -0
  235. package/references/openteams/examples/bmad-method/roles/master.yaml +8 -0
  236. package/references/openteams/examples/bmad-method/roles/pm.yaml +9 -0
  237. package/references/openteams/examples/bmad-method/roles/qa.yaml +8 -0
  238. package/references/openteams/examples/bmad-method/roles/quick-flow-dev.yaml +8 -0
  239. package/references/openteams/examples/bmad-method/roles/scrum-master.yaml +9 -0
  240. package/references/openteams/examples/bmad-method/roles/tech-writer.yaml +8 -0
  241. package/references/openteams/examples/bmad-method/roles/ux-designer.yaml +8 -0
  242. package/references/openteams/examples/bmad-method/team.yaml +161 -0
  243. package/references/openteams/examples/get-shit-done/prompts/codebase-mapper/ROLE.md +17 -0
  244. package/references/openteams/examples/get-shit-done/prompts/codebase-mapper/SOUL.md +5 -0
  245. package/references/openteams/examples/get-shit-done/prompts/debugger/ROLE.md +25 -0
  246. package/references/openteams/examples/get-shit-done/prompts/debugger/SOUL.md +5 -0
  247. package/references/openteams/examples/get-shit-done/prompts/executor/ROLE.md +34 -0
  248. package/references/openteams/examples/get-shit-done/prompts/executor/SOUL.md +5 -0
  249. package/references/openteams/examples/get-shit-done/prompts/integration-checker/ROLE.md +18 -0
  250. package/references/openteams/examples/get-shit-done/prompts/integration-checker/SOUL.md +3 -0
  251. package/references/openteams/examples/get-shit-done/prompts/orchestrator/ROLE.md +42 -0
  252. package/references/openteams/examples/get-shit-done/prompts/orchestrator/SOUL.md +5 -0
  253. package/references/openteams/examples/get-shit-done/prompts/phase-researcher/ROLE.md +15 -0
  254. package/references/openteams/examples/get-shit-done/prompts/phase-researcher/SOUL.md +3 -0
  255. package/references/openteams/examples/get-shit-done/prompts/plan-checker/ROLE.md +17 -0
  256. package/references/openteams/examples/get-shit-done/prompts/plan-checker/SOUL.md +3 -0
  257. package/references/openteams/examples/get-shit-done/prompts/planner/ROLE.md +28 -0
  258. package/references/openteams/examples/get-shit-done/prompts/planner/SOUL.md +5 -0
  259. package/references/openteams/examples/get-shit-done/prompts/project-researcher/ROLE.md +16 -0
  260. package/references/openteams/examples/get-shit-done/prompts/project-researcher/SOUL.md +3 -0
  261. package/references/openteams/examples/get-shit-done/prompts/research-synthesizer/ROLE.md +13 -0
  262. package/references/openteams/examples/get-shit-done/prompts/research-synthesizer/SOUL.md +3 -0
  263. package/references/openteams/examples/get-shit-done/prompts/roadmapper/ROLE.md +14 -0
  264. package/references/openteams/examples/get-shit-done/prompts/roadmapper/SOUL.md +3 -0
  265. package/references/openteams/examples/get-shit-done/prompts/verifier/ROLE.md +19 -0
  266. package/references/openteams/examples/get-shit-done/prompts/verifier/SOUL.md +5 -0
  267. package/references/openteams/examples/get-shit-done/roles/codebase-mapper.yaml +8 -0
  268. package/references/openteams/examples/get-shit-done/roles/debugger.yaml +8 -0
  269. package/references/openteams/examples/get-shit-done/roles/executor.yaml +8 -0
  270. package/references/openteams/examples/get-shit-done/roles/integration-checker.yaml +8 -0
  271. package/references/openteams/examples/get-shit-done/roles/orchestrator.yaml +9 -0
  272. package/references/openteams/examples/get-shit-done/roles/phase-researcher.yaml +7 -0
  273. package/references/openteams/examples/get-shit-done/roles/plan-checker.yaml +8 -0
  274. package/references/openteams/examples/get-shit-done/roles/planner.yaml +8 -0
  275. package/references/openteams/examples/get-shit-done/roles/project-researcher.yaml +8 -0
  276. package/references/openteams/examples/get-shit-done/roles/research-synthesizer.yaml +7 -0
  277. package/references/openteams/examples/get-shit-done/roles/roadmapper.yaml +7 -0
  278. package/references/openteams/examples/get-shit-done/roles/verifier.yaml +8 -0
  279. package/references/openteams/examples/get-shit-done/team.yaml +154 -0
  280. package/references/openteams/package-lock.json +2181 -0
  281. package/references/openteams/package.json +48 -0
  282. package/references/openteams/schema/role.schema.json +125 -0
  283. package/references/openteams/schema/team.schema.json +284 -0
  284. package/references/openteams/src/cli/agent.ts +104 -0
  285. package/references/openteams/src/cli/cli.test.ts +381 -0
  286. package/references/openteams/src/cli/generate.ts +220 -0
  287. package/references/openteams/src/cli/message.ts +241 -0
  288. package/references/openteams/src/cli/task.ts +154 -0
  289. package/references/openteams/src/cli/team.ts +104 -0
  290. package/references/openteams/src/cli/template.ts +207 -0
  291. package/references/openteams/src/cli.ts +45 -0
  292. package/references/openteams/src/db/database.test.ts +185 -0
  293. package/references/openteams/src/db/database.ts +240 -0
  294. package/references/openteams/src/generators/agent-prompt-generator.test.ts +332 -0
  295. package/references/openteams/src/generators/agent-prompt-generator.ts +521 -0
  296. package/references/openteams/src/generators/package-generator.test.ts +129 -0
  297. package/references/openteams/src/generators/package-generator.ts +102 -0
  298. package/references/openteams/src/generators/skill-generator.test.ts +246 -0
  299. package/references/openteams/src/generators/skill-generator.ts +374 -0
  300. package/references/openteams/src/index.ts +104 -0
  301. package/references/openteams/src/services/agent-service.test.ts +158 -0
  302. package/references/openteams/src/services/agent-service.ts +84 -0
  303. package/references/openteams/src/services/communication-service.test.ts +455 -0
  304. package/references/openteams/src/services/communication-service.ts +371 -0
  305. package/references/openteams/src/services/message-service.test.ts +342 -0
  306. package/references/openteams/src/services/message-service.ts +203 -0
  307. package/references/openteams/src/services/task-service.test.ts +434 -0
  308. package/references/openteams/src/services/task-service.ts +239 -0
  309. package/references/openteams/src/services/team-service.test.ts +181 -0
  310. package/references/openteams/src/services/team-service.ts +139 -0
  311. package/references/openteams/src/services/template-service.test.ts +306 -0
  312. package/references/openteams/src/services/template-service.ts +182 -0
  313. package/references/openteams/src/spawner/acp-factory.ts +96 -0
  314. package/references/openteams/src/spawner/interface.ts +31 -0
  315. package/references/openteams/src/spawner/mock.test.ts +93 -0
  316. package/references/openteams/src/spawner/mock.ts +59 -0
  317. package/references/openteams/src/template/loader.test.ts +1319 -0
  318. package/references/openteams/src/template/loader.ts +698 -0
  319. package/references/openteams/src/template/types.ts +200 -0
  320. package/references/openteams/src/types.ts +205 -0
  321. package/references/openteams/tsconfig.json +18 -0
  322. package/references/openteams/vitest.config.ts +9 -0
  323. package/references/skill-tree/.claude/settings.json +6 -0
  324. package/references/skill-tree/.sudocode/issues.jsonl +11 -0
  325. package/references/skill-tree/.sudocode/specs.jsonl +1 -0
  326. package/references/skill-tree/CLAUDE.md +150 -0
  327. package/references/skill-tree/README.md +324 -0
  328. package/references/skill-tree/docs/GAPS_v1.md +221 -0
  329. package/references/skill-tree/docs/INTEGRATION_PLAN.md +467 -0
  330. package/references/skill-tree/docs/TODOS.md +91 -0
  331. package/references/skill-tree/docs/anthropic_skill_guide.md +1364 -0
  332. package/references/skill-tree/docs/design/federated-skill-trees.md +524 -0
  333. package/references/skill-tree/docs/design/multi-agent-sync.md +759 -0
  334. package/references/skill-tree/docs/scraper/BRAINSTORM.md +583 -0
  335. package/references/skill-tree/docs/scraper/POC_PLAN.md +420 -0
  336. package/references/skill-tree/docs/scraper/README.md +170 -0
  337. package/references/skill-tree/examples/basic-usage.ts +190 -0
  338. package/references/skill-tree/package-lock.json +1509 -0
  339. package/references/skill-tree/package.json +66 -0
  340. package/references/skill-tree/scraper/README.md +123 -0
  341. package/references/skill-tree/scraper/docs/DESIGN.md +683 -0
  342. package/references/skill-tree/scraper/docs/PLAN.md +336 -0
  343. package/references/skill-tree/scraper/drizzle.config.ts +10 -0
  344. package/references/skill-tree/scraper/package-lock.json +6329 -0
  345. package/references/skill-tree/scraper/package.json +68 -0
  346. package/references/skill-tree/scraper/test/fixtures/invalid-skill/missing-description.md +7 -0
  347. package/references/skill-tree/scraper/test/fixtures/invalid-skill/missing-name.md +7 -0
  348. package/references/skill-tree/scraper/test/fixtures/minimal-skill/SKILL.md +27 -0
  349. package/references/skill-tree/scraper/test/fixtures/skill-json/SKILL.json +21 -0
  350. package/references/skill-tree/scraper/test/fixtures/skill-with-meta/SKILL.md +54 -0
  351. package/references/skill-tree/scraper/test/fixtures/skill-with-meta/_meta.json +24 -0
  352. package/references/skill-tree/scraper/test/fixtures/valid-skill/SKILL.md +93 -0
  353. package/references/skill-tree/scraper/test/fixtures/valid-skill/_meta.json +22 -0
  354. package/references/skill-tree/scraper/tsup.config.ts +14 -0
  355. package/references/skill-tree/scraper/vitest.config.ts +17 -0
  356. package/references/skill-tree/scripts/convert-to-vitest.ts +166 -0
  357. package/references/skill-tree/skills/skill-writer/SKILL.md +339 -0
  358. package/references/skill-tree/skills/skill-writer/references/examples.md +326 -0
  359. package/references/skill-tree/skills/skill-writer/references/patterns.md +210 -0
  360. package/references/skill-tree/skills/skill-writer/references/quality-checklist.md +123 -0
  361. package/references/skill-tree/test/run-all.ts +106 -0
  362. package/references/skill-tree/test/utils.ts +128 -0
  363. package/references/skill-tree/vitest.config.ts +16 -0
  364. package/src/agent/agent-manager.ts +143 -72
  365. package/src/agent/types.ts +9 -0
  366. package/src/api/__tests__/server.test.ts +203 -4
  367. package/src/api/server.ts +130 -5
  368. package/src/api/types.ts +3 -1
  369. package/src/cli/acp.ts +68 -1
  370. package/src/cli/index.ts +5 -1
  371. package/src/cli/mcp.ts +27 -13
  372. package/src/config/project-config.ts +27 -3
  373. package/src/index.ts +3 -0
  374. package/src/lifecycle/__tests__/handlers.test.ts +53 -0
  375. package/src/lifecycle/handlers/index.ts +25 -8
  376. package/src/lifecycle/types.ts +3 -0
  377. package/src/map/adapter/__tests__/stream-extensions.test.ts +494 -0
  378. package/src/map/adapter/extensions/index.ts +36 -0
  379. package/src/map/adapter/extensions/streams.ts +839 -0
  380. package/src/map/adapter/index.ts +5 -0
  381. package/src/map/adapter/types.ts +8 -1
  382. package/src/mcp/mcp-server.ts +14 -3
  383. package/src/mcp/tools/done.ts +19 -0
  384. package/src/roles/builtin/coordinator.ts +2 -0
  385. package/src/roles/builtin/integrator.ts +2 -0
  386. package/src/roles/builtin/worker.ts +3 -0
  387. package/src/roles/capabilities.ts +11 -0
  388. package/src/roles/config-loader.ts +3 -2
  389. package/src/roles/types.ts +7 -0
  390. package/src/server/combined-server.ts +15 -1
  391. package/src/store/__tests__/event-store-oob.test.ts +109 -0
  392. package/src/store/event-store.ts +13 -3
  393. package/src/store/instance.ts +2 -2
  394. package/src/store/types/agents.ts +5 -0
  395. package/src/task/backend/__tests__/memory-pull-mode.test.ts +153 -0
  396. package/src/task/backend/opentasks/daemon-manager.ts +4 -1
  397. package/src/teams/CLAUDE.md +180 -0
  398. package/src/teams/__tests__/e2e/workspace-isolation.e2e.test.ts +1263 -0
  399. package/src/teams/__tests__/team-manager.test.ts +814 -0
  400. package/src/teams/__tests__/team-system.test.ts +1291 -8
  401. package/src/teams/index.ts +21 -3
  402. package/src/teams/seed-defaults.ts +79 -0
  403. package/src/teams/team-loader.ts +200 -234
  404. package/src/teams/team-manager.ts +387 -0
  405. package/src/teams/team-runtime.ts +590 -121
  406. package/src/teams/types.ts +99 -200
@@ -0,0 +1,698 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import yaml from "js-yaml";
4
+ import type {
5
+ TeamManifest,
6
+ RoleDefinition,
7
+ ResolvedTemplate,
8
+ ResolvedRole,
9
+ ResolvedPrompts,
10
+ PromptSection,
11
+ CapabilityComposition,
12
+ McpServerEntry,
13
+ LoadOptions,
14
+ AsyncLoadOptions,
15
+ } from "./types";
16
+
17
+ export class TemplateLoader {
18
+ /**
19
+ * Load a team template from a directory.
20
+ * Expects: team.yaml, optional roles/*.yaml, optional prompts/*.md
21
+ *
22
+ * @param templateDir - Absolute or relative path to the template directory
23
+ * @param options - Optional hooks for external role resolution and post-processing
24
+ */
25
+ static load(templateDir: string, options?: LoadOptions): ResolvedTemplate {
26
+ const { manifest, roles, prompts, mcpServers, absDir } =
27
+ TemplateLoader.loadCore(templateDir);
28
+
29
+ // Resolve role inheritance chains (with optional external resolution)
30
+ TemplateLoader.resolveInheritance(roles, options?.resolveExternalRole);
31
+
32
+ // Post-process each role if hook provided
33
+ if (options?.postProcessRole) {
34
+ for (const [name, role] of roles) {
35
+ roles.set(name, options.postProcessRole(role, manifest));
36
+ }
37
+ }
38
+
39
+ // Load prompts
40
+ for (const roleName of manifest.roles) {
41
+ const resolved = TemplateLoader.loadPromptsForRole(
42
+ absDir,
43
+ roleName,
44
+ manifest,
45
+ roles.get(roleName)
46
+ );
47
+ if (resolved) {
48
+ prompts.set(roleName, resolved);
49
+ }
50
+ }
51
+
52
+ let result: ResolvedTemplate = {
53
+ manifest,
54
+ roles,
55
+ prompts,
56
+ mcpServers,
57
+ sourcePath: absDir,
58
+ };
59
+
60
+ if (options?.postProcess) {
61
+ result = options.postProcess(result);
62
+ }
63
+
64
+ return result;
65
+ }
66
+
67
+ /**
68
+ * Async variant of load(). Identical file I/O but hooks may return Promises.
69
+ *
70
+ * @param templateDir - Absolute or relative path to the template directory
71
+ * @param options - Optional async hooks for external role resolution and post-processing
72
+ */
73
+ static async loadAsync(
74
+ templateDir: string,
75
+ options?: AsyncLoadOptions
76
+ ): Promise<ResolvedTemplate> {
77
+ const { manifest, roles, prompts, mcpServers, absDir } =
78
+ TemplateLoader.loadCore(templateDir);
79
+
80
+ // Resolve role inheritance chains (with optional async external resolution)
81
+ await TemplateLoader.resolveInheritanceAsync(
82
+ roles,
83
+ options?.resolveExternalRole
84
+ );
85
+
86
+ // Post-process each role if hook provided
87
+ if (options?.postProcessRole) {
88
+ for (const [name, role] of roles) {
89
+ roles.set(name, await options.postProcessRole(role, manifest));
90
+ }
91
+ }
92
+
93
+ // Load prompts
94
+ for (const roleName of manifest.roles) {
95
+ const resolved = TemplateLoader.loadPromptsForRole(
96
+ absDir,
97
+ roleName,
98
+ manifest,
99
+ roles.get(roleName)
100
+ );
101
+ if (resolved) {
102
+ prompts.set(roleName, resolved);
103
+ }
104
+ }
105
+
106
+ let result: ResolvedTemplate = {
107
+ manifest,
108
+ roles,
109
+ prompts,
110
+ mcpServers,
111
+ sourcePath: absDir,
112
+ };
113
+
114
+ if (options?.postProcess) {
115
+ result = await options.postProcess(result);
116
+ }
117
+
118
+ return result;
119
+ }
120
+
121
+ /**
122
+ * Core loading logic shared by load() and loadAsync().
123
+ * Reads filesystem and parses YAML — no hooks called.
124
+ */
125
+ private static loadCore(templateDir: string): {
126
+ manifest: TeamManifest;
127
+ roles: Map<string, ResolvedRole>;
128
+ prompts: Map<string, ResolvedPrompts>;
129
+ mcpServers: Map<string, McpServerEntry[]>;
130
+ absDir: string;
131
+ } {
132
+ const absDir = path.resolve(templateDir);
133
+
134
+ if (!fs.existsSync(absDir)) {
135
+ throw new Error(`Template directory not found: ${absDir}`);
136
+ }
137
+
138
+ const manifestPath = path.join(absDir, "team.yaml");
139
+ if (!fs.existsSync(manifestPath)) {
140
+ throw new Error(`team.yaml not found in ${absDir}`);
141
+ }
142
+
143
+ const manifestContent = fs.readFileSync(manifestPath, "utf-8");
144
+ const manifest = yaml.load(manifestContent) as TeamManifest;
145
+
146
+ TemplateLoader.validateManifest(manifest);
147
+
148
+ const roles = new Map<string, ResolvedRole>();
149
+ const prompts = new Map<string, ResolvedPrompts>();
150
+
151
+ // Load role definitions
152
+ for (const roleName of manifest.roles) {
153
+ const rolePath = path.join(absDir, "roles", `${roleName}.yaml`);
154
+ if (fs.existsSync(rolePath)) {
155
+ const roleContent = fs.readFileSync(rolePath, "utf-8");
156
+ const roleDef = yaml.load(roleContent) as RoleDefinition;
157
+ roles.set(roleName, TemplateLoader.resolveRole(roleDef));
158
+ } else {
159
+ // Implicit role — just the name, no definition file
160
+ roles.set(roleName, {
161
+ name: roleName,
162
+ displayName: roleName,
163
+ description: `Role: ${roleName}`,
164
+ capabilities: [],
165
+ raw: { name: roleName },
166
+ });
167
+ }
168
+ }
169
+
170
+ // Load MCP server configs
171
+ const mcpServers = TemplateLoader.loadMcpServers(absDir);
172
+
173
+ return { manifest, roles, prompts, mcpServers, absDir };
174
+ }
175
+
176
+ /**
177
+ * Load a template from an inline manifest object (no filesystem).
178
+ * Used for programmatic/test scenarios.
179
+ */
180
+ static loadFromManifest(manifest: TeamManifest): ResolvedTemplate {
181
+ TemplateLoader.validateManifest(manifest);
182
+
183
+ const roles = new Map<string, ResolvedRole>();
184
+ for (const roleName of manifest.roles) {
185
+ roles.set(roleName, {
186
+ name: roleName,
187
+ displayName: roleName,
188
+ description: `Role: ${roleName}`,
189
+ capabilities: [],
190
+ raw: { name: roleName },
191
+ });
192
+ }
193
+
194
+ return {
195
+ manifest,
196
+ roles,
197
+ prompts: new Map(),
198
+ mcpServers: new Map(),
199
+ sourcePath: "",
200
+ };
201
+ }
202
+
203
+ private static validateManifest(manifest: TeamManifest): void {
204
+ if (!manifest.name) {
205
+ throw new Error("Template manifest missing required field: name");
206
+ }
207
+ if (!manifest.version) {
208
+ throw new Error("Template manifest missing required field: version");
209
+ }
210
+ if (!manifest.roles || !Array.isArray(manifest.roles) || manifest.roles.length === 0) {
211
+ throw new Error("Template manifest must define at least one role");
212
+ }
213
+ if (!manifest.topology?.root?.role) {
214
+ throw new Error("Template manifest must define topology.root.role");
215
+ }
216
+
217
+ // Validate all topology references point to declared roles
218
+ const declaredRoles = new Set(manifest.roles);
219
+
220
+ if (!declaredRoles.has(manifest.topology.root.role)) {
221
+ throw new Error(
222
+ `topology.root.role "${manifest.topology.root.role}" is not in the roles list`
223
+ );
224
+ }
225
+
226
+ if (manifest.topology.companions) {
227
+ for (const comp of manifest.topology.companions) {
228
+ if (!declaredRoles.has(comp.role)) {
229
+ throw new Error(
230
+ `topology.companions role "${comp.role}" is not in the roles list`
231
+ );
232
+ }
233
+ }
234
+ }
235
+
236
+ if (manifest.topology.spawn_rules) {
237
+ for (const [from, targets] of Object.entries(manifest.topology.spawn_rules)) {
238
+ if (!declaredRoles.has(from)) {
239
+ throw new Error(
240
+ `spawn_rules key "${from}" is not in the roles list`
241
+ );
242
+ }
243
+ for (const target of targets) {
244
+ if (!declaredRoles.has(target)) {
245
+ throw new Error(
246
+ `spawn_rules "${from}" references unknown role "${target}"`
247
+ );
248
+ }
249
+ }
250
+ }
251
+ }
252
+
253
+ // Validate communication references
254
+ if (manifest.communication) {
255
+ const channels = manifest.communication.channels
256
+ ? new Set(Object.keys(manifest.communication.channels))
257
+ : new Set<string>();
258
+
259
+ if (manifest.communication.subscriptions) {
260
+ for (const [role, subs] of Object.entries(manifest.communication.subscriptions)) {
261
+ if (!declaredRoles.has(role)) {
262
+ throw new Error(
263
+ `communication.subscriptions key "${role}" is not in the roles list`
264
+ );
265
+ }
266
+ for (const sub of subs) {
267
+ if (!channels.has(sub.channel)) {
268
+ throw new Error(
269
+ `subscription for "${role}" references unknown channel "${sub.channel}"`
270
+ );
271
+ }
272
+ }
273
+ }
274
+ }
275
+
276
+ if (manifest.communication.emissions) {
277
+ for (const role of Object.keys(manifest.communication.emissions)) {
278
+ if (!declaredRoles.has(role)) {
279
+ throw new Error(
280
+ `communication.emissions key "${role}" is not in the roles list`
281
+ );
282
+ }
283
+ }
284
+ }
285
+
286
+ if (manifest.communication.routing?.peers) {
287
+ for (const peer of manifest.communication.routing.peers) {
288
+ if (!declaredRoles.has(peer.from)) {
289
+ throw new Error(
290
+ `routing.peers.from "${peer.from}" is not in the roles list`
291
+ );
292
+ }
293
+ if (!declaredRoles.has(peer.to)) {
294
+ throw new Error(
295
+ `routing.peers.to "${peer.to}" is not in the roles list`
296
+ );
297
+ }
298
+ }
299
+ }
300
+ }
301
+ }
302
+
303
+ /**
304
+ * Merge a child role's capabilities with its parent using CapabilityComposition.
305
+ */
306
+ private static mergeCapabilities(
307
+ role: ResolvedRole,
308
+ parentRole: ResolvedRole
309
+ ): void {
310
+ const raw = role.raw;
311
+ if (raw.capabilities && !Array.isArray(raw.capabilities)) {
312
+ // CapabilityComposition — merge with parent
313
+ const comp = raw.capabilities as CapabilityComposition;
314
+ const parentCaps = [...parentRole.capabilities];
315
+ const toAdd = comp.add ?? [];
316
+ const toRemove = new Set(comp.remove ?? []);
317
+
318
+ // Start with parent caps, add child additions, remove exclusions
319
+ const merged = [...new Set([...parentCaps, ...toAdd])];
320
+ role.capabilities = merged.filter((c) => !toRemove.has(c));
321
+ }
322
+ // If capabilities is a plain array, it's an explicit override — keep as-is
323
+ }
324
+
325
+ /**
326
+ * Resolve external parents: for roles that `extends` a name not in the local map,
327
+ * call the resolver hook to get the parent ResolvedRole.
328
+ * Returns a map of external parent name → ResolvedRole.
329
+ */
330
+ private static resolveExternalParents(
331
+ roles: Map<string, ResolvedRole>,
332
+ resolver?: (name: string) => ResolvedRole | null
333
+ ): Map<string, ResolvedRole> {
334
+ const externals = new Map<string, ResolvedRole>();
335
+ if (!resolver) return externals;
336
+
337
+ for (const [, role] of roles) {
338
+ if (role.extends && !roles.has(role.extends) && !externals.has(role.extends)) {
339
+ const external = resolver(role.extends);
340
+ if (external) {
341
+ externals.set(role.extends, external);
342
+ }
343
+ }
344
+ }
345
+ return externals;
346
+ }
347
+
348
+ /**
349
+ * Resolve external parents (async variant).
350
+ */
351
+ private static async resolveExternalParentsAsync(
352
+ roles: Map<string, ResolvedRole>,
353
+ resolver?: (name: string) => Promise<ResolvedRole | null> | ResolvedRole | null
354
+ ): Promise<Map<string, ResolvedRole>> {
355
+ const externals = new Map<string, ResolvedRole>();
356
+ if (!resolver) return externals;
357
+
358
+ for (const [, role] of roles) {
359
+ if (role.extends && !roles.has(role.extends) && !externals.has(role.extends)) {
360
+ const external = await resolver(role.extends);
361
+ if (external) {
362
+ externals.set(role.extends, external);
363
+ }
364
+ }
365
+ }
366
+ return externals;
367
+ }
368
+
369
+ /**
370
+ * Core inheritance resolution logic. Operates on a combined map of
371
+ * local + external parent roles.
372
+ */
373
+ private static resolveInheritanceCore(
374
+ roles: Map<string, ResolvedRole>,
375
+ allRoles: Map<string, ResolvedRole>
376
+ ): void {
377
+ // Build dependency map: child -> parent (only for parents resolvable in allRoles)
378
+ const extendsMap = new Map<string, string>();
379
+ for (const [name, role] of roles) {
380
+ if (role.extends && allRoles.has(role.extends)) {
381
+ extendsMap.set(name, role.extends);
382
+ }
383
+ }
384
+
385
+ if (extendsMap.size === 0) return;
386
+
387
+ // Detect cycles by following chains (only through local roles)
388
+ for (const startName of extendsMap.keys()) {
389
+ const chain: string[] = [];
390
+ let current: string | undefined = startName;
391
+ while (current) {
392
+ if (chain.includes(current)) {
393
+ const cycleStart = chain.indexOf(current);
394
+ const cyclePath = [...chain.slice(cycleStart), current].join(" -> ");
395
+ throw new Error(`Circular role inheritance detected: ${cyclePath}`);
396
+ }
397
+ chain.push(current);
398
+ current = extendsMap.get(current);
399
+ }
400
+ }
401
+
402
+ // Resolve in topological order (parents before children)
403
+ const resolved = new Set<string>();
404
+
405
+ const resolve = (name: string): void => {
406
+ if (resolved.has(name)) return;
407
+
408
+ const parent = extendsMap.get(name);
409
+ if (parent && roles.has(parent)) {
410
+ resolve(parent);
411
+ }
412
+
413
+ const role = roles.get(name)!;
414
+ if (parent) {
415
+ const parentRole = allRoles.get(parent)!;
416
+ TemplateLoader.mergeCapabilities(role, parentRole);
417
+ }
418
+
419
+ resolved.add(name);
420
+ };
421
+
422
+ for (const name of extendsMap.keys()) {
423
+ resolve(name);
424
+ }
425
+ }
426
+
427
+ /**
428
+ * Resolve role inheritance chains. For each role with `extends` pointing
429
+ * to another role in the map (or resolvable via external resolver),
430
+ * merge parent capabilities with the child's add/remove composition.
431
+ * Detects circular inheritance.
432
+ *
433
+ * @param resolveExternalRole - Optional hook to resolve roles not in the local map
434
+ */
435
+ private static resolveInheritance(
436
+ roles: Map<string, ResolvedRole>,
437
+ resolveExternalRole?: (name: string) => ResolvedRole | null
438
+ ): void {
439
+ const externals = TemplateLoader.resolveExternalParents(roles, resolveExternalRole);
440
+ const allRoles = new Map([...roles, ...externals]);
441
+ TemplateLoader.resolveInheritanceCore(roles, allRoles);
442
+ }
443
+
444
+ /**
445
+ * Async variant of resolveInheritance.
446
+ */
447
+ private static async resolveInheritanceAsync(
448
+ roles: Map<string, ResolvedRole>,
449
+ resolveExternalRole?: (name: string) => Promise<ResolvedRole | null> | ResolvedRole | null
450
+ ): Promise<void> {
451
+ const externals = await TemplateLoader.resolveExternalParentsAsync(roles, resolveExternalRole);
452
+ const allRoles = new Map([...roles, ...externals]);
453
+ TemplateLoader.resolveInheritanceCore(roles, allRoles);
454
+ }
455
+
456
+ /**
457
+ * Normalize a role definition's capability fields into a canonical form.
458
+ *
459
+ * Supports two syntaxes for capability composition:
460
+ * 1. `capabilities: { add: [...], remove: [...] }` (CapabilityComposition)
461
+ * 2. `capabilities_add: [...]` / `capabilities_remove: [...]` (flat fields)
462
+ *
463
+ * Both are normalized into CapabilityComposition on `raw.capabilities` so that
464
+ * `resolveInheritance()` has a single code path.
465
+ *
466
+ * Validation: errors if both syntaxes are used simultaneously.
467
+ */
468
+ private static normalizeRoleDefinition(def: RoleDefinition): RoleDefinition {
469
+ const hasComposition = def.capabilities && !Array.isArray(def.capabilities);
470
+ const hasFlatFields = def.capabilities_add !== undefined || def.capabilities_remove !== undefined;
471
+
472
+ if (hasComposition && hasFlatFields) {
473
+ throw new Error(
474
+ `Role "${def.name}" uses both CapabilityComposition in "capabilities" and flat ` +
475
+ `"capabilities_add"/"capabilities_remove" fields. Use one syntax or the other.`
476
+ );
477
+ }
478
+
479
+ // Normalize flat fields into CapabilityComposition on the capabilities field
480
+ if (hasFlatFields) {
481
+ const normalized = { ...def };
482
+ normalized.capabilities = {
483
+ add: def.capabilities_add,
484
+ remove: def.capabilities_remove,
485
+ } as CapabilityComposition;
486
+ delete normalized.capabilities_add;
487
+ delete normalized.capabilities_remove;
488
+ return normalized;
489
+ }
490
+
491
+ return def;
492
+ }
493
+
494
+ private static resolveRole(def: RoleDefinition): ResolvedRole {
495
+ // Normalize flat capabilities_add/remove into CapabilityComposition
496
+ const normalized = TemplateLoader.normalizeRoleDefinition(def);
497
+ let capabilities: string[] = [];
498
+
499
+ if (normalized.capabilities) {
500
+ if (Array.isArray(normalized.capabilities)) {
501
+ capabilities = normalized.capabilities;
502
+ } else {
503
+ // CapabilityComposition — resolve against parent later if extends is used.
504
+ // For now, just collect the add list.
505
+ const comp = normalized.capabilities as CapabilityComposition;
506
+ capabilities = comp.add ?? [];
507
+ // remove is applied when composing with parent; tracked in raw for later
508
+ }
509
+ }
510
+
511
+ return {
512
+ name: normalized.name,
513
+ extends: normalized.extends,
514
+ displayName: normalized.display_name ?? normalized.name,
515
+ description: normalized.description ?? `Role: ${normalized.name}`,
516
+ capabilities,
517
+ promptFile: normalized.prompt,
518
+ promptFiles: normalized.prompts,
519
+ raw: normalized,
520
+ };
521
+ }
522
+
523
+ /**
524
+ * Load tools/mcp-servers.json if present.
525
+ * Returns a map of role name → MCP server entries.
526
+ */
527
+ private static loadMcpServers(
528
+ absDir: string
529
+ ): Map<string, McpServerEntry[]> {
530
+ const result = new Map<string, McpServerEntry[]>();
531
+ const mcpPath = path.join(absDir, "tools", "mcp-servers.json");
532
+
533
+ if (!fs.existsSync(mcpPath)) {
534
+ return result;
535
+ }
536
+
537
+ let raw: string;
538
+ try {
539
+ raw = fs.readFileSync(mcpPath, "utf-8");
540
+ } catch (err) {
541
+ throw new Error(
542
+ `Failed to read ${mcpPath}: ${err instanceof Error ? err.message : String(err)}`
543
+ );
544
+ }
545
+
546
+ let parsed: Record<string, { servers: McpServerEntry[] }>;
547
+ try {
548
+ parsed = JSON.parse(raw);
549
+ } catch (err) {
550
+ throw new Error(
551
+ `Failed to parse ${mcpPath}: ${err instanceof Error ? err.message : String(err)}`
552
+ );
553
+ }
554
+
555
+ for (const [roleName, config] of Object.entries(parsed)) {
556
+ if (config.servers && Array.isArray(config.servers)) {
557
+ result.set(roleName, config.servers);
558
+ }
559
+ }
560
+
561
+ return result;
562
+ }
563
+
564
+ /**
565
+ * Load prompts for a role. Supports two layouts:
566
+ *
567
+ * 1. Single file: prompts/<role>.md (backward compatible)
568
+ * 2. Directory: prompts/<role>/prompt.md + additional .md files
569
+ *
570
+ * When a directory exists, prompt.md is the primary prompt and all
571
+ * other .md files become additional sections. The role YAML can
572
+ * specify an explicit file list via `prompts:` to control ordering.
573
+ */
574
+ private static loadPromptsForRole(
575
+ absDir: string,
576
+ roleName: string,
577
+ manifest: TeamManifest,
578
+ role?: ResolvedRole
579
+ ): ResolvedPrompts | null {
580
+ // Priority 1: topology node prompt path (single file, backward compat)
581
+ if (manifest.topology.root.role === roleName && manifest.topology.root.prompt) {
582
+ const p = path.join(absDir, manifest.topology.root.prompt);
583
+ if (fs.existsSync(p)) {
584
+ return { primary: fs.readFileSync(p, "utf-8"), additional: [] };
585
+ }
586
+ }
587
+
588
+ if (manifest.topology.companions) {
589
+ for (const comp of manifest.topology.companions) {
590
+ if (comp.role === roleName && comp.prompt) {
591
+ const p = path.join(absDir, comp.prompt);
592
+ if (fs.existsSync(p)) {
593
+ return { primary: fs.readFileSync(p, "utf-8"), additional: [] };
594
+ }
595
+ }
596
+ }
597
+ }
598
+
599
+ // Priority 2: role definition prompt field (single file)
600
+ if (role?.promptFile && !role.promptFiles) {
601
+ const p = path.join(absDir, role.promptFile);
602
+ if (fs.existsSync(p)) {
603
+ return { primary: fs.readFileSync(p, "utf-8"), additional: [] };
604
+ }
605
+ }
606
+
607
+ // Priority 3: prompt directory — prompts/<roleName>/
608
+ const promptDirPath = path.join(absDir, "prompts", roleName);
609
+ if (fs.existsSync(promptDirPath) && fs.statSync(promptDirPath).isDirectory()) {
610
+ return TemplateLoader.loadPromptDirectory(promptDirPath, role);
611
+ }
612
+
613
+ // Priority 4: single file convention — prompts/<roleName>.md
614
+ const conventionPath = path.join(absDir, "prompts", `${roleName}.md`);
615
+ if (fs.existsSync(conventionPath)) {
616
+ return { primary: fs.readFileSync(conventionPath, "utf-8"), additional: [] };
617
+ }
618
+
619
+ return null;
620
+ }
621
+
622
+ /**
623
+ * Load a prompt directory into a ResolvedPrompts.
624
+ *
625
+ * If the role YAML declares `prompts:` (an ordered list of filenames),
626
+ * those files are loaded in that order. The first file is primary,
627
+ * the rest are additional sections.
628
+ *
629
+ * Otherwise, ROLE.md is the primary and remaining .md files are
630
+ * loaded as additional sections. SOUL.md is always ordered first
631
+ * among additional files so personality/values precede other materials.
632
+ */
633
+ private static loadPromptDirectory(
634
+ dirPath: string,
635
+ role?: ResolvedRole
636
+ ): ResolvedPrompts | null {
637
+ // Explicit ordering from role YAML
638
+ if (role?.promptFiles && role.promptFiles.length > 0) {
639
+ const files = role.promptFiles;
640
+ let primary: string | null = null;
641
+ const additional: PromptSection[] = [];
642
+
643
+ for (const file of files) {
644
+ const filePath = path.join(dirPath, file);
645
+ if (!fs.existsSync(filePath)) continue;
646
+ const content = fs.readFileSync(filePath, "utf-8");
647
+ if (primary === null) {
648
+ primary = content;
649
+ } else {
650
+ const stem = path.basename(file, path.extname(file));
651
+ additional.push({ name: stem, content });
652
+ }
653
+ }
654
+
655
+ if (primary === null) return null;
656
+ return { primary, additional };
657
+ }
658
+
659
+ // Convention: ROLE.md is primary, SOUL.md is first additional,
660
+ // remaining .md files sorted alphabetically.
661
+ // Falls back to prompt.md / first-alphabetical for backward compat.
662
+ const allFiles = fs.readdirSync(dirPath)
663
+ .filter((f: string) => f.endsWith(".md"))
664
+ .sort();
665
+
666
+ if (allFiles.length === 0) return null;
667
+
668
+ // Determine primary file: ROLE.md > prompt.md > first alphabetically
669
+ let primaryFile: string;
670
+ if (allFiles.includes("ROLE.md")) {
671
+ primaryFile = "ROLE.md";
672
+ } else if (allFiles.includes("prompt.md")) {
673
+ primaryFile = "prompt.md";
674
+ } else {
675
+ primaryFile = allFiles[0];
676
+ }
677
+
678
+ const primary = fs.readFileSync(path.join(dirPath, primaryFile), "utf-8");
679
+
680
+ // Build additional list: SOUL.md first, then the rest alphabetically
681
+ const additional: PromptSection[] = [];
682
+ const soulFile = allFiles.find((f: string) => f === "SOUL.md" || f === "soul.md");
683
+ if (soulFile && soulFile !== primaryFile) {
684
+ const content = fs.readFileSync(path.join(dirPath, soulFile), "utf-8");
685
+ additional.push({ name: "soul", content });
686
+ }
687
+
688
+ for (const file of allFiles) {
689
+ if (file === primaryFile) continue;
690
+ if (file === soulFile) continue; // already added above
691
+ const stem = path.basename(file, ".md");
692
+ const content = fs.readFileSync(path.join(dirPath, file), "utf-8");
693
+ additional.push({ name: stem, content });
694
+ }
695
+
696
+ return { primary, additional };
697
+ }
698
+ }