macro-agent 0.1.0 → 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 (660) hide show
  1. package/.claude/settings.local.json +3 -1
  2. package/.sudocode/issues.jsonl +28 -0
  3. package/.sudocode/specs.jsonl +8 -0
  4. package/CLAUDE.md +25 -17
  5. package/README.md +11 -29
  6. package/dist/acp/macro-agent.d.ts +15 -0
  7. package/dist/acp/macro-agent.d.ts.map +1 -1
  8. package/dist/acp/macro-agent.js +131 -35
  9. package/dist/acp/macro-agent.js.map +1 -1
  10. package/dist/acp/types.d.ts +32 -1
  11. package/dist/acp/types.d.ts.map +1 -1
  12. package/dist/acp/types.js.map +1 -1
  13. package/dist/agent/agent-manager.d.ts +65 -1
  14. package/dist/agent/agent-manager.d.ts.map +1 -1
  15. package/dist/agent/agent-manager.js +544 -200
  16. package/dist/agent/agent-manager.js.map +1 -1
  17. package/dist/agent/types.d.ts +8 -1
  18. package/dist/agent/types.d.ts.map +1 -1
  19. package/dist/agent/types.js.map +1 -1
  20. package/dist/api/server.d.ts +8 -1
  21. package/dist/api/server.d.ts.map +1 -1
  22. package/dist/api/server.js +136 -8
  23. package/dist/api/server.js.map +1 -1
  24. package/dist/api/types.d.ts +1 -1
  25. package/dist/api/types.d.ts.map +1 -1
  26. package/dist/auth/index.d.ts +2 -0
  27. package/dist/auth/index.d.ts.map +1 -0
  28. package/dist/auth/index.js +2 -0
  29. package/dist/auth/index.js.map +1 -0
  30. package/dist/auth/token.d.ts +41 -0
  31. package/dist/auth/token.d.ts.map +1 -0
  32. package/dist/auth/token.js +73 -0
  33. package/dist/auth/token.js.map +1 -0
  34. package/dist/cli/acp.d.ts +2 -23
  35. package/dist/cli/acp.d.ts.map +1 -1
  36. package/dist/cli/acp.js +197 -61
  37. package/dist/cli/acp.js.map +1 -1
  38. package/dist/cli/index.js +152 -16
  39. package/dist/cli/index.js.map +1 -1
  40. package/dist/cli/mcp.d.ts +6 -0
  41. package/dist/cli/mcp.d.ts.map +1 -1
  42. package/dist/cli/mcp.js +279 -173
  43. package/dist/cli/mcp.js.map +1 -1
  44. package/dist/cli/parse-args.d.ts +20 -0
  45. package/dist/cli/parse-args.d.ts.map +1 -0
  46. package/dist/cli/parse-args.js +43 -0
  47. package/dist/cli/parse-args.js.map +1 -0
  48. package/dist/cli/stable-instance-id.d.ts +8 -0
  49. package/dist/cli/stable-instance-id.d.ts.map +1 -0
  50. package/dist/cli/stable-instance-id.js +14 -0
  51. package/dist/cli/stable-instance-id.js.map +1 -0
  52. package/dist/config/project-config.d.ts +85 -7
  53. package/dist/config/project-config.d.ts.map +1 -1
  54. package/dist/config/project-config.js +133 -20
  55. package/dist/config/project-config.js.map +1 -1
  56. package/dist/index.d.ts +1 -0
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +2 -0
  59. package/dist/index.js.map +1 -1
  60. package/dist/lifecycle/handlers/index.d.ts +7 -3
  61. package/dist/lifecycle/handlers/index.d.ts.map +1 -1
  62. package/dist/lifecycle/handlers/index.js +25 -8
  63. package/dist/lifecycle/handlers/index.js.map +1 -1
  64. package/dist/lifecycle/types.d.ts +2 -0
  65. package/dist/lifecycle/types.d.ts.map +1 -1
  66. package/dist/lifecycle/types.js.map +1 -1
  67. package/dist/map/adapter/acp-over-map.d.ts +17 -0
  68. package/dist/map/adapter/acp-over-map.d.ts.map +1 -1
  69. package/dist/map/adapter/acp-over-map.js +384 -23
  70. package/dist/map/adapter/acp-over-map.js.map +1 -1
  71. package/dist/map/adapter/connection-manager.d.ts.map +1 -1
  72. package/dist/map/adapter/connection-manager.js +3 -0
  73. package/dist/map/adapter/connection-manager.js.map +1 -1
  74. package/dist/map/adapter/event-log.d.ts +87 -0
  75. package/dist/map/adapter/event-log.d.ts.map +1 -0
  76. package/dist/map/adapter/event-log.js +122 -0
  77. package/dist/map/adapter/event-log.js.map +1 -0
  78. package/dist/map/adapter/event-translator.js +6 -6
  79. package/dist/map/adapter/event-translator.js.map +1 -1
  80. package/dist/map/adapter/extensions/agent-lifecycle.d.ts +82 -0
  81. package/dist/map/adapter/extensions/agent-lifecycle.d.ts.map +1 -0
  82. package/dist/map/adapter/extensions/agent-lifecycle.js +164 -0
  83. package/dist/map/adapter/extensions/agent-lifecycle.js.map +1 -0
  84. package/dist/map/adapter/extensions/index.d.ts +13 -1
  85. package/dist/map/adapter/extensions/index.d.ts.map +1 -1
  86. package/dist/map/adapter/extensions/index.js +61 -0
  87. package/dist/map/adapter/extensions/index.js.map +1 -1
  88. package/dist/map/adapter/extensions/mcp-bridge.d.ts +57 -0
  89. package/dist/map/adapter/extensions/mcp-bridge.d.ts.map +1 -0
  90. package/dist/map/adapter/extensions/mcp-bridge.js +745 -0
  91. package/dist/map/adapter/extensions/mcp-bridge.js.map +1 -0
  92. package/dist/map/adapter/extensions/rename.d.ts +29 -0
  93. package/dist/map/adapter/extensions/rename.d.ts.map +1 -0
  94. package/dist/map/adapter/extensions/rename.js +49 -0
  95. package/dist/map/adapter/extensions/rename.js.map +1 -0
  96. package/dist/map/adapter/extensions/streams.d.ts +95 -0
  97. package/dist/map/adapter/extensions/streams.d.ts.map +1 -0
  98. package/dist/map/adapter/extensions/streams.js +515 -0
  99. package/dist/map/adapter/extensions/streams.js.map +1 -0
  100. package/dist/map/adapter/extensions/task.d.ts.map +1 -1
  101. package/dist/map/adapter/extensions/task.js +10 -0
  102. package/dist/map/adapter/extensions/task.js.map +1 -1
  103. package/dist/map/adapter/extensions/update-metadata.d.ts +29 -0
  104. package/dist/map/adapter/extensions/update-metadata.d.ts.map +1 -0
  105. package/dist/map/adapter/extensions/update-metadata.js +67 -0
  106. package/dist/map/adapter/extensions/update-metadata.js.map +1 -0
  107. package/dist/map/adapter/index.d.ts +2 -1
  108. package/dist/map/adapter/index.d.ts.map +1 -1
  109. package/dist/map/adapter/index.js +10 -2
  110. package/dist/map/adapter/index.js.map +1 -1
  111. package/dist/map/adapter/interface.d.ts +2 -0
  112. package/dist/map/adapter/interface.d.ts.map +1 -1
  113. package/dist/map/adapter/map-adapter.d.ts +3 -0
  114. package/dist/map/adapter/map-adapter.d.ts.map +1 -1
  115. package/dist/map/adapter/map-adapter.js +258 -35
  116. package/dist/map/adapter/map-adapter.js.map +1 -1
  117. package/dist/map/adapter/subscription-manager.d.ts.map +1 -1
  118. package/dist/map/adapter/subscription-manager.js +5 -1
  119. package/dist/map/adapter/subscription-manager.js.map +1 -1
  120. package/dist/map/adapter/types.d.ts +3 -1
  121. package/dist/map/adapter/types.d.ts.map +1 -1
  122. package/dist/mcp/map-client.d.ts +39 -0
  123. package/dist/mcp/map-client.d.ts.map +1 -0
  124. package/dist/mcp/map-client.js +129 -0
  125. package/dist/mcp/map-client.js.map +1 -0
  126. package/dist/mcp/mcp-server.d.ts +16 -0
  127. package/dist/mcp/mcp-server.d.ts.map +1 -1
  128. package/dist/mcp/mcp-server.js +125 -88
  129. package/dist/mcp/mcp-server.js.map +1 -1
  130. package/dist/mcp/tools/done.d.ts.map +1 -1
  131. package/dist/mcp/tools/done.js +18 -0
  132. package/dist/mcp/tools/done.js.map +1 -1
  133. package/dist/mcp/types.d.ts +9 -1
  134. package/dist/mcp/types.d.ts.map +1 -1
  135. package/dist/mcp/types.js.map +1 -1
  136. package/dist/metrics/metrics.js +1 -1
  137. package/dist/metrics/metrics.js.map +1 -1
  138. package/dist/roles/builtin/coordinator.d.ts.map +1 -1
  139. package/dist/roles/builtin/coordinator.js +2 -1
  140. package/dist/roles/builtin/coordinator.js.map +1 -1
  141. package/dist/roles/builtin/integrator.d.ts.map +1 -1
  142. package/dist/roles/builtin/integrator.js +2 -1
  143. package/dist/roles/builtin/integrator.js.map +1 -1
  144. package/dist/roles/builtin/worker.d.ts.map +1 -1
  145. package/dist/roles/builtin/worker.js +3 -1
  146. package/dist/roles/builtin/worker.js.map +1 -1
  147. package/dist/roles/capabilities.d.ts +9 -1
  148. package/dist/roles/capabilities.d.ts.map +1 -1
  149. package/dist/roles/capabilities.js +27 -7
  150. package/dist/roles/capabilities.js.map +1 -1
  151. package/dist/roles/config-loader.d.ts +6 -6
  152. package/dist/roles/config-loader.d.ts.map +1 -1
  153. package/dist/roles/config-loader.js +8 -7
  154. package/dist/roles/config-loader.js.map +1 -1
  155. package/dist/roles/registry.d.ts +2 -2
  156. package/dist/roles/registry.js +2 -2
  157. package/dist/roles/types.d.ts +3 -1
  158. package/dist/roles/types.d.ts.map +1 -1
  159. package/dist/server/combined-server.d.ts +28 -1
  160. package/dist/server/combined-server.d.ts.map +1 -1
  161. package/dist/server/combined-server.js +111 -8
  162. package/dist/server/combined-server.js.map +1 -1
  163. package/dist/store/event-store.d.ts +2 -1
  164. package/dist/store/event-store.d.ts.map +1 -1
  165. package/dist/store/event-store.js +80 -24
  166. package/dist/store/event-store.js.map +1 -1
  167. package/dist/store/instance.d.ts +1 -1
  168. package/dist/store/instance.d.ts.map +1 -1
  169. package/dist/store/instance.js +2 -2
  170. package/dist/store/instance.js.map +1 -1
  171. package/dist/store/types/agents.d.ts +23 -0
  172. package/dist/store/types/agents.d.ts.map +1 -1
  173. package/dist/store/types/events.d.ts +1 -1
  174. package/dist/store/types/events.d.ts.map +1 -1
  175. package/dist/task/backend/index.d.ts +47 -29
  176. package/dist/task/backend/index.d.ts.map +1 -1
  177. package/dist/task/backend/index.js +109 -71
  178. package/dist/task/backend/index.js.map +1 -1
  179. package/dist/task/backend/memory.d.ts +1 -0
  180. package/dist/task/backend/memory.d.ts.map +1 -1
  181. package/dist/task/backend/memory.js +3 -0
  182. package/dist/task/backend/memory.js.map +1 -1
  183. package/dist/task/backend/opentasks/backend.d.ts +140 -0
  184. package/dist/task/backend/opentasks/backend.d.ts.map +1 -0
  185. package/dist/task/backend/opentasks/backend.js +1023 -0
  186. package/dist/task/backend/opentasks/backend.js.map +1 -0
  187. package/dist/task/backend/opentasks/client.d.ts +337 -0
  188. package/dist/task/backend/opentasks/client.d.ts.map +1 -0
  189. package/dist/task/backend/opentasks/client.js +225 -0
  190. package/dist/task/backend/opentasks/client.js.map +1 -0
  191. package/dist/task/backend/opentasks/daemon-manager.d.ts +89 -0
  192. package/dist/task/backend/opentasks/daemon-manager.d.ts.map +1 -0
  193. package/dist/task/backend/opentasks/daemon-manager.js +195 -0
  194. package/dist/task/backend/opentasks/daemon-manager.js.map +1 -0
  195. package/dist/task/backend/opentasks/index.d.ts +21 -0
  196. package/dist/task/backend/opentasks/index.d.ts.map +1 -0
  197. package/dist/task/backend/opentasks/index.js +21 -0
  198. package/dist/task/backend/opentasks/index.js.map +1 -0
  199. package/dist/task/backend/opentasks/mapping.d.ts +48 -0
  200. package/dist/task/backend/opentasks/mapping.d.ts.map +1 -0
  201. package/dist/task/backend/opentasks/mapping.js +77 -0
  202. package/dist/task/backend/opentasks/mapping.js.map +1 -0
  203. package/dist/task/backend/types.d.ts +33 -53
  204. package/dist/task/backend/types.d.ts.map +1 -1
  205. package/dist/task/backend/types.js +7 -11
  206. package/dist/task/backend/types.js.map +1 -1
  207. package/dist/task/backend/unified-tool-provider.d.ts +57 -0
  208. package/dist/task/backend/unified-tool-provider.d.ts.map +1 -0
  209. package/dist/task/backend/unified-tool-provider.js +623 -0
  210. package/dist/task/backend/unified-tool-provider.js.map +1 -0
  211. package/dist/teams/index.d.ts +3 -1
  212. package/dist/teams/index.d.ts.map +1 -1
  213. package/dist/teams/index.js +2 -0
  214. package/dist/teams/index.js.map +1 -1
  215. package/dist/teams/seed-defaults.d.ts +20 -0
  216. package/dist/teams/seed-defaults.d.ts.map +1 -0
  217. package/dist/teams/seed-defaults.js +71 -0
  218. package/dist/teams/seed-defaults.js.map +1 -0
  219. package/dist/teams/team-loader.d.ts +7 -3
  220. package/dist/teams/team-loader.d.ts.map +1 -1
  221. package/dist/teams/team-loader.js +156 -164
  222. package/dist/teams/team-loader.js.map +1 -1
  223. package/dist/teams/team-manager.d.ts +112 -0
  224. package/dist/teams/team-manager.d.ts.map +1 -0
  225. package/dist/teams/team-manager.js +305 -0
  226. package/dist/teams/team-manager.js.map +1 -0
  227. package/dist/teams/team-runtime.d.ts +125 -19
  228. package/dist/teams/team-runtime.d.ts.map +1 -1
  229. package/dist/teams/team-runtime.js +529 -119
  230. package/dist/teams/team-runtime.js.map +1 -1
  231. package/dist/teams/types.d.ts +41 -151
  232. package/dist/teams/types.d.ts.map +1 -1
  233. package/dist/teams/types.js +2 -3
  234. package/dist/teams/types.js.map +1 -1
  235. package/docs/architecture.md +7 -6
  236. package/docs/configuration.md +26 -62
  237. package/docs/implementation-details.md +5 -5
  238. package/docs/implementation-summary.md +17 -17
  239. package/docs/plan-self-driving-support.md +4 -4
  240. package/docs/spec-self-driving-support.md +10 -10
  241. package/docs/team-templates.md +2 -2
  242. package/docs/teams.md +76 -3
  243. package/docs/troubleshooting.md +10 -11
  244. package/package.json +7 -4
  245. package/references/minimem/.claude/settings.json +7 -0
  246. package/references/minimem/.sudocode/issues.jsonl +18 -0
  247. package/references/minimem/.sudocode/specs.jsonl +1 -0
  248. package/references/minimem/CLAUDE.md +310 -0
  249. package/references/minimem/README.md +562 -0
  250. package/references/minimem/claude-plugin/.claude-plugin/plugin.json +10 -0
  251. package/references/minimem/claude-plugin/.mcp.json +7 -0
  252. package/references/minimem/claude-plugin/README.md +158 -0
  253. package/references/minimem/claude-plugin/commands/recall.md +47 -0
  254. package/references/minimem/claude-plugin/commands/remember.md +41 -0
  255. package/references/minimem/claude-plugin/hooks/__tests__/hooks.test.ts +272 -0
  256. package/references/minimem/claude-plugin/hooks/hooks.json +27 -0
  257. package/references/minimem/claude-plugin/hooks/session-end.sh +86 -0
  258. package/references/minimem/claude-plugin/hooks/session-start.sh +85 -0
  259. package/references/minimem/claude-plugin/skills/memory/SKILL.md +108 -0
  260. package/references/minimem/media/banner.png +0 -0
  261. package/references/minimem/package-lock.json +5373 -0
  262. package/references/minimem/package.json +72 -0
  263. package/references/minimem/scripts/postbuild.js +35 -0
  264. package/references/minimem/src/__tests__/edge-cases.test.ts +371 -0
  265. package/references/minimem/src/__tests__/errors.test.ts +265 -0
  266. package/references/minimem/src/__tests__/helpers.ts +199 -0
  267. package/references/minimem/src/__tests__/internal.test.ts +407 -0
  268. package/references/minimem/src/__tests__/knowledge.test.ts +287 -0
  269. package/references/minimem/src/__tests__/minimem.integration.test.ts +1127 -0
  270. package/references/minimem/src/__tests__/session.test.ts +190 -0
  271. package/references/minimem/src/cli/__tests__/commands.test.ts +759 -0
  272. package/references/minimem/src/cli/commands/__tests__/conflicts.test.ts +141 -0
  273. package/references/minimem/src/cli/commands/append.ts +76 -0
  274. package/references/minimem/src/cli/commands/config.ts +262 -0
  275. package/references/minimem/src/cli/commands/conflicts.ts +413 -0
  276. package/references/minimem/src/cli/commands/daemon.ts +169 -0
  277. package/references/minimem/src/cli/commands/index.ts +12 -0
  278. package/references/minimem/src/cli/commands/init.ts +88 -0
  279. package/references/minimem/src/cli/commands/mcp.ts +177 -0
  280. package/references/minimem/src/cli/commands/push-pull.ts +213 -0
  281. package/references/minimem/src/cli/commands/search.ts +158 -0
  282. package/references/minimem/src/cli/commands/status.ts +84 -0
  283. package/references/minimem/src/cli/commands/sync-init.ts +290 -0
  284. package/references/minimem/src/cli/commands/sync.ts +70 -0
  285. package/references/minimem/src/cli/commands/upsert.ts +197 -0
  286. package/references/minimem/src/cli/config.ts +584 -0
  287. package/references/minimem/src/cli/index.ts +264 -0
  288. package/references/minimem/src/cli/shared.ts +161 -0
  289. package/references/minimem/src/cli/sync/__tests__/central.test.ts +152 -0
  290. package/references/minimem/src/cli/sync/__tests__/conflicts.test.ts +209 -0
  291. package/references/minimem/src/cli/sync/__tests__/daemon.test.ts +118 -0
  292. package/references/minimem/src/cli/sync/__tests__/detection.test.ts +207 -0
  293. package/references/minimem/src/cli/sync/__tests__/integration.test.ts +476 -0
  294. package/references/minimem/src/cli/sync/__tests__/registry.test.ts +363 -0
  295. package/references/minimem/src/cli/sync/__tests__/state.test.ts +255 -0
  296. package/references/minimem/src/cli/sync/__tests__/validation.test.ts +193 -0
  297. package/references/minimem/src/cli/sync/__tests__/watcher.test.ts +178 -0
  298. package/references/minimem/src/cli/sync/central.ts +292 -0
  299. package/references/minimem/src/cli/sync/conflicts.ts +204 -0
  300. package/references/minimem/src/cli/sync/daemon.ts +407 -0
  301. package/references/minimem/src/cli/sync/detection.ts +138 -0
  302. package/references/minimem/src/cli/sync/index.ts +107 -0
  303. package/references/minimem/src/cli/sync/operations.ts +373 -0
  304. package/references/minimem/src/cli/sync/registry.ts +279 -0
  305. package/references/minimem/src/cli/sync/state.ts +355 -0
  306. package/references/minimem/src/cli/sync/validation.ts +206 -0
  307. package/references/minimem/src/cli/sync/watcher.ts +234 -0
  308. package/references/minimem/src/cli/version.ts +34 -0
  309. package/references/minimem/src/core/index.ts +9 -0
  310. package/references/minimem/src/core/indexer.ts +628 -0
  311. package/references/minimem/src/core/searcher.ts +221 -0
  312. package/references/minimem/src/db/schema.ts +183 -0
  313. package/references/minimem/src/db/sqlite-vec.ts +24 -0
  314. package/references/minimem/src/embeddings/__tests__/embeddings.test.ts +431 -0
  315. package/references/minimem/src/embeddings/batch-gemini.ts +392 -0
  316. package/references/minimem/src/embeddings/batch-openai.ts +409 -0
  317. package/references/minimem/src/embeddings/embeddings.ts +434 -0
  318. package/references/minimem/src/index.ts +109 -0
  319. package/references/minimem/src/internal.ts +299 -0
  320. package/references/minimem/src/minimem.ts +1276 -0
  321. package/references/minimem/src/search/__tests__/hybrid.test.ts +247 -0
  322. package/references/minimem/src/search/graph.ts +234 -0
  323. package/references/minimem/src/search/hybrid.ts +151 -0
  324. package/references/minimem/src/search/search.ts +256 -0
  325. package/references/minimem/src/server/__tests__/mcp.test.ts +341 -0
  326. package/references/minimem/src/server/__tests__/tools.test.ts +364 -0
  327. package/references/minimem/src/server/mcp.ts +326 -0
  328. package/references/minimem/src/server/tools.ts +720 -0
  329. package/references/minimem/src/session.ts +460 -0
  330. package/references/minimem/tsconfig.json +19 -0
  331. package/references/minimem/tsup.config.ts +26 -0
  332. package/references/minimem/vitest.config.ts +24 -0
  333. package/references/openteams/.claude/settings.json +6 -0
  334. package/references/openteams/README.md +1 -0
  335. package/references/openteams/SKILL.md +341 -0
  336. package/references/openteams/design.md +411 -0
  337. package/references/openteams/examples/bmad-method/prompts/analyst/ROLE.md +16 -0
  338. package/references/openteams/examples/bmad-method/prompts/analyst/SOUL.md +5 -0
  339. package/references/openteams/examples/bmad-method/prompts/architect/ROLE.md +24 -0
  340. package/references/openteams/examples/bmad-method/prompts/architect/SOUL.md +5 -0
  341. package/references/openteams/examples/bmad-method/prompts/developer/ROLE.md +25 -0
  342. package/references/openteams/examples/bmad-method/prompts/developer/SOUL.md +5 -0
  343. package/references/openteams/examples/bmad-method/prompts/master/ROLE.md +21 -0
  344. package/references/openteams/examples/bmad-method/prompts/master/SOUL.md +5 -0
  345. package/references/openteams/examples/bmad-method/prompts/pm/ROLE.md +20 -0
  346. package/references/openteams/examples/bmad-method/prompts/pm/SOUL.md +5 -0
  347. package/references/openteams/examples/bmad-method/prompts/qa/ROLE.md +17 -0
  348. package/references/openteams/examples/bmad-method/prompts/qa/SOUL.md +5 -0
  349. package/references/openteams/examples/bmad-method/prompts/quick-flow-dev/ROLE.md +23 -0
  350. package/references/openteams/examples/bmad-method/prompts/quick-flow-dev/SOUL.md +5 -0
  351. package/references/openteams/examples/bmad-method/prompts/scrum-master/ROLE.md +27 -0
  352. package/references/openteams/examples/bmad-method/prompts/scrum-master/SOUL.md +5 -0
  353. package/references/openteams/examples/bmad-method/prompts/tech-writer/ROLE.md +21 -0
  354. package/references/openteams/examples/bmad-method/prompts/tech-writer/SOUL.md +5 -0
  355. package/references/openteams/examples/bmad-method/prompts/ux-designer/ROLE.md +16 -0
  356. package/references/openteams/examples/bmad-method/prompts/ux-designer/SOUL.md +5 -0
  357. package/references/openteams/examples/bmad-method/roles/analyst.yaml +9 -0
  358. package/references/openteams/examples/bmad-method/roles/architect.yaml +9 -0
  359. package/references/openteams/examples/bmad-method/roles/developer.yaml +8 -0
  360. package/references/openteams/examples/bmad-method/roles/master.yaml +8 -0
  361. package/references/openteams/examples/bmad-method/roles/pm.yaml +9 -0
  362. package/references/openteams/examples/bmad-method/roles/qa.yaml +8 -0
  363. package/references/openteams/examples/bmad-method/roles/quick-flow-dev.yaml +8 -0
  364. package/references/openteams/examples/bmad-method/roles/scrum-master.yaml +9 -0
  365. package/references/openteams/examples/bmad-method/roles/tech-writer.yaml +8 -0
  366. package/references/openteams/examples/bmad-method/roles/ux-designer.yaml +8 -0
  367. package/references/openteams/examples/bmad-method/team.yaml +161 -0
  368. package/references/openteams/examples/get-shit-done/prompts/codebase-mapper/ROLE.md +17 -0
  369. package/references/openteams/examples/get-shit-done/prompts/codebase-mapper/SOUL.md +5 -0
  370. package/references/openteams/examples/get-shit-done/prompts/debugger/ROLE.md +25 -0
  371. package/references/openteams/examples/get-shit-done/prompts/debugger/SOUL.md +5 -0
  372. package/references/openteams/examples/get-shit-done/prompts/executor/ROLE.md +34 -0
  373. package/references/openteams/examples/get-shit-done/prompts/executor/SOUL.md +5 -0
  374. package/references/openteams/examples/get-shit-done/prompts/integration-checker/ROLE.md +18 -0
  375. package/references/openteams/examples/get-shit-done/prompts/integration-checker/SOUL.md +3 -0
  376. package/references/openteams/examples/get-shit-done/prompts/orchestrator/ROLE.md +42 -0
  377. package/references/openteams/examples/get-shit-done/prompts/orchestrator/SOUL.md +5 -0
  378. package/references/openteams/examples/get-shit-done/prompts/phase-researcher/ROLE.md +15 -0
  379. package/references/openteams/examples/get-shit-done/prompts/phase-researcher/SOUL.md +3 -0
  380. package/references/openteams/examples/get-shit-done/prompts/plan-checker/ROLE.md +17 -0
  381. package/references/openteams/examples/get-shit-done/prompts/plan-checker/SOUL.md +3 -0
  382. package/references/openteams/examples/get-shit-done/prompts/planner/ROLE.md +28 -0
  383. package/references/openteams/examples/get-shit-done/prompts/planner/SOUL.md +5 -0
  384. package/references/openteams/examples/get-shit-done/prompts/project-researcher/ROLE.md +16 -0
  385. package/references/openteams/examples/get-shit-done/prompts/project-researcher/SOUL.md +3 -0
  386. package/references/openteams/examples/get-shit-done/prompts/research-synthesizer/ROLE.md +13 -0
  387. package/references/openteams/examples/get-shit-done/prompts/research-synthesizer/SOUL.md +3 -0
  388. package/references/openteams/examples/get-shit-done/prompts/roadmapper/ROLE.md +14 -0
  389. package/references/openteams/examples/get-shit-done/prompts/roadmapper/SOUL.md +3 -0
  390. package/references/openteams/examples/get-shit-done/prompts/verifier/ROLE.md +19 -0
  391. package/references/openteams/examples/get-shit-done/prompts/verifier/SOUL.md +5 -0
  392. package/references/openteams/examples/get-shit-done/roles/codebase-mapper.yaml +8 -0
  393. package/references/openteams/examples/get-shit-done/roles/debugger.yaml +8 -0
  394. package/references/openteams/examples/get-shit-done/roles/executor.yaml +8 -0
  395. package/references/openteams/examples/get-shit-done/roles/integration-checker.yaml +8 -0
  396. package/references/openteams/examples/get-shit-done/roles/orchestrator.yaml +9 -0
  397. package/references/openteams/examples/get-shit-done/roles/phase-researcher.yaml +7 -0
  398. package/references/openteams/examples/get-shit-done/roles/plan-checker.yaml +8 -0
  399. package/references/openteams/examples/get-shit-done/roles/planner.yaml +8 -0
  400. package/references/openteams/examples/get-shit-done/roles/project-researcher.yaml +8 -0
  401. package/references/openteams/examples/get-shit-done/roles/research-synthesizer.yaml +7 -0
  402. package/references/openteams/examples/get-shit-done/roles/roadmapper.yaml +7 -0
  403. package/references/openteams/examples/get-shit-done/roles/verifier.yaml +8 -0
  404. package/references/openteams/examples/get-shit-done/team.yaml +154 -0
  405. package/references/openteams/package-lock.json +2181 -0
  406. package/references/openteams/package.json +48 -0
  407. package/references/openteams/schema/role.schema.json +125 -0
  408. package/references/openteams/schema/team.schema.json +284 -0
  409. package/references/openteams/src/cli/agent.ts +104 -0
  410. package/references/openteams/src/cli/cli.test.ts +381 -0
  411. package/references/openteams/src/cli/generate.ts +220 -0
  412. package/references/openteams/src/cli/message.ts +241 -0
  413. package/references/openteams/src/cli/task.ts +154 -0
  414. package/references/openteams/src/cli/team.ts +104 -0
  415. package/references/openteams/src/cli/template.ts +207 -0
  416. package/references/openteams/src/cli.ts +45 -0
  417. package/references/openteams/src/db/database.test.ts +185 -0
  418. package/references/openteams/src/db/database.ts +240 -0
  419. package/references/openteams/src/generators/agent-prompt-generator.test.ts +332 -0
  420. package/references/openteams/src/generators/agent-prompt-generator.ts +521 -0
  421. package/references/openteams/src/generators/package-generator.test.ts +129 -0
  422. package/references/openteams/src/generators/package-generator.ts +102 -0
  423. package/references/openteams/src/generators/skill-generator.test.ts +246 -0
  424. package/references/openteams/src/generators/skill-generator.ts +374 -0
  425. package/references/openteams/src/index.ts +104 -0
  426. package/references/openteams/src/services/agent-service.test.ts +158 -0
  427. package/references/openteams/src/services/agent-service.ts +84 -0
  428. package/references/openteams/src/services/communication-service.test.ts +455 -0
  429. package/references/openteams/src/services/communication-service.ts +371 -0
  430. package/references/openteams/src/services/message-service.test.ts +342 -0
  431. package/references/openteams/src/services/message-service.ts +203 -0
  432. package/references/openteams/src/services/task-service.test.ts +434 -0
  433. package/references/openteams/src/services/task-service.ts +239 -0
  434. package/references/openteams/src/services/team-service.test.ts +181 -0
  435. package/references/openteams/src/services/team-service.ts +139 -0
  436. package/references/openteams/src/services/template-service.test.ts +306 -0
  437. package/references/openteams/src/services/template-service.ts +182 -0
  438. package/references/openteams/src/spawner/acp-factory.ts +96 -0
  439. package/references/openteams/src/spawner/interface.ts +31 -0
  440. package/references/openteams/src/spawner/mock.test.ts +93 -0
  441. package/references/openteams/src/spawner/mock.ts +59 -0
  442. package/references/openteams/src/template/loader.test.ts +1319 -0
  443. package/references/openteams/src/template/loader.ts +698 -0
  444. package/references/openteams/src/template/types.ts +200 -0
  445. package/references/openteams/src/types.ts +205 -0
  446. package/references/openteams/tsconfig.json +18 -0
  447. package/references/openteams/vitest.config.ts +9 -0
  448. package/references/skill-tree/.claude/settings.json +6 -0
  449. package/references/skill-tree/.sudocode/issues.jsonl +11 -0
  450. package/references/skill-tree/.sudocode/specs.jsonl +1 -0
  451. package/references/skill-tree/CLAUDE.md +150 -0
  452. package/references/skill-tree/README.md +324 -0
  453. package/references/skill-tree/docs/GAPS_v1.md +221 -0
  454. package/references/skill-tree/docs/INTEGRATION_PLAN.md +467 -0
  455. package/references/skill-tree/docs/TODOS.md +91 -0
  456. package/references/skill-tree/docs/anthropic_skill_guide.md +1364 -0
  457. package/references/skill-tree/docs/design/federated-skill-trees.md +524 -0
  458. package/references/skill-tree/docs/design/multi-agent-sync.md +759 -0
  459. package/references/skill-tree/docs/scraper/BRAINSTORM.md +583 -0
  460. package/references/skill-tree/docs/scraper/POC_PLAN.md +420 -0
  461. package/references/skill-tree/docs/scraper/README.md +170 -0
  462. package/references/skill-tree/examples/basic-usage.ts +190 -0
  463. package/references/skill-tree/package-lock.json +1509 -0
  464. package/references/skill-tree/package.json +66 -0
  465. package/references/skill-tree/scraper/README.md +123 -0
  466. package/references/skill-tree/scraper/docs/DESIGN.md +683 -0
  467. package/references/skill-tree/scraper/docs/PLAN.md +336 -0
  468. package/references/skill-tree/scraper/drizzle.config.ts +10 -0
  469. package/references/skill-tree/scraper/package-lock.json +6329 -0
  470. package/references/skill-tree/scraper/package.json +68 -0
  471. package/references/skill-tree/scraper/test/fixtures/invalid-skill/missing-description.md +7 -0
  472. package/references/skill-tree/scraper/test/fixtures/invalid-skill/missing-name.md +7 -0
  473. package/references/skill-tree/scraper/test/fixtures/minimal-skill/SKILL.md +27 -0
  474. package/references/skill-tree/scraper/test/fixtures/skill-json/SKILL.json +21 -0
  475. package/references/skill-tree/scraper/test/fixtures/skill-with-meta/SKILL.md +54 -0
  476. package/references/skill-tree/scraper/test/fixtures/skill-with-meta/_meta.json +24 -0
  477. package/references/skill-tree/scraper/test/fixtures/valid-skill/SKILL.md +93 -0
  478. package/references/skill-tree/scraper/test/fixtures/valid-skill/_meta.json +22 -0
  479. package/references/skill-tree/scraper/tsup.config.ts +14 -0
  480. package/references/skill-tree/scraper/vitest.config.ts +17 -0
  481. package/references/skill-tree/scripts/convert-to-vitest.ts +166 -0
  482. package/references/skill-tree/skills/skill-writer/SKILL.md +339 -0
  483. package/references/skill-tree/skills/skill-writer/references/examples.md +326 -0
  484. package/references/skill-tree/skills/skill-writer/references/patterns.md +210 -0
  485. package/references/skill-tree/skills/skill-writer/references/quality-checklist.md +123 -0
  486. package/references/skill-tree/test/run-all.ts +106 -0
  487. package/references/skill-tree/test/utils.ts +128 -0
  488. package/references/skill-tree/vitest.config.ts +16 -0
  489. package/src/__tests__/e2e/agent-spawn-visibility.e2e.test.ts +761 -0
  490. package/src/__tests__/e2e/full-agent-conflict-resolution.e2e.test.ts +2 -2
  491. package/src/__tests__/e2e/mcp-thin-client-bridge.e2e.test.ts +304 -0
  492. package/src/__tests__/e2e/mcp-tools-available.e2e.test.ts +324 -0
  493. package/src/__tests__/e2e/multi-agent.e2e.test.ts +5 -5
  494. package/src/__tests__/e2e/spawn-session-streaming.e2e.test.ts +563 -0
  495. package/src/acp/__tests__/integration.test.ts +56 -31
  496. package/src/acp/__tests__/macro-agent.test.ts +16 -7
  497. package/src/acp/macro-agent.ts +170 -36
  498. package/src/acp/types.ts +46 -1
  499. package/src/agent/__tests__/agent-manager.test.ts +228 -2
  500. package/src/agent/agent-manager.ts +809 -285
  501. package/src/agent/types.ts +12 -1
  502. package/src/api/__tests__/server.test.ts +203 -4
  503. package/src/api/server.ts +169 -10
  504. package/src/api/types.ts +3 -1
  505. package/src/auth/__tests__/token.test.ts +100 -0
  506. package/src/auth/index.ts +1 -0
  507. package/src/auth/token.ts +82 -0
  508. package/src/cli/__tests__/acp.test.ts +1 -1
  509. package/src/cli/__tests__/stable-instance-id.test.ts +1 -1
  510. package/src/cli/acp.ts +197 -72
  511. package/src/cli/index.ts +125 -15
  512. package/src/cli/mcp.ts +315 -197
  513. package/src/cli/parse-args.ts +54 -0
  514. package/src/cli/stable-instance-id.ts +14 -0
  515. package/src/config/project-config.ts +214 -27
  516. package/src/index.ts +3 -0
  517. package/src/lifecycle/__tests__/cascade-termination.test.ts +1 -1
  518. package/src/lifecycle/__tests__/handlers.test.ts +53 -0
  519. package/src/lifecycle/handlers/index.ts +25 -8
  520. package/src/lifecycle/types.ts +3 -0
  521. package/src/map/adapter/__tests__/acp-over-map-cancel.test.ts +22 -4
  522. package/src/map/adapter/__tests__/acp-over-map-getmodels.test.ts +355 -0
  523. package/src/map/adapter/__tests__/acp-over-map-history.test.ts +263 -0
  524. package/src/map/adapter/__tests__/acp-over-map-persistence.e2e.test.ts +1 -1
  525. package/src/map/adapter/__tests__/event-broadcast.test.ts +420 -0
  526. package/src/map/adapter/__tests__/event-log.test.ts +527 -0
  527. package/src/map/adapter/__tests__/event-translator.test.ts +3 -3
  528. package/src/map/adapter/__tests__/extensions.test.ts +408 -0
  529. package/src/map/adapter/__tests__/map-adapter.test.ts +99 -0
  530. package/src/map/adapter/__tests__/mcp-bridge.test.ts +1187 -0
  531. package/src/map/adapter/__tests__/multi-client-broadcast.test.ts +711 -0
  532. package/src/map/adapter/__tests__/stream-extensions.test.ts +494 -0
  533. package/src/map/adapter/__tests__/websocket-integration.test.ts +218 -0
  534. package/src/map/adapter/acp-over-map.ts +678 -66
  535. package/src/map/adapter/connection-manager.ts +3 -0
  536. package/src/map/adapter/event-log.ts +208 -0
  537. package/src/map/adapter/event-translator.ts +6 -6
  538. package/src/map/adapter/extensions/agent-lifecycle.ts +267 -0
  539. package/src/map/adapter/extensions/index.ts +96 -0
  540. package/src/map/adapter/extensions/mcp-bridge.ts +995 -0
  541. package/src/map/adapter/extensions/streams.ts +839 -0
  542. package/src/map/adapter/extensions/task.ts +11 -0
  543. package/src/map/adapter/extensions/update-metadata.ts +126 -0
  544. package/src/map/adapter/index.ts +33 -0
  545. package/src/map/adapter/interface.ts +2 -0
  546. package/src/map/adapter/map-adapter.ts +312 -47
  547. package/src/map/adapter/subscription-manager.ts +5 -1
  548. package/src/map/adapter/types.ts +10 -1
  549. package/src/mcp/__tests__/map-client.test.ts +386 -0
  550. package/src/mcp/__tests__/mcp-server-thin-client.test.ts +368 -0
  551. package/src/mcp/__tests__/mcp-server.test.ts +100 -1
  552. package/src/mcp/map-client.ts +177 -0
  553. package/src/mcp/mcp-server.ts +205 -103
  554. package/src/mcp/tools/done.ts +19 -0
  555. package/src/mcp/types.ts +6 -1
  556. package/src/metrics/metrics.ts +1 -1
  557. package/src/monitor/__tests__/stale-agent-flow.integration.test.ts +1 -1
  558. package/src/roles/__tests__/config-loader.test.ts +7 -7
  559. package/src/roles/builtin/coordinator.ts +2 -0
  560. package/src/roles/builtin/integrator.ts +2 -0
  561. package/src/roles/builtin/worker.ts +3 -0
  562. package/src/roles/capabilities.ts +28 -7
  563. package/src/roles/config-loader.ts +8 -7
  564. package/src/roles/registry.ts +2 -2
  565. package/src/roles/types.ts +7 -0
  566. package/src/server/__tests__/combined-server.test.ts +94 -21
  567. package/src/server/combined-server.ts +203 -33
  568. package/src/steering/__tests__/steering-integration.test.ts +1 -1
  569. package/src/store/__tests__/event-store-oob.test.ts +109 -0
  570. package/src/store/__tests__/event-store.test.ts +196 -1
  571. package/src/store/__tests__/instance.test.ts +3 -3
  572. package/src/store/event-store.ts +92 -23
  573. package/src/store/instance.ts +2 -2
  574. package/src/store/types/agents.ts +20 -0
  575. package/src/store/types/events.ts +1 -1
  576. package/src/task/backend/__tests__/create-task-backend.test.ts +225 -0
  577. package/src/task/backend/__tests__/e2e/unified-tool-provider-opentasks.e2e.test.ts +524 -0
  578. package/src/task/backend/__tests__/memory-pull-mode.test.ts +153 -0
  579. package/src/task/backend/__tests__/unified-tool-provider.test.ts +579 -0
  580. package/src/task/backend/index.ts +156 -106
  581. package/src/task/backend/memory.ts +4 -0
  582. package/src/task/backend/opentasks/__tests__/backend.test.ts +968 -0
  583. package/src/task/backend/opentasks/__tests__/daemon-manager.test.ts +406 -0
  584. package/src/task/backend/opentasks/__tests__/mapping.test.ts +84 -0
  585. package/src/task/backend/opentasks/__tests__/opentasks-backend.e2e.test.ts +1338 -0
  586. package/src/task/backend/opentasks/backend.ts +1323 -0
  587. package/src/task/backend/opentasks/client.ts +652 -0
  588. package/src/task/backend/opentasks/daemon-manager.ts +256 -0
  589. package/src/task/backend/opentasks/index.ts +69 -0
  590. package/src/task/backend/opentasks/mapping.ts +94 -0
  591. package/src/task/backend/types.ts +42 -66
  592. package/src/task/backend/unified-tool-provider.ts +779 -0
  593. package/src/teams/CLAUDE.md +180 -0
  594. package/src/teams/__tests__/cross-subsystem.integration.test.ts +1 -1
  595. package/src/teams/__tests__/e2e/workspace-isolation.e2e.test.ts +1263 -0
  596. package/src/teams/__tests__/team-manager.test.ts +814 -0
  597. package/src/teams/__tests__/team-system.test.ts +1291 -8
  598. package/src/teams/index.ts +21 -3
  599. package/src/teams/seed-defaults.ts +79 -0
  600. package/src/teams/team-loader.ts +202 -236
  601. package/src/teams/team-manager.ts +387 -0
  602. package/src/teams/team-runtime.ts +592 -121
  603. package/src/teams/types.ts +99 -200
  604. package/test_fixtures/README.md +2 -3
  605. package/test_fixtures/fixtures/index.ts +0 -3
  606. package/test_fixtures/fixtures/projects/project-with-specs.ts +7 -149
  607. package/test_fixtures/fixtures/repos/index.ts +1 -3
  608. package/test_fixtures/fixtures/repos/temp-repo-factory.ts +0 -116
  609. package/test_fixtures/fixtures/repos/types.ts +0 -11
  610. package/test_fixtures/harness/__tests__/fixtures.test.ts +10 -102
  611. package/test_fixtures/harness/__tests__/temp-repo-and-simulator.test.ts +0 -33
  612. package/test_fixtures/harness/simulator/agent-simulator.ts +4 -4
  613. package/vitest.config.ts +1 -1
  614. package/vitest.e2e.config.ts +1 -1
  615. package/vitest.setup.ts +1 -30
  616. package/.macro-agent/teams/self-driving/prompts/grinder.md +0 -27
  617. package/.macro-agent/teams/self-driving/prompts/judge.md +0 -27
  618. package/.macro-agent/teams/self-driving/prompts/planner.md +0 -33
  619. package/.macro-agent/teams/self-driving/roles/grinder.yaml +0 -17
  620. package/.macro-agent/teams/self-driving/roles/judge.yaml +0 -24
  621. package/.macro-agent/teams/self-driving/roles/planner.yaml +0 -18
  622. package/.macro-agent/teams/self-driving/team.yaml +0 -103
  623. package/.macro-agent/teams/structured/prompts/developer.md +0 -26
  624. package/.macro-agent/teams/structured/prompts/lead.md +0 -25
  625. package/.macro-agent/teams/structured/prompts/reviewer.md +0 -24
  626. package/.macro-agent/teams/structured/roles/developer.yaml +0 -12
  627. package/.macro-agent/teams/structured/roles/lead.yaml +0 -11
  628. package/.macro-agent/teams/structured/roles/reviewer.yaml +0 -19
  629. package/.macro-agent/teams/structured/team.yaml +0 -89
  630. package/docs/sudocode-integration.md +0 -383
  631. package/src/task/backend/__tests__/backend-parity.test.ts +0 -451
  632. package/src/task/backend/__tests__/tool-provider-edge-cases.test.ts +0 -430
  633. package/src/task/backend/__tests__/tool-provider.test.ts +0 -983
  634. package/src/task/backend/sudocode/__tests__/backend-edge-cases.test.ts +0 -575
  635. package/src/task/backend/sudocode/__tests__/backend.test.ts +0 -1194
  636. package/src/task/backend/sudocode/__tests__/client-integration.test.ts +0 -418
  637. package/src/task/backend/sudocode/__tests__/client.test.ts +0 -345
  638. package/src/task/backend/sudocode/__tests__/e2e/backend.e2e.test.ts +0 -753
  639. package/src/task/backend/sudocode/__tests__/e2e/server-client.e2e.test.ts +0 -680
  640. package/src/task/backend/sudocode/__tests__/e2e-workflow.test.ts +0 -666
  641. package/src/task/backend/sudocode/__tests__/integration/standalone-client.integration.test.ts +0 -396
  642. package/src/task/backend/sudocode/__tests__/integration/sudocode-cli.integration.test.ts +0 -328
  643. package/src/task/backend/sudocode/__tests__/integration/test-utils.ts +0 -175
  644. package/src/task/backend/sudocode/__tests__/mapping-edge-cases.test.ts +0 -265
  645. package/src/task/backend/sudocode/__tests__/server-client.test.ts +0 -675
  646. package/src/task/backend/sudocode/__tests__/sync-policy-edge-cases.test.ts +0 -521
  647. package/src/task/backend/sudocode/__tests__/sync-policy.test.ts +0 -519
  648. package/src/task/backend/sudocode/__tests__/tools.test.ts +0 -471
  649. package/src/task/backend/sudocode/backend.ts +0 -1237
  650. package/src/task/backend/sudocode/client.ts +0 -515
  651. package/src/task/backend/sudocode/index.ts +0 -120
  652. package/src/task/backend/sudocode/mapping.ts +0 -93
  653. package/src/task/backend/sudocode/server-client.ts +0 -522
  654. package/src/task/backend/sudocode/standalone-client.ts +0 -623
  655. package/src/task/backend/sudocode/sync-policy.ts +0 -387
  656. package/src/task/backend/sudocode/tools.ts +0 -896
  657. package/src/task/backend/tool-provider.ts +0 -506
  658. package/test_fixtures/fixtures/sudocode/index.ts +0 -29
  659. package/test_fixtures/fixtures/sudocode/issues.ts +0 -185
  660. package/test_fixtures/fixtures/sudocode/specs.ts +0 -159
@@ -0,0 +1,1319 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import os from "os";
5
+ import { TemplateLoader } from "./loader";
6
+
7
+ describe("TemplateLoader", () => {
8
+ let tmpDir: string;
9
+
10
+ beforeEach(() => {
11
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "openteams-test-"));
12
+ });
13
+
14
+ afterEach(() => {
15
+ fs.rmSync(tmpDir, { recursive: true, force: true });
16
+ });
17
+
18
+ function writeYaml(relPath: string, content: string): void {
19
+ const full = path.join(tmpDir, relPath);
20
+ fs.mkdirSync(path.dirname(full), { recursive: true });
21
+ fs.writeFileSync(full, content, "utf-8");
22
+ }
23
+
24
+ describe("load", () => {
25
+ it("loads a minimal valid template", () => {
26
+ writeYaml(
27
+ "team.yaml",
28
+ `
29
+ name: test-team
30
+ version: 1
31
+ roles:
32
+ - worker
33
+ topology:
34
+ root:
35
+ role: worker
36
+ `
37
+ );
38
+
39
+ const template = TemplateLoader.load(tmpDir);
40
+ expect(template.manifest.name).toBe("test-team");
41
+ expect(template.manifest.version).toBe(1);
42
+ expect(template.manifest.roles).toEqual(["worker"]);
43
+ expect(template.manifest.topology.root.role).toBe("worker");
44
+ expect(template.roles.size).toBe(1);
45
+ expect(template.roles.get("worker")!.name).toBe("worker");
46
+ });
47
+
48
+ it("loads a full self-driving template", () => {
49
+ writeYaml(
50
+ "team.yaml",
51
+ `
52
+ name: self-driving
53
+ description: "Autonomous codebase development"
54
+ version: 1
55
+ roles:
56
+ - planner
57
+ - grinder
58
+ - judge
59
+ topology:
60
+ root:
61
+ role: planner
62
+ prompt: prompts/planner.md
63
+ config:
64
+ model: sonnet
65
+ companions:
66
+ - role: judge
67
+ prompt: prompts/judge.md
68
+ config:
69
+ model: haiku
70
+ spawn_rules:
71
+ planner: [grinder, planner]
72
+ judge: []
73
+ grinder: []
74
+ communication:
75
+ channels:
76
+ task_updates:
77
+ description: "Task lifecycle events"
78
+ signals: [TASK_CREATED, TASK_COMPLETED, TASK_FAILED]
79
+ work_coordination:
80
+ description: "Work assignment"
81
+ signals: [WORK_ASSIGNED, WORKER_DONE]
82
+ subscriptions:
83
+ planner:
84
+ - channel: task_updates
85
+ - channel: work_coordination
86
+ signals: [WORKER_DONE]
87
+ judge:
88
+ - channel: task_updates
89
+ signals: [TASK_FAILED]
90
+ grinder:
91
+ - channel: work_coordination
92
+ signals: [WORK_ASSIGNED]
93
+ emissions:
94
+ planner: [TASK_CREATED, WORK_ASSIGNED]
95
+ judge: [FIXUP_CREATED]
96
+ grinder: [WORKER_DONE]
97
+ routing:
98
+ status: upstream
99
+ peers:
100
+ - from: judge
101
+ to: planner
102
+ via: direct
103
+ signals: [FIXUP_CREATED]
104
+ macro_agent:
105
+ task_assignment:
106
+ mode: pull
107
+ `
108
+ );
109
+
110
+ writeYaml("prompts/planner.md", "# Planner\nYou are the planner.");
111
+ writeYaml("prompts/judge.md", "# Judge\nYou evaluate quality.");
112
+
113
+ const template = TemplateLoader.load(tmpDir);
114
+ expect(template.manifest.name).toBe("self-driving");
115
+ expect(template.manifest.roles).toHaveLength(3);
116
+ expect(template.manifest.topology.companions).toHaveLength(1);
117
+ expect(template.manifest.topology.spawn_rules!.planner).toEqual([
118
+ "grinder",
119
+ "planner",
120
+ ]);
121
+ expect(template.manifest.communication!.channels!.task_updates.signals).toEqual([
122
+ "TASK_CREATED",
123
+ "TASK_COMPLETED",
124
+ "TASK_FAILED",
125
+ ]);
126
+ expect(template.prompts.get("planner")!.primary).toContain("You are the planner");
127
+ expect(template.prompts.get("judge")!.primary).toContain("evaluate quality");
128
+ expect(template.manifest.macro_agent).toEqual({
129
+ task_assignment: { mode: "pull" },
130
+ });
131
+ });
132
+
133
+ it("loads role definitions from roles/ directory", () => {
134
+ writeYaml(
135
+ "team.yaml",
136
+ `
137
+ name: with-roles
138
+ version: 1
139
+ roles:
140
+ - coder
141
+ topology:
142
+ root:
143
+ role: coder
144
+ `
145
+ );
146
+
147
+ writeYaml(
148
+ "roles/coder.yaml",
149
+ `
150
+ name: coder
151
+ extends: worker
152
+ display_name: "Code Writer"
153
+ description: "Writes and tests code"
154
+ capabilities:
155
+ add:
156
+ - file.write
157
+ - exec.test
158
+ remove:
159
+ - agent.spawn.worker
160
+ macro_agent:
161
+ workspace:
162
+ type: own
163
+ `
164
+ );
165
+
166
+ const template = TemplateLoader.load(tmpDir);
167
+ const coder = template.roles.get("coder")!;
168
+ expect(coder.displayName).toBe("Code Writer");
169
+ expect(coder.description).toBe("Writes and tests code");
170
+ expect(coder.extends).toBe("worker");
171
+ expect(coder.capabilities).toEqual(["file.write", "exec.test"]);
172
+ expect(coder.raw.macro_agent).toEqual({ workspace: { type: "own" } });
173
+ });
174
+
175
+ it("loads role with full capability list", () => {
176
+ writeYaml(
177
+ "team.yaml",
178
+ `
179
+ name: simple
180
+ version: 1
181
+ roles:
182
+ - runner
183
+ topology:
184
+ root:
185
+ role: runner
186
+ `
187
+ );
188
+
189
+ writeYaml(
190
+ "roles/runner.yaml",
191
+ `
192
+ name: runner
193
+ description: "Task runner"
194
+ capabilities:
195
+ - exec.build
196
+ - exec.test
197
+ - exec.lint
198
+ `
199
+ );
200
+
201
+ const template = TemplateLoader.load(tmpDir);
202
+ expect(template.roles.get("runner")!.capabilities).toEqual([
203
+ "exec.build",
204
+ "exec.test",
205
+ "exec.lint",
206
+ ]);
207
+ });
208
+
209
+ it("loads prompts by convention (prompts/<role>.md)", () => {
210
+ writeYaml(
211
+ "team.yaml",
212
+ `
213
+ name: conv
214
+ version: 1
215
+ roles:
216
+ - worker
217
+ topology:
218
+ root:
219
+ role: worker
220
+ `
221
+ );
222
+
223
+ writeYaml("prompts/worker.md", "# Worker\nDo work.");
224
+
225
+ const template = TemplateLoader.load(tmpDir);
226
+ expect(template.prompts.get("worker")!.primary).toContain("Do work");
227
+ expect(template.prompts.get("worker")!.additional).toEqual([]);
228
+ });
229
+
230
+ it("loads prompt directory with ROLE.md as primary", () => {
231
+ writeYaml(
232
+ "team.yaml",
233
+ `
234
+ name: dir-test
235
+ version: 1
236
+ roles:
237
+ - developer
238
+ topology:
239
+ root:
240
+ role: developer
241
+ `
242
+ );
243
+
244
+ writeYaml("prompts/developer/ROLE.md", "# Developer\nImplement features.");
245
+ writeYaml("prompts/developer/SOUL.md", "You are a pragmatic craftsman.");
246
+ writeYaml("prompts/developer/RULES.md", "Follow TDD. Write tests first.");
247
+
248
+ const template = TemplateLoader.load(tmpDir);
249
+ const prompts = template.prompts.get("developer")!;
250
+ expect(prompts.primary).toContain("Implement features");
251
+ expect(prompts.additional).toHaveLength(2);
252
+ // SOUL.md is always first among additional files
253
+ expect(prompts.additional[0].name).toBe("soul");
254
+ expect(prompts.additional[0].content).toContain("pragmatic craftsman");
255
+ expect(prompts.additional[1].name).toBe("RULES");
256
+ });
257
+
258
+ it("orders SOUL.md before other additional files", () => {
259
+ writeYaml(
260
+ "team.yaml",
261
+ `
262
+ name: soul-order-test
263
+ version: 1
264
+ roles:
265
+ - dev
266
+ topology:
267
+ root:
268
+ role: dev
269
+ `
270
+ );
271
+
272
+ writeYaml("prompts/dev/ROLE.md", "Build things.");
273
+ writeYaml("prompts/dev/SOUL.md", "You are creative.");
274
+ writeYaml("prompts/dev/aaa-first-alphabetically.md", "Coding standards.");
275
+
276
+ const template = TemplateLoader.load(tmpDir);
277
+ const prompts = template.prompts.get("dev")!;
278
+ expect(prompts.primary).toContain("Build things");
279
+ // SOUL.md should come first despite aaa sorting earlier alphabetically
280
+ expect(prompts.additional[0].name).toBe("soul");
281
+ expect(prompts.additional[1].name).toBe("aaa-first-alphabetically");
282
+ });
283
+
284
+ it("falls back to prompt.md when ROLE.md is absent", () => {
285
+ writeYaml(
286
+ "team.yaml",
287
+ `
288
+ name: fallback-test
289
+ version: 1
290
+ roles:
291
+ - tester
292
+ topology:
293
+ root:
294
+ role: tester
295
+ `
296
+ );
297
+
298
+ writeYaml("prompts/tester/prompt.md", "Run all tests.");
299
+ writeYaml("prompts/tester/SOUL.md", "Break things on purpose.");
300
+
301
+ const template = TemplateLoader.load(tmpDir);
302
+ const prompts = template.prompts.get("tester")!;
303
+ expect(prompts.primary).toContain("Run all tests");
304
+ expect(prompts.additional).toHaveLength(1);
305
+ expect(prompts.additional[0].name).toBe("soul");
306
+ });
307
+
308
+ it("uses first file alphabetically when neither ROLE.md nor prompt.md exist", () => {
309
+ writeYaml(
310
+ "team.yaml",
311
+ `
312
+ name: alpha-test
313
+ version: 1
314
+ roles:
315
+ - tester
316
+ topology:
317
+ root:
318
+ role: tester
319
+ `
320
+ );
321
+
322
+ writeYaml("prompts/tester/instructions.md", "Run all tests.");
323
+ writeYaml("prompts/tester/SOUL.md", "Break things on purpose.");
324
+
325
+ const template = TemplateLoader.load(tmpDir);
326
+ const prompts = template.prompts.get("tester")!;
327
+ // SOUL.md sorts before instructions.md (uppercase < lowercase in ASCII)
328
+ // so SOUL.md is picked as primary in the alphabetical fallback
329
+ expect(prompts.primary).toContain("Break things on purpose");
330
+ expect(prompts.additional).toHaveLength(1);
331
+ expect(prompts.additional[0].name).toBe("instructions");
332
+ });
333
+
334
+ it("respects explicit prompts ordering from role YAML", () => {
335
+ writeYaml(
336
+ "team.yaml",
337
+ `
338
+ name: ordered-test
339
+ version: 1
340
+ roles:
341
+ - coder
342
+ topology:
343
+ root:
344
+ role: coder
345
+ `
346
+ );
347
+
348
+ writeYaml(
349
+ "roles/coder.yaml",
350
+ `
351
+ name: coder
352
+ description: "A coder"
353
+ prompts:
354
+ - SOUL.md
355
+ - ROLE.md
356
+ - RULES.md
357
+ `
358
+ );
359
+
360
+ writeYaml("prompts/coder/ROLE.md", "Write code.");
361
+ writeYaml("prompts/coder/SOUL.md", "You are meticulous.");
362
+ writeYaml("prompts/coder/RULES.md", "Use TypeScript.");
363
+
364
+ const template = TemplateLoader.load(tmpDir);
365
+ const prompts = template.prompts.get("coder")!;
366
+ // SOUL.md is first in the list, so it becomes primary
367
+ expect(prompts.primary).toContain("meticulous");
368
+ expect(prompts.additional).toHaveLength(2);
369
+ expect(prompts.additional[0].name).toBe("ROLE");
370
+ expect(prompts.additional[1].name).toBe("RULES");
371
+ });
372
+
373
+ it("prefers prompt directory over single file", () => {
374
+ writeYaml(
375
+ "team.yaml",
376
+ `
377
+ name: priority-test
378
+ version: 1
379
+ roles:
380
+ - worker
381
+ topology:
382
+ root:
383
+ role: worker
384
+ `
385
+ );
386
+
387
+ // Both exist — directory should win
388
+ writeYaml("prompts/worker.md", "Single file prompt.");
389
+ writeYaml("prompts/worker/ROLE.md", "Directory prompt.");
390
+ writeYaml("prompts/worker/SOUL.md", "Directory soul.");
391
+
392
+ const template = TemplateLoader.load(tmpDir);
393
+ const prompts = template.prompts.get("worker")!;
394
+ expect(prompts.primary).toContain("Directory prompt");
395
+ expect(prompts.additional).toHaveLength(1);
396
+ expect(prompts.additional[0].name).toBe("soul");
397
+ });
398
+ });
399
+
400
+ describe("validation", () => {
401
+ it("throws when team.yaml is missing", () => {
402
+ expect(() => TemplateLoader.load(tmpDir)).toThrow("team.yaml not found");
403
+ });
404
+
405
+ it("throws when name is missing", () => {
406
+ writeYaml(
407
+ "team.yaml",
408
+ `
409
+ version: 1
410
+ roles:
411
+ - worker
412
+ topology:
413
+ root:
414
+ role: worker
415
+ `
416
+ );
417
+
418
+ expect(() => TemplateLoader.load(tmpDir)).toThrow("missing required field: name");
419
+ });
420
+
421
+ it("throws when roles list is empty", () => {
422
+ writeYaml(
423
+ "team.yaml",
424
+ `
425
+ name: bad
426
+ version: 1
427
+ roles: []
428
+ topology:
429
+ root:
430
+ role: worker
431
+ `
432
+ );
433
+
434
+ expect(() => TemplateLoader.load(tmpDir)).toThrow("at least one role");
435
+ });
436
+
437
+ it("throws when topology.root.role is not in roles", () => {
438
+ writeYaml(
439
+ "team.yaml",
440
+ `
441
+ name: bad
442
+ version: 1
443
+ roles:
444
+ - worker
445
+ topology:
446
+ root:
447
+ role: unknown
448
+ `
449
+ );
450
+
451
+ expect(() => TemplateLoader.load(tmpDir)).toThrow(
452
+ 'topology.root.role "unknown" is not in the roles list'
453
+ );
454
+ });
455
+
456
+ it("throws when companion role is not in roles", () => {
457
+ writeYaml(
458
+ "team.yaml",
459
+ `
460
+ name: bad
461
+ version: 1
462
+ roles:
463
+ - worker
464
+ topology:
465
+ root:
466
+ role: worker
467
+ companions:
468
+ - role: ghost
469
+ `
470
+ );
471
+
472
+ expect(() => TemplateLoader.load(tmpDir)).toThrow(
473
+ 'topology.companions role "ghost" is not in the roles list'
474
+ );
475
+ });
476
+
477
+ it("throws when spawn_rules reference unknown role", () => {
478
+ writeYaml(
479
+ "team.yaml",
480
+ `
481
+ name: bad
482
+ version: 1
483
+ roles:
484
+ - worker
485
+ topology:
486
+ root:
487
+ role: worker
488
+ spawn_rules:
489
+ worker: [ghost]
490
+ `
491
+ );
492
+
493
+ expect(() => TemplateLoader.load(tmpDir)).toThrow(
494
+ 'spawn_rules "worker" references unknown role "ghost"'
495
+ );
496
+ });
497
+
498
+ it("throws when subscription references unknown channel", () => {
499
+ writeYaml(
500
+ "team.yaml",
501
+ `
502
+ name: bad
503
+ version: 1
504
+ roles:
505
+ - worker
506
+ topology:
507
+ root:
508
+ role: worker
509
+ communication:
510
+ channels: {}
511
+ subscriptions:
512
+ worker:
513
+ - channel: nonexistent
514
+ `
515
+ );
516
+
517
+ expect(() => TemplateLoader.load(tmpDir)).toThrow(
518
+ 'references unknown channel "nonexistent"'
519
+ );
520
+ });
521
+
522
+ it("throws when subscription role is not in roles", () => {
523
+ writeYaml(
524
+ "team.yaml",
525
+ `
526
+ name: bad
527
+ version: 1
528
+ roles:
529
+ - worker
530
+ topology:
531
+ root:
532
+ role: worker
533
+ communication:
534
+ channels:
535
+ ch1:
536
+ signals: [SIG]
537
+ subscriptions:
538
+ ghost:
539
+ - channel: ch1
540
+ `
541
+ );
542
+
543
+ expect(() => TemplateLoader.load(tmpDir)).toThrow(
544
+ 'subscriptions key "ghost" is not in the roles list'
545
+ );
546
+ });
547
+
548
+ it("throws when peer route references unknown role", () => {
549
+ writeYaml(
550
+ "team.yaml",
551
+ `
552
+ name: bad
553
+ version: 1
554
+ roles:
555
+ - worker
556
+ topology:
557
+ root:
558
+ role: worker
559
+ communication:
560
+ routing:
561
+ peers:
562
+ - from: worker
563
+ to: ghost
564
+ via: direct
565
+ `
566
+ );
567
+
568
+ expect(() => TemplateLoader.load(tmpDir)).toThrow(
569
+ 'routing.peers.to "ghost" is not in the roles list'
570
+ );
571
+ });
572
+ });
573
+
574
+ describe("role inheritance", () => {
575
+ it("resolves single-level inheritance with add/remove", () => {
576
+ writeYaml(
577
+ "team.yaml",
578
+ `
579
+ name: inherit-test
580
+ version: 1
581
+ roles:
582
+ - senior
583
+ - junior
584
+ topology:
585
+ root:
586
+ role: senior
587
+ `
588
+ );
589
+
590
+ writeYaml(
591
+ "roles/senior.yaml",
592
+ `
593
+ name: senior
594
+ capabilities:
595
+ - code
596
+ - review
597
+ - deploy
598
+ `
599
+ );
600
+
601
+ writeYaml(
602
+ "roles/junior.yaml",
603
+ `
604
+ name: junior
605
+ extends: senior
606
+ capabilities:
607
+ add:
608
+ - code
609
+ - debug
610
+ remove:
611
+ - deploy
612
+ `
613
+ );
614
+
615
+ const template = TemplateLoader.load(tmpDir);
616
+ const junior = template.roles.get("junior")!;
617
+ expect(junior.capabilities.sort()).toEqual(["code", "debug", "review"]);
618
+ });
619
+
620
+ it("resolves multi-level inheritance (A extends B extends C)", () => {
621
+ writeYaml(
622
+ "team.yaml",
623
+ `
624
+ name: multi-inherit
625
+ version: 1
626
+ roles:
627
+ - base
628
+ - mid
629
+ - leaf
630
+ topology:
631
+ root:
632
+ role: base
633
+ `
634
+ );
635
+
636
+ writeYaml(
637
+ "roles/base.yaml",
638
+ `
639
+ name: base
640
+ capabilities:
641
+ - read
642
+ - write
643
+ - admin
644
+ `
645
+ );
646
+
647
+ writeYaml(
648
+ "roles/mid.yaml",
649
+ `
650
+ name: mid
651
+ extends: base
652
+ capabilities:
653
+ add:
654
+ - build
655
+ remove:
656
+ - admin
657
+ `
658
+ );
659
+
660
+ writeYaml(
661
+ "roles/leaf.yaml",
662
+ `
663
+ name: leaf
664
+ extends: mid
665
+ capabilities:
666
+ add:
667
+ - deploy
668
+ remove:
669
+ - write
670
+ `
671
+ );
672
+
673
+ const template = TemplateLoader.load(tmpDir);
674
+ // base: [read, write, admin]
675
+ // mid: base + build - admin = [read, write, build]
676
+ // leaf: mid + deploy - write = [read, build, deploy]
677
+ expect(template.roles.get("mid")!.capabilities.sort()).toEqual(
678
+ ["build", "read", "write"]
679
+ );
680
+ expect(template.roles.get("leaf")!.capabilities.sort()).toEqual(
681
+ ["build", "deploy", "read"]
682
+ );
683
+ });
684
+
685
+ it("keeps explicit capability array as override (no merge)", () => {
686
+ writeYaml(
687
+ "team.yaml",
688
+ `
689
+ name: override-test
690
+ version: 1
691
+ roles:
692
+ - parent
693
+ - child
694
+ topology:
695
+ root:
696
+ role: parent
697
+ `
698
+ );
699
+
700
+ writeYaml(
701
+ "roles/parent.yaml",
702
+ `
703
+ name: parent
704
+ capabilities:
705
+ - a
706
+ - b
707
+ - c
708
+ `
709
+ );
710
+
711
+ writeYaml(
712
+ "roles/child.yaml",
713
+ `
714
+ name: child
715
+ extends: parent
716
+ capabilities:
717
+ - x
718
+ - y
719
+ `
720
+ );
721
+
722
+ const template = TemplateLoader.load(tmpDir);
723
+ // Plain array = explicit override, not merged with parent
724
+ expect(template.roles.get("child")!.capabilities).toEqual(["x", "y"]);
725
+ });
726
+
727
+ it("detects circular inheritance", () => {
728
+ writeYaml(
729
+ "team.yaml",
730
+ `
731
+ name: cycle-test
732
+ version: 1
733
+ roles:
734
+ - alpha
735
+ - beta
736
+ topology:
737
+ root:
738
+ role: alpha
739
+ `
740
+ );
741
+
742
+ writeYaml(
743
+ "roles/alpha.yaml",
744
+ `
745
+ name: alpha
746
+ extends: beta
747
+ capabilities:
748
+ add:
749
+ - a
750
+ `
751
+ );
752
+
753
+ writeYaml(
754
+ "roles/beta.yaml",
755
+ `
756
+ name: beta
757
+ extends: alpha
758
+ capabilities:
759
+ add:
760
+ - b
761
+ `
762
+ );
763
+
764
+ expect(() => TemplateLoader.load(tmpDir)).toThrow(
765
+ "Circular role inheritance detected"
766
+ );
767
+ });
768
+
769
+ it("resolves flat capabilities_add/capabilities_remove syntax", () => {
770
+ writeYaml(
771
+ "team.yaml",
772
+ `
773
+ name: flat-syntax
774
+ version: 1
775
+ roles:
776
+ - base
777
+ - child
778
+ topology:
779
+ root:
780
+ role: base
781
+ `
782
+ );
783
+
784
+ writeYaml(
785
+ "roles/base.yaml",
786
+ `
787
+ name: base
788
+ capabilities:
789
+ - read
790
+ - write
791
+ - admin
792
+ `
793
+ );
794
+
795
+ writeYaml(
796
+ "roles/child.yaml",
797
+ `
798
+ name: child
799
+ extends: base
800
+ capabilities_add:
801
+ - debug
802
+ - test
803
+ capabilities_remove:
804
+ - admin
805
+ `
806
+ );
807
+
808
+ const template = TemplateLoader.load(tmpDir);
809
+ const child = template.roles.get("child")!;
810
+ expect(child.capabilities.sort()).toEqual(["debug", "read", "test", "write"]);
811
+ });
812
+
813
+ it("resolves flat capabilities_add only (no remove)", () => {
814
+ writeYaml(
815
+ "team.yaml",
816
+ `
817
+ name: add-only
818
+ version: 1
819
+ roles:
820
+ - parent
821
+ - child
822
+ topology:
823
+ root:
824
+ role: parent
825
+ `
826
+ );
827
+
828
+ writeYaml(
829
+ "roles/parent.yaml",
830
+ `
831
+ name: parent
832
+ capabilities:
833
+ - a
834
+ - b
835
+ `
836
+ );
837
+
838
+ writeYaml(
839
+ "roles/child.yaml",
840
+ `
841
+ name: child
842
+ extends: parent
843
+ capabilities_add:
844
+ - c
845
+ `
846
+ );
847
+
848
+ const template = TemplateLoader.load(tmpDir);
849
+ expect(template.roles.get("child")!.capabilities.sort()).toEqual(["a", "b", "c"]);
850
+ });
851
+
852
+ it("resolves flat capabilities_remove only (no add)", () => {
853
+ writeYaml(
854
+ "team.yaml",
855
+ `
856
+ name: remove-only
857
+ version: 1
858
+ roles:
859
+ - parent
860
+ - child
861
+ topology:
862
+ root:
863
+ role: parent
864
+ `
865
+ );
866
+
867
+ writeYaml(
868
+ "roles/parent.yaml",
869
+ `
870
+ name: parent
871
+ capabilities:
872
+ - a
873
+ - b
874
+ - c
875
+ `
876
+ );
877
+
878
+ writeYaml(
879
+ "roles/child.yaml",
880
+ `
881
+ name: child
882
+ extends: parent
883
+ capabilities_remove:
884
+ - c
885
+ `
886
+ );
887
+
888
+ const template = TemplateLoader.load(tmpDir);
889
+ expect(template.roles.get("child")!.capabilities.sort()).toEqual(["a", "b"]);
890
+ });
891
+
892
+ it("errors when both CapabilityComposition and flat fields are used", () => {
893
+ writeYaml(
894
+ "team.yaml",
895
+ `
896
+ name: conflict
897
+ version: 1
898
+ roles:
899
+ - bad
900
+ topology:
901
+ root:
902
+ role: bad
903
+ `
904
+ );
905
+
906
+ writeYaml(
907
+ "roles/bad.yaml",
908
+ `
909
+ name: bad
910
+ capabilities:
911
+ add:
912
+ - x
913
+ remove:
914
+ - y
915
+ capabilities_add:
916
+ - z
917
+ `
918
+ );
919
+
920
+ expect(() => TemplateLoader.load(tmpDir)).toThrow(
921
+ 'uses both CapabilityComposition'
922
+ );
923
+ });
924
+
925
+ it("ignores extends when parent is not in roles map", () => {
926
+ writeYaml(
927
+ "team.yaml",
928
+ `
929
+ name: external-parent
930
+ version: 1
931
+ roles:
932
+ - child
933
+ topology:
934
+ root:
935
+ role: child
936
+ `
937
+ );
938
+
939
+ writeYaml(
940
+ "roles/child.yaml",
941
+ `
942
+ name: child
943
+ extends: nonexistent-parent
944
+ capabilities:
945
+ add:
946
+ - foo
947
+ - bar
948
+ `
949
+ );
950
+
951
+ // Parent not in roles map → just use add list as-is
952
+ const template = TemplateLoader.load(tmpDir);
953
+ expect(template.roles.get("child")!.capabilities).toEqual(["foo", "bar"]);
954
+ expect(template.roles.get("child")!.extends).toBe("nonexistent-parent");
955
+ });
956
+ });
957
+
958
+ describe("load with options (hooks)", () => {
959
+ it("resolves external parent via resolveExternalRole hook", () => {
960
+ writeYaml(
961
+ "team.yaml",
962
+ `
963
+ name: hook-test
964
+ version: 1
965
+ roles:
966
+ - child
967
+ topology:
968
+ root:
969
+ role: child
970
+ `
971
+ );
972
+
973
+ writeYaml(
974
+ "roles/child.yaml",
975
+ `
976
+ name: child
977
+ extends: external-worker
978
+ capabilities:
979
+ add:
980
+ - debug
981
+ remove:
982
+ - git.push
983
+ `
984
+ );
985
+
986
+ const template = TemplateLoader.load(tmpDir, {
987
+ resolveExternalRole: (name) => {
988
+ if (name === "external-worker") {
989
+ return {
990
+ name: "external-worker",
991
+ displayName: "Worker",
992
+ description: "External worker role",
993
+ capabilities: ["file.read", "file.write", "git.push"],
994
+ raw: { name: "external-worker" },
995
+ };
996
+ }
997
+ return null;
998
+ },
999
+ });
1000
+
1001
+ const child = template.roles.get("child")!;
1002
+ // parent: [file.read, file.write, git.push] + debug - git.push
1003
+ expect(child.capabilities.sort()).toEqual(["debug", "file.read", "file.write"]);
1004
+ });
1005
+
1006
+ it("external resolver returning null falls back to add-only", () => {
1007
+ writeYaml(
1008
+ "team.yaml",
1009
+ `
1010
+ name: null-resolver
1011
+ version: 1
1012
+ roles:
1013
+ - orphan
1014
+ topology:
1015
+ root:
1016
+ role: orphan
1017
+ `
1018
+ );
1019
+
1020
+ writeYaml(
1021
+ "roles/orphan.yaml",
1022
+ `
1023
+ name: orphan
1024
+ extends: nonexistent
1025
+ capabilities:
1026
+ add:
1027
+ - foo
1028
+ `
1029
+ );
1030
+
1031
+ const template = TemplateLoader.load(tmpDir, {
1032
+ resolveExternalRole: () => null,
1033
+ });
1034
+
1035
+ // No parent found → just the add list
1036
+ expect(template.roles.get("orphan")!.capabilities).toEqual(["foo"]);
1037
+ });
1038
+
1039
+ it("postProcessRole transforms each role", () => {
1040
+ writeYaml(
1041
+ "team.yaml",
1042
+ `
1043
+ name: post-process
1044
+ version: 1
1045
+ roles:
1046
+ - worker
1047
+ topology:
1048
+ root:
1049
+ role: worker
1050
+ `
1051
+ );
1052
+
1053
+ writeYaml(
1054
+ "roles/worker.yaml",
1055
+ `
1056
+ name: worker
1057
+ capabilities:
1058
+ - build
1059
+ `
1060
+ );
1061
+
1062
+ const template = TemplateLoader.load(tmpDir, {
1063
+ postProcessRole: (role) => ({
1064
+ ...role,
1065
+ capabilities: [...role.capabilities, "injected.cap"],
1066
+ }),
1067
+ });
1068
+
1069
+ expect(template.roles.get("worker")!.capabilities).toEqual([
1070
+ "build",
1071
+ "injected.cap",
1072
+ ]);
1073
+ });
1074
+
1075
+ it("postProcess transforms the entire template", () => {
1076
+ writeYaml(
1077
+ "team.yaml",
1078
+ `
1079
+ name: full-post
1080
+ version: 1
1081
+ roles:
1082
+ - worker
1083
+ topology:
1084
+ root:
1085
+ role: worker
1086
+ `
1087
+ );
1088
+
1089
+ const template = TemplateLoader.load(tmpDir, {
1090
+ postProcess: (t) => ({
1091
+ ...t,
1092
+ sourcePath: "/overridden",
1093
+ }),
1094
+ });
1095
+
1096
+ expect(template.sourcePath).toBe("/overridden");
1097
+ });
1098
+ });
1099
+
1100
+ describe("loadAsync", () => {
1101
+ it("loads template with async external resolver", async () => {
1102
+ writeYaml(
1103
+ "team.yaml",
1104
+ `
1105
+ name: async-test
1106
+ version: 1
1107
+ roles:
1108
+ - child
1109
+ topology:
1110
+ root:
1111
+ role: child
1112
+ `
1113
+ );
1114
+
1115
+ writeYaml(
1116
+ "roles/child.yaml",
1117
+ `
1118
+ name: child
1119
+ extends: async-parent
1120
+ capabilities:
1121
+ add:
1122
+ - test
1123
+ `
1124
+ );
1125
+
1126
+ const template = await TemplateLoader.loadAsync(tmpDir, {
1127
+ resolveExternalRole: async (name) => {
1128
+ // Simulate async lookup
1129
+ await new Promise((r) => setTimeout(r, 1));
1130
+ if (name === "async-parent") {
1131
+ return {
1132
+ name: "async-parent",
1133
+ displayName: "Async Parent",
1134
+ description: "Resolved asynchronously",
1135
+ capabilities: ["read", "write"],
1136
+ raw: { name: "async-parent" },
1137
+ };
1138
+ }
1139
+ return null;
1140
+ },
1141
+ });
1142
+
1143
+ const child = template.roles.get("child")!;
1144
+ expect(child.capabilities.sort()).toEqual(["read", "test", "write"]);
1145
+ });
1146
+
1147
+ it("loads without options (same as sync)", async () => {
1148
+ writeYaml(
1149
+ "team.yaml",
1150
+ `
1151
+ name: async-basic
1152
+ version: 1
1153
+ roles:
1154
+ - worker
1155
+ topology:
1156
+ root:
1157
+ role: worker
1158
+ `
1159
+ );
1160
+
1161
+ const template = await TemplateLoader.loadAsync(tmpDir);
1162
+ expect(template.manifest.name).toBe("async-basic");
1163
+ expect(template.roles.get("worker")!.name).toBe("worker");
1164
+ });
1165
+
1166
+ it("supports async postProcessRole", async () => {
1167
+ writeYaml(
1168
+ "team.yaml",
1169
+ `
1170
+ name: async-post
1171
+ version: 1
1172
+ roles:
1173
+ - dev
1174
+ topology:
1175
+ root:
1176
+ role: dev
1177
+ `
1178
+ );
1179
+
1180
+ const template = await TemplateLoader.loadAsync(tmpDir, {
1181
+ postProcessRole: async (role) => {
1182
+ await new Promise((r) => setTimeout(r, 1));
1183
+ return { ...role, capabilities: [...role.capabilities, "async.cap"] };
1184
+ },
1185
+ });
1186
+
1187
+ expect(template.roles.get("dev")!.capabilities).toContain("async.cap");
1188
+ });
1189
+
1190
+ it("supports async postProcess", async () => {
1191
+ writeYaml(
1192
+ "team.yaml",
1193
+ `
1194
+ name: async-post-full
1195
+ version: 1
1196
+ roles:
1197
+ - dev
1198
+ topology:
1199
+ root:
1200
+ role: dev
1201
+ `
1202
+ );
1203
+
1204
+ const template = await TemplateLoader.loadAsync(tmpDir, {
1205
+ postProcess: async (t) => {
1206
+ await new Promise((r) => setTimeout(r, 1));
1207
+ return { ...t, sourcePath: "/async-overridden" };
1208
+ },
1209
+ });
1210
+
1211
+ expect(template.sourcePath).toBe("/async-overridden");
1212
+ });
1213
+ });
1214
+
1215
+ describe("MCP server loading", () => {
1216
+ it("loads tools/mcp-servers.json per role", () => {
1217
+ writeYaml(
1218
+ "team.yaml",
1219
+ `
1220
+ name: mcp-test
1221
+ version: 1
1222
+ roles:
1223
+ - planner
1224
+ - worker
1225
+ topology:
1226
+ root:
1227
+ role: planner
1228
+ `
1229
+ );
1230
+
1231
+ const mcpConfig = {
1232
+ planner: {
1233
+ servers: [
1234
+ { name: "sudocode", command: "npx", args: ["sudocode-mcp"] },
1235
+ ],
1236
+ },
1237
+ worker: {
1238
+ servers: [
1239
+ { name: "linter", command: "eslint-mcp", env: { CI: "true" } },
1240
+ { name: "tester", command: "vitest-mcp" },
1241
+ ],
1242
+ },
1243
+ };
1244
+ const toolsDir = path.join(tmpDir, "tools");
1245
+ fs.mkdirSync(toolsDir, { recursive: true });
1246
+ fs.writeFileSync(
1247
+ path.join(toolsDir, "mcp-servers.json"),
1248
+ JSON.stringify(mcpConfig),
1249
+ "utf-8"
1250
+ );
1251
+
1252
+ const template = TemplateLoader.load(tmpDir);
1253
+ expect(template.mcpServers.size).toBe(2);
1254
+ expect(template.mcpServers.get("planner")).toHaveLength(1);
1255
+ expect(template.mcpServers.get("planner")![0].name).toBe("sudocode");
1256
+ expect(template.mcpServers.get("worker")).toHaveLength(2);
1257
+ expect(template.mcpServers.get("worker")![0].env).toEqual({ CI: "true" });
1258
+ });
1259
+
1260
+ it("returns empty map when tools/ does not exist", () => {
1261
+ writeYaml(
1262
+ "team.yaml",
1263
+ `
1264
+ name: no-tools
1265
+ version: 1
1266
+ roles:
1267
+ - worker
1268
+ topology:
1269
+ root:
1270
+ role: worker
1271
+ `
1272
+ );
1273
+
1274
+ const template = TemplateLoader.load(tmpDir);
1275
+ expect(template.mcpServers.size).toBe(0);
1276
+ });
1277
+
1278
+ it("throws on malformed JSON", () => {
1279
+ writeYaml(
1280
+ "team.yaml",
1281
+ `
1282
+ name: bad-json
1283
+ version: 1
1284
+ roles:
1285
+ - worker
1286
+ topology:
1287
+ root:
1288
+ role: worker
1289
+ `
1290
+ );
1291
+
1292
+ const toolsDir = path.join(tmpDir, "tools");
1293
+ fs.mkdirSync(toolsDir, { recursive: true });
1294
+ fs.writeFileSync(
1295
+ path.join(toolsDir, "mcp-servers.json"),
1296
+ "{ invalid json",
1297
+ "utf-8"
1298
+ );
1299
+
1300
+ expect(() => TemplateLoader.load(tmpDir)).toThrow("Failed to parse");
1301
+ });
1302
+ });
1303
+
1304
+ describe("loadFromManifest", () => {
1305
+ it("loads from an inline manifest", () => {
1306
+ const template = TemplateLoader.loadFromManifest({
1307
+ name: "inline",
1308
+ version: 1,
1309
+ roles: ["worker"],
1310
+ topology: { root: { role: "worker" } },
1311
+ });
1312
+
1313
+ expect(template.manifest.name).toBe("inline");
1314
+ expect(template.roles.get("worker")!.name).toBe("worker");
1315
+ expect(template.mcpServers.size).toBe(0);
1316
+ expect(template.sourcePath).toBe("");
1317
+ });
1318
+ });
1319
+ });