@ag-eco/agentplate-cli 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (455) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +462 -0
  3. package/agents/ap-co-creation.md +90 -0
  4. package/agents/builder.md +144 -0
  5. package/agents/coordinator.md +377 -0
  6. package/agents/lead.md +435 -0
  7. package/agents/merger.md +164 -0
  8. package/agents/monitor.md +214 -0
  9. package/agents/orchestrator.md +239 -0
  10. package/agents/reviewer.md +140 -0
  11. package/agents/scout.md +125 -0
  12. package/agents/supervisor.md +427 -0
  13. package/package.json +66 -0
  14. package/src/agents/capabilities.test.ts +85 -0
  15. package/src/agents/capabilities.ts +125 -0
  16. package/src/agents/checkpoint.test.ts +88 -0
  17. package/src/agents/checkpoint.ts +101 -0
  18. package/src/agents/copilot-hooks-deployer.test.ts +162 -0
  19. package/src/agents/copilot-hooks-deployer.ts +93 -0
  20. package/src/agents/guard-rules.test.ts +372 -0
  21. package/src/agents/guard-rules.ts +97 -0
  22. package/src/agents/headless-mail-injector.test.ts +709 -0
  23. package/src/agents/headless-mail-injector.ts +377 -0
  24. package/src/agents/headless-prompt.test.ts +102 -0
  25. package/src/agents/headless-prompt.ts +68 -0
  26. package/src/agents/hooks-deployer.test.ts +3119 -0
  27. package/src/agents/hooks-deployer.ts +804 -0
  28. package/src/agents/identity.test.ts +604 -0
  29. package/src/agents/identity.ts +384 -0
  30. package/src/agents/lifecycle.test.ts +196 -0
  31. package/src/agents/lifecycle.ts +183 -0
  32. package/src/agents/mail-poll-detect.test.ts +153 -0
  33. package/src/agents/mail-poll-detect.ts +73 -0
  34. package/src/agents/manifest.test.ts +1026 -0
  35. package/src/agents/manifest.ts +376 -0
  36. package/src/agents/overlay.test.ts +1058 -0
  37. package/src/agents/overlay.ts +490 -0
  38. package/src/agents/scope-detect.test.ts +190 -0
  39. package/src/agents/scope-detect.ts +146 -0
  40. package/src/agents/turn-lock.test.ts +181 -0
  41. package/src/agents/turn-lock.ts +235 -0
  42. package/src/agents/turn-runner-dispatch.test.ts +182 -0
  43. package/src/agents/turn-runner-dispatch.ts +105 -0
  44. package/src/agents/turn-runner.test.ts +2312 -0
  45. package/src/agents/turn-runner.ts +1383 -0
  46. package/src/beads/client.test.ts +217 -0
  47. package/src/beads/client.ts +230 -0
  48. package/src/beads/molecules.test.ts +338 -0
  49. package/src/beads/molecules.ts +198 -0
  50. package/src/commands/agents.test.ts +328 -0
  51. package/src/commands/agents.ts +299 -0
  52. package/src/commands/clean.test.ts +797 -0
  53. package/src/commands/clean.ts +791 -0
  54. package/src/commands/completions.test.ts +348 -0
  55. package/src/commands/completions.ts +981 -0
  56. package/src/commands/coordinator.test.ts +2975 -0
  57. package/src/commands/coordinator.ts +1841 -0
  58. package/src/commands/costs.test.ts +1183 -0
  59. package/src/commands/costs.ts +599 -0
  60. package/src/commands/dashboard.test.ts +954 -0
  61. package/src/commands/dashboard.ts +1212 -0
  62. package/src/commands/discover.test.ts +288 -0
  63. package/src/commands/discover.ts +202 -0
  64. package/src/commands/doctor.test.ts +303 -0
  65. package/src/commands/doctor.ts +311 -0
  66. package/src/commands/ecosystem.test.ts +226 -0
  67. package/src/commands/ecosystem.ts +248 -0
  68. package/src/commands/errors.test.ts +654 -0
  69. package/src/commands/errors.ts +197 -0
  70. package/src/commands/feed.test.ts +709 -0
  71. package/src/commands/feed.ts +260 -0
  72. package/src/commands/group.test.ts +475 -0
  73. package/src/commands/group.ts +546 -0
  74. package/src/commands/hooks.test.ts +458 -0
  75. package/src/commands/hooks.ts +263 -0
  76. package/src/commands/init.test.ts +1011 -0
  77. package/src/commands/init.ts +967 -0
  78. package/src/commands/inspect.test.ts +1239 -0
  79. package/src/commands/inspect.ts +648 -0
  80. package/src/commands/log.test.ts +1913 -0
  81. package/src/commands/log.ts +958 -0
  82. package/src/commands/logs.test.ts +801 -0
  83. package/src/commands/logs.ts +483 -0
  84. package/src/commands/mail.test.ts +1501 -0
  85. package/src/commands/mail.ts +848 -0
  86. package/src/commands/merge.test.ts +864 -0
  87. package/src/commands/merge.ts +381 -0
  88. package/src/commands/metrics.test.ts +458 -0
  89. package/src/commands/metrics.ts +129 -0
  90. package/src/commands/monitor.test.ts +191 -0
  91. package/src/commands/monitor.ts +409 -0
  92. package/src/commands/nudge.test.ts +579 -0
  93. package/src/commands/nudge.ts +646 -0
  94. package/src/commands/orchestrator.ts +42 -0
  95. package/src/commands/prime.test.ts +612 -0
  96. package/src/commands/prime.ts +359 -0
  97. package/src/commands/replay.test.ts +757 -0
  98. package/src/commands/replay.ts +231 -0
  99. package/src/commands/run.test.ts +469 -0
  100. package/src/commands/run.ts +353 -0
  101. package/src/commands/serve/agent-actions.test.ts +210 -0
  102. package/src/commands/serve/agent-actions.ts +192 -0
  103. package/src/commands/serve/build.test.ts +202 -0
  104. package/src/commands/serve/build.ts +206 -0
  105. package/src/commands/serve/coordinator-actions.test.ts +339 -0
  106. package/src/commands/serve/coordinator-actions.ts +410 -0
  107. package/src/commands/serve/dev.test.ts +168 -0
  108. package/src/commands/serve/dev.ts +117 -0
  109. package/src/commands/serve/mail-actions.test.ts +312 -0
  110. package/src/commands/serve/mail-actions.ts +167 -0
  111. package/src/commands/serve/rest.test.ts +1680 -0
  112. package/src/commands/serve/rest.ts +1130 -0
  113. package/src/commands/serve/static.ts +51 -0
  114. package/src/commands/serve/ws.test.ts +361 -0
  115. package/src/commands/serve/ws.ts +332 -0
  116. package/src/commands/serve.test.ts +459 -0
  117. package/src/commands/serve.ts +654 -0
  118. package/src/commands/sling.test.ts +1583 -0
  119. package/src/commands/sling.ts +1351 -0
  120. package/src/commands/spec.test.ts +179 -0
  121. package/src/commands/spec.ts +105 -0
  122. package/src/commands/status.test.ts +614 -0
  123. package/src/commands/status.ts +403 -0
  124. package/src/commands/stop.test.ts +964 -0
  125. package/src/commands/stop.ts +319 -0
  126. package/src/commands/supervisor.test.ts +185 -0
  127. package/src/commands/supervisor.ts +537 -0
  128. package/src/commands/trace.test.ts +762 -0
  129. package/src/commands/trace.ts +205 -0
  130. package/src/commands/update.test.ts +466 -0
  131. package/src/commands/update.ts +263 -0
  132. package/src/commands/upgrade.test.ts +48 -0
  133. package/src/commands/upgrade.ts +240 -0
  134. package/src/commands/watch.test.ts +257 -0
  135. package/src/commands/watch.ts +308 -0
  136. package/src/commands/worktree.test.ts +1297 -0
  137. package/src/commands/worktree.ts +451 -0
  138. package/src/config.test.ts +1535 -0
  139. package/src/config.ts +1064 -0
  140. package/src/doctor/agents.test.ts +523 -0
  141. package/src/doctor/agents.ts +399 -0
  142. package/src/doctor/config-check.test.ts +191 -0
  143. package/src/doctor/config-check.ts +183 -0
  144. package/src/doctor/consistency.test.ts +807 -0
  145. package/src/doctor/consistency.ts +347 -0
  146. package/src/doctor/databases.test.ts +350 -0
  147. package/src/doctor/databases.ts +243 -0
  148. package/src/doctor/dependencies.test.ts +296 -0
  149. package/src/doctor/dependencies.ts +272 -0
  150. package/src/doctor/ecosystem.test.ts +308 -0
  151. package/src/doctor/ecosystem.ts +156 -0
  152. package/src/doctor/logs.test.ts +253 -0
  153. package/src/doctor/logs.ts +295 -0
  154. package/src/doctor/merge-queue.test.ts +315 -0
  155. package/src/doctor/merge-queue.ts +167 -0
  156. package/src/doctor/providers.test.ts +409 -0
  157. package/src/doctor/providers.ts +250 -0
  158. package/src/doctor/serve.test.ts +95 -0
  159. package/src/doctor/serve.ts +86 -0
  160. package/src/doctor/structure.test.ts +423 -0
  161. package/src/doctor/structure.ts +285 -0
  162. package/src/doctor/types.ts +43 -0
  163. package/src/doctor/version.test.ts +241 -0
  164. package/src/doctor/version.ts +132 -0
  165. package/src/doctor/watchdog.test.ts +167 -0
  166. package/src/doctor/watchdog.ts +214 -0
  167. package/src/e2e/init-sling-lifecycle.test.ts +283 -0
  168. package/src/errors.test.ts +350 -0
  169. package/src/errors.ts +217 -0
  170. package/src/events/store.test.ts +660 -0
  171. package/src/events/store.ts +369 -0
  172. package/src/events/tailer.test.ts +719 -0
  173. package/src/events/tailer.ts +332 -0
  174. package/src/events/tool-filter.test.ts +330 -0
  175. package/src/events/tool-filter.ts +126 -0
  176. package/src/index.ts +533 -0
  177. package/src/insights/analyzer.test.ts +466 -0
  178. package/src/insights/analyzer.ts +203 -0
  179. package/src/insights/quality-gates.test.ts +141 -0
  180. package/src/insights/quality-gates.ts +156 -0
  181. package/src/json.test.ts +72 -0
  182. package/src/json.ts +53 -0
  183. package/src/loam/client.test.ts +752 -0
  184. package/src/loam/client.ts +664 -0
  185. package/src/logging/color.test.ts +252 -0
  186. package/src/logging/color.ts +105 -0
  187. package/src/logging/format.test.ts +110 -0
  188. package/src/logging/format.ts +255 -0
  189. package/src/logging/logger.test.ts +814 -0
  190. package/src/logging/logger.ts +266 -0
  191. package/src/logging/reporter.test.ts +259 -0
  192. package/src/logging/reporter.ts +110 -0
  193. package/src/logging/sanitizer.test.ts +190 -0
  194. package/src/logging/sanitizer.ts +57 -0
  195. package/src/logging/theme.ts +140 -0
  196. package/src/mail/broadcast.test.ts +204 -0
  197. package/src/mail/broadcast.ts +92 -0
  198. package/src/mail/client.test.ts +774 -0
  199. package/src/mail/client.ts +236 -0
  200. package/src/mail/store.test.ts +898 -0
  201. package/src/mail/store.ts +425 -0
  202. package/src/merge/lock.test.ts +149 -0
  203. package/src/merge/lock.ts +140 -0
  204. package/src/merge/predict.test.ts +387 -0
  205. package/src/merge/predict.ts +249 -0
  206. package/src/merge/queue.test.ts +426 -0
  207. package/src/merge/queue.ts +246 -0
  208. package/src/merge/resolver.test.ts +1993 -0
  209. package/src/merge/resolver.ts +926 -0
  210. package/src/metrics/pricing.test.ts +258 -0
  211. package/src/metrics/pricing.ts +135 -0
  212. package/src/metrics/store.test.ts +978 -0
  213. package/src/metrics/store.ts +501 -0
  214. package/src/metrics/summary.test.ts +398 -0
  215. package/src/metrics/summary.ts +178 -0
  216. package/src/metrics/transcript.test.ts +483 -0
  217. package/src/metrics/transcript.ts +114 -0
  218. package/src/runtimes/__fixtures__/claude-stream-fixture.ts +22 -0
  219. package/src/runtimes/aider.test.ts +124 -0
  220. package/src/runtimes/aider.ts +147 -0
  221. package/src/runtimes/amp.test.ts +164 -0
  222. package/src/runtimes/amp.ts +154 -0
  223. package/src/runtimes/claude.test.ts +1474 -0
  224. package/src/runtimes/claude.ts +579 -0
  225. package/src/runtimes/codex.test.ts +805 -0
  226. package/src/runtimes/codex.ts +273 -0
  227. package/src/runtimes/connections.test.ts +214 -0
  228. package/src/runtimes/connections.ts +103 -0
  229. package/src/runtimes/copilot.test.ts +707 -0
  230. package/src/runtimes/copilot.ts +316 -0
  231. package/src/runtimes/cursor.test.ts +497 -0
  232. package/src/runtimes/cursor.ts +205 -0
  233. package/src/runtimes/gemini.test.ts +537 -0
  234. package/src/runtimes/gemini.ts +243 -0
  235. package/src/runtimes/goose.test.ts +133 -0
  236. package/src/runtimes/goose.ts +157 -0
  237. package/src/runtimes/headless-connection.test.ts +264 -0
  238. package/src/runtimes/headless-connection.ts +158 -0
  239. package/src/runtimes/opencode.test.ts +325 -0
  240. package/src/runtimes/opencode.ts +188 -0
  241. package/src/runtimes/pi-guards.test.ts +486 -0
  242. package/src/runtimes/pi-guards.ts +367 -0
  243. package/src/runtimes/pi.test.ts +789 -0
  244. package/src/runtimes/pi.ts +305 -0
  245. package/src/runtimes/registry.test.ts +196 -0
  246. package/src/runtimes/registry.ts +99 -0
  247. package/src/runtimes/sapling.test.ts +1267 -0
  248. package/src/runtimes/sapling.ts +710 -0
  249. package/src/runtimes/types.ts +266 -0
  250. package/src/schema-consistency.test.ts +246 -0
  251. package/src/sessions/compat.test.ts +281 -0
  252. package/src/sessions/compat.ts +105 -0
  253. package/src/sessions/store.test.ts +1748 -0
  254. package/src/sessions/store.ts +858 -0
  255. package/src/test-helpers.test.ts +124 -0
  256. package/src/test-helpers.ts +145 -0
  257. package/src/test-setup.test.ts +31 -0
  258. package/src/test-setup.ts +28 -0
  259. package/src/tools/loam/api.ts +368 -0
  260. package/src/tools/loam/cli.ts +278 -0
  261. package/src/tools/loam/commands/add.ts +52 -0
  262. package/src/tools/loam/commands/archive.ts +214 -0
  263. package/src/tools/loam/commands/audit.ts +276 -0
  264. package/src/tools/loam/commands/compact.ts +1062 -0
  265. package/src/tools/loam/commands/completions.ts +79 -0
  266. package/src/tools/loam/commands/config.ts +381 -0
  267. package/src/tools/loam/commands/delete-domain.ts +121 -0
  268. package/src/tools/loam/commands/delete.ts +316 -0
  269. package/src/tools/loam/commands/diff.ts +200 -0
  270. package/src/tools/loam/commands/doctor.ts +1113 -0
  271. package/src/tools/loam/commands/edit.ts +226 -0
  272. package/src/tools/loam/commands/init.ts +31 -0
  273. package/src/tools/loam/commands/learn.ts +179 -0
  274. package/src/tools/loam/commands/move.ts +323 -0
  275. package/src/tools/loam/commands/onboard.ts +374 -0
  276. package/src/tools/loam/commands/outcome.ts +185 -0
  277. package/src/tools/loam/commands/prime.ts +688 -0
  278. package/src/tools/loam/commands/prune.ts +614 -0
  279. package/src/tools/loam/commands/query.ts +218 -0
  280. package/src/tools/loam/commands/rank.ts +180 -0
  281. package/src/tools/loam/commands/ready.ts +189 -0
  282. package/src/tools/loam/commands/record.ts +1210 -0
  283. package/src/tools/loam/commands/restore.ts +166 -0
  284. package/src/tools/loam/commands/search.ts +327 -0
  285. package/src/tools/loam/commands/setup.ts +887 -0
  286. package/src/tools/loam/commands/status.ts +103 -0
  287. package/src/tools/loam/commands/sync.ts +298 -0
  288. package/src/tools/loam/commands/update.ts +19 -0
  289. package/src/tools/loam/commands/upgrade.ts +93 -0
  290. package/src/tools/loam/commands/validate.ts +190 -0
  291. package/src/tools/loam/index.ts +62 -0
  292. package/src/tools/loam/log.ts +127 -0
  293. package/src/tools/loam/registry/builtins.ts +409 -0
  294. package/src/tools/loam/registry/custom.ts +431 -0
  295. package/src/tools/loam/registry/init.ts +55 -0
  296. package/src/tools/loam/registry/template.ts +40 -0
  297. package/src/tools/loam/registry/type-registry.ts +113 -0
  298. package/src/tools/loam/schemas/config-schema.ts +489 -0
  299. package/src/tools/loam/schemas/config.ts +245 -0
  300. package/src/tools/loam/schemas/index.ts +18 -0
  301. package/src/tools/loam/schemas/record-schema.ts +191 -0
  302. package/src/tools/loam/schemas/record.ts +115 -0
  303. package/src/tools/loam/utils/active-work.ts +205 -0
  304. package/src/tools/loam/utils/anchor-validity.ts +80 -0
  305. package/src/tools/loam/utils/archive.ts +146 -0
  306. package/src/tools/loam/utils/audit.ts +667 -0
  307. package/src/tools/loam/utils/bm25.ts +238 -0
  308. package/src/tools/loam/utils/budget.ts +142 -0
  309. package/src/tools/loam/utils/config.ts +344 -0
  310. package/src/tools/loam/utils/dir-anchors.ts +62 -0
  311. package/src/tools/loam/utils/domain-rules.ts +114 -0
  312. package/src/tools/loam/utils/expertise.ts +393 -0
  313. package/src/tools/loam/utils/format-helpers.ts +96 -0
  314. package/src/tools/loam/utils/format.ts +1234 -0
  315. package/src/tools/loam/utils/git-context.ts +50 -0
  316. package/src/tools/loam/utils/git.ts +183 -0
  317. package/src/tools/loam/utils/hooks.ts +299 -0
  318. package/src/tools/loam/utils/index.ts +52 -0
  319. package/src/tools/loam/utils/json-output.ts +13 -0
  320. package/src/tools/loam/utils/lock.ts +76 -0
  321. package/src/tools/loam/utils/markers.ts +48 -0
  322. package/src/tools/loam/utils/numeric-flags.ts +20 -0
  323. package/src/tools/loam/utils/palette.ts +44 -0
  324. package/src/tools/loam/utils/prime-ranking.ts +135 -0
  325. package/src/tools/loam/utils/recipe-discovery.ts +195 -0
  326. package/src/tools/loam/utils/runtime-flags.ts +28 -0
  327. package/src/tools/loam/utils/scoring.ts +94 -0
  328. package/src/tools/loam/utils/version.ts +116 -0
  329. package/src/tools/sprout/commands/block.ts +64 -0
  330. package/src/tools/sprout/commands/blocked.ts +86 -0
  331. package/src/tools/sprout/commands/close.ts +129 -0
  332. package/src/tools/sprout/commands/completions.ts +198 -0
  333. package/src/tools/sprout/commands/config.ts +238 -0
  334. package/src/tools/sprout/commands/create.ts +164 -0
  335. package/src/tools/sprout/commands/dep.ts +148 -0
  336. package/src/tools/sprout/commands/doctor.ts +979 -0
  337. package/src/tools/sprout/commands/init.ts +83 -0
  338. package/src/tools/sprout/commands/label.ts +178 -0
  339. package/src/tools/sprout/commands/list.ts +210 -0
  340. package/src/tools/sprout/commands/migrate.ts +133 -0
  341. package/src/tools/sprout/commands/onboard.ts +207 -0
  342. package/src/tools/sprout/commands/plan-show.ts +278 -0
  343. package/src/tools/sprout/commands/plan.ts +2526 -0
  344. package/src/tools/sprout/commands/prime.ts +399 -0
  345. package/src/tools/sprout/commands/ready.ts +245 -0
  346. package/src/tools/sprout/commands/search.ts +221 -0
  347. package/src/tools/sprout/commands/show.ts +277 -0
  348. package/src/tools/sprout/commands/stats.ts +146 -0
  349. package/src/tools/sprout/commands/sync.ts +134 -0
  350. package/src/tools/sprout/commands/tpl.ts +364 -0
  351. package/src/tools/sprout/commands/unblock.ts +115 -0
  352. package/src/tools/sprout/commands/update.ts +257 -0
  353. package/src/tools/sprout/commands/upgrade.ts +91 -0
  354. package/src/tools/sprout/config-schema.ts +152 -0
  355. package/src/tools/sprout/config.ts +355 -0
  356. package/src/tools/sprout/filter.ts +107 -0
  357. package/src/tools/sprout/format.ts +43 -0
  358. package/src/tools/sprout/id.ts +22 -0
  359. package/src/tools/sprout/index.ts +204 -0
  360. package/src/tools/sprout/log.ts +76 -0
  361. package/src/tools/sprout/markers.ts +22 -0
  362. package/src/tools/sprout/output.ts +121 -0
  363. package/src/tools/sprout/plan-backref.ts +93 -0
  364. package/src/tools/sprout/plan-context.ts +81 -0
  365. package/src/tools/sprout/plan-domain.ts +139 -0
  366. package/src/tools/sprout/plan-lifecycle.ts +65 -0
  367. package/src/tools/sprout/plan-loam.ts +207 -0
  368. package/src/tools/sprout/plan-schema.ts +209 -0
  369. package/src/tools/sprout/sort.ts +31 -0
  370. package/src/tools/sprout/store.ts +172 -0
  371. package/src/tools/sprout/types.ts +118 -0
  372. package/src/tools/sprout/validation.ts +119 -0
  373. package/src/tools/sprout/version.ts +1 -0
  374. package/src/tools/sprout/yaml.ts +387 -0
  375. package/src/tools/trellis/commands/archive.ts +87 -0
  376. package/src/tools/trellis/commands/completions.ts +610 -0
  377. package/src/tools/trellis/commands/config.ts +382 -0
  378. package/src/tools/trellis/commands/create.ts +252 -0
  379. package/src/tools/trellis/commands/diff.ts +150 -0
  380. package/src/tools/trellis/commands/doctor.ts +771 -0
  381. package/src/tools/trellis/commands/emit.ts +365 -0
  382. package/src/tools/trellis/commands/history.ts +83 -0
  383. package/src/tools/trellis/commands/import.ts +198 -0
  384. package/src/tools/trellis/commands/init.ts +81 -0
  385. package/src/tools/trellis/commands/list.ts +103 -0
  386. package/src/tools/trellis/commands/onboard.ts +156 -0
  387. package/src/tools/trellis/commands/pin.ts +172 -0
  388. package/src/tools/trellis/commands/prime.ts +193 -0
  389. package/src/tools/trellis/commands/render.ts +122 -0
  390. package/src/tools/trellis/commands/schema.ts +353 -0
  391. package/src/tools/trellis/commands/show.ts +115 -0
  392. package/src/tools/trellis/commands/stats.ts +65 -0
  393. package/src/tools/trellis/commands/sync.ts +112 -0
  394. package/src/tools/trellis/commands/tree.ts +123 -0
  395. package/src/tools/trellis/commands/update.ts +330 -0
  396. package/src/tools/trellis/commands/upgrade.ts +95 -0
  397. package/src/tools/trellis/commands/validate.ts +166 -0
  398. package/src/tools/trellis/config-schema.ts +81 -0
  399. package/src/tools/trellis/config.ts +108 -0
  400. package/src/tools/trellis/frontmatter.ts +348 -0
  401. package/src/tools/trellis/id.ts +24 -0
  402. package/src/tools/trellis/index.ts +209 -0
  403. package/src/tools/trellis/markers.ts +28 -0
  404. package/src/tools/trellis/output.ts +84 -0
  405. package/src/tools/trellis/render.ts +212 -0
  406. package/src/tools/trellis/store.ts +144 -0
  407. package/src/tools/trellis/types.ts +82 -0
  408. package/src/tools/trellis/validate.ts +199 -0
  409. package/src/tools/trellis/yaml.ts +309 -0
  410. package/src/tracker/beads.test.ts +454 -0
  411. package/src/tracker/beads.ts +56 -0
  412. package/src/tracker/factory.test.ts +90 -0
  413. package/src/tracker/factory.ts +65 -0
  414. package/src/tracker/sprout.test.ts +461 -0
  415. package/src/tracker/sprout.ts +182 -0
  416. package/src/tracker/types.ts +52 -0
  417. package/src/trellis/client.test.ts +107 -0
  418. package/src/trellis/client.ts +179 -0
  419. package/src/types.ts +970 -0
  420. package/src/utils/bin.test.ts +10 -0
  421. package/src/utils/bin.ts +37 -0
  422. package/src/utils/browser.test.ts +49 -0
  423. package/src/utils/browser.ts +48 -0
  424. package/src/utils/fs.test.ts +119 -0
  425. package/src/utils/fs.ts +62 -0
  426. package/src/utils/pid.test.ts +152 -0
  427. package/src/utils/pid.ts +130 -0
  428. package/src/utils/process-scan.test.ts +53 -0
  429. package/src/utils/process-scan.ts +76 -0
  430. package/src/utils/time.test.ts +43 -0
  431. package/src/utils/time.ts +37 -0
  432. package/src/utils/version.test.ts +33 -0
  433. package/src/utils/version.ts +70 -0
  434. package/src/version.ts +5 -0
  435. package/src/watchdog/daemon.test.ts +3721 -0
  436. package/src/watchdog/daemon.ts +1257 -0
  437. package/src/watchdog/health.test.ts +830 -0
  438. package/src/watchdog/health.ts +434 -0
  439. package/src/watchdog/triage.test.ts +205 -0
  440. package/src/watchdog/triage.ts +205 -0
  441. package/src/worktree/manager.test.ts +720 -0
  442. package/src/worktree/manager.ts +405 -0
  443. package/src/worktree/process.test.ts +172 -0
  444. package/src/worktree/process.ts +131 -0
  445. package/src/worktree/tmux.test.ts +1616 -0
  446. package/src/worktree/tmux.ts +721 -0
  447. package/templates/CLAUDE.md.tmpl +100 -0
  448. package/templates/copilot-hooks.json.tmpl +13 -0
  449. package/templates/hooks.json.tmpl +109 -0
  450. package/templates/overlay.md.tmpl +88 -0
  451. package/ui/dist/apple-touch-icon-bdy6teep.png +0 -0
  452. package/ui/dist/chunk-8s31f05k.css +1 -0
  453. package/ui/dist/chunk-vm5rz679.js +300 -0
  454. package/ui/dist/favicon-nzb39vza.svg +4 -0
  455. package/ui/dist/index.html +17 -0
@@ -0,0 +1,612 @@
1
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2
+ import { mkdir, mkdtemp } from "node:fs/promises";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { cleanupTempDir, createTempGitRepo } from "../test-helpers.ts";
6
+ import type { AgentManifest, AgentSession, SessionMetrics } from "../types.ts";
7
+ import { formatManifest, formatMetrics, primeCommand } from "./prime.ts";
8
+
9
+ /**
10
+ * Tests for `agentplate prime` command.
11
+ *
12
+ * Uses real filesystem (temp directories) and process.stdout spy to test
13
+ * the prime command end-to-end.
14
+ */
15
+
16
+ describe("primeCommand", () => {
17
+ let chunks: string[];
18
+ let originalWrite: typeof process.stdout.write;
19
+ let originalStderrWrite: typeof process.stderr.write;
20
+ let stderrChunks: string[];
21
+ let tempDir: string;
22
+ let originalCwd: string;
23
+
24
+ beforeEach(async () => {
25
+ // Spy on stdout
26
+ chunks = [];
27
+ originalWrite = process.stdout.write;
28
+ process.stdout.write = ((chunk: string) => {
29
+ chunks.push(chunk);
30
+ return true;
31
+ }) as typeof process.stdout.write;
32
+
33
+ // Spy on stderr
34
+ stderrChunks = [];
35
+ originalStderrWrite = process.stderr.write;
36
+ process.stderr.write = ((chunk: string) => {
37
+ stderrChunks.push(chunk);
38
+ return true;
39
+ }) as typeof process.stderr.write;
40
+
41
+ // Create temp dir with .agentplate/config.yaml structure
42
+ tempDir = await mkdtemp(join(tmpdir(), "prime-test-"));
43
+ const agentplateDir = join(tempDir, ".agentplate");
44
+ await Bun.write(
45
+ join(agentplateDir, "config.yaml"),
46
+ `project:\n name: test-project\n root: ${tempDir}\n canonicalBranch: main\nloam:\n enabled: false\n`,
47
+ );
48
+
49
+ // Change to temp dir so loadConfig() works
50
+ originalCwd = process.cwd();
51
+ process.chdir(tempDir);
52
+ });
53
+
54
+ afterEach(async () => {
55
+ process.stdout.write = originalWrite;
56
+ process.stderr.write = originalStderrWrite;
57
+ process.chdir(originalCwd);
58
+ await cleanupTempDir(tempDir);
59
+ });
60
+
61
+ function output(): string {
62
+ return chunks.join("");
63
+ }
64
+
65
+ describe("Orchestrator priming (no --agent flag)", () => {
66
+ test("default prime outputs project context", async () => {
67
+ await primeCommand({});
68
+ const out = output();
69
+
70
+ expect(out).toContain("# Agentplate Context");
71
+ expect(out).toContain("## Project: test-project");
72
+ expect(out).toContain("Canonical branch: main");
73
+ expect(out).toContain("Max concurrent agents:");
74
+ expect(out).toContain("Max depth:");
75
+ });
76
+
77
+ test("includes agent manifest section", async () => {
78
+ await primeCommand({});
79
+ const out = output();
80
+
81
+ expect(out).toContain("## Agent Manifest");
82
+ // Without manifest file, should show fallback message
83
+ expect(out).toContain("No agent manifest found.");
84
+ });
85
+
86
+ test("without metrics.db shows no recent sessions message", async () => {
87
+ await primeCommand({});
88
+ const out = output();
89
+
90
+ expect(out).toContain("## Recent Activity");
91
+ expect(out).toContain("No recent sessions.");
92
+ });
93
+
94
+ test("--compact skips Recent Activity and Expertise sections", async () => {
95
+ await primeCommand({ compact: true });
96
+ const out = output();
97
+
98
+ // Should still have project basics
99
+ expect(out).toContain("# Agentplate Context");
100
+ expect(out).toContain("## Project: test-project");
101
+
102
+ // Should NOT have these sections
103
+ expect(out).not.toContain("## Recent Activity");
104
+ expect(out).not.toContain("## Expertise");
105
+ });
106
+ });
107
+
108
+ describe("Agent priming (--agent <name>)", () => {
109
+ test("unknown agent outputs basic context and warns", async () => {
110
+ await primeCommand({ agent: "unknown-agent" });
111
+ const out = output();
112
+
113
+ expect(out).toContain("# Agent Context: unknown-agent");
114
+ expect(out).toContain("## Identity");
115
+ expect(out).toContain("New agent - no prior sessions");
116
+ expect(out).toContain('agent "unknown-agent" not found');
117
+ });
118
+
119
+ test("agent with identity.yaml shows identity details", async () => {
120
+ // Write identity.yaml
121
+ const agentDir = join(tempDir, ".agentplate", "agents", "my-builder");
122
+ await Bun.write(
123
+ join(agentDir, "identity.yaml"),
124
+ `name: my-builder
125
+ capability: builder
126
+ created: "2026-01-01T00:00:00Z"
127
+ sessionsCompleted: 3
128
+ expertiseDomains:
129
+ - typescript
130
+ - testing
131
+ recentTasks:
132
+ - taskId: task-001
133
+ summary: "Implemented feature X"
134
+ completedAt: "2026-01-10T12:00:00Z"
135
+ `,
136
+ );
137
+
138
+ await primeCommand({ agent: "my-builder" });
139
+ const out = output();
140
+
141
+ expect(out).toContain("# Agent Context: my-builder");
142
+ expect(out).toContain("Name: my-builder");
143
+ expect(out).toContain("Capability: builder");
144
+ expect(out).toContain("Sessions completed: 3");
145
+ expect(out).toContain("Expertise: typescript, testing");
146
+ expect(out).toContain("Recent tasks:");
147
+ expect(out).toContain("task-001: Implemented feature X");
148
+ });
149
+
150
+ test("agent with active session shows Activation section", async () => {
151
+ // Write sessions.json with active session
152
+ const sessions: AgentSession[] = [
153
+ {
154
+ id: "session-001",
155
+ agentName: "active-builder",
156
+ capability: "builder",
157
+ worktreePath: join(tempDir, ".agentplate", "worktrees", "active-builder"),
158
+ branchName: "agentplate/active-builder/task-001",
159
+ taskId: "task-001",
160
+ tmuxSession: "agentplate-active-builder",
161
+ state: "working",
162
+ pid: 12345,
163
+ parentAgent: null,
164
+ depth: 0,
165
+ runId: null,
166
+ startedAt: new Date().toISOString(),
167
+ lastActivity: new Date().toISOString(),
168
+ escalationLevel: 0,
169
+ stalledSince: null,
170
+ transcriptPath: null,
171
+ },
172
+ ];
173
+
174
+ await Bun.write(
175
+ join(tempDir, ".agentplate", "sessions.json"),
176
+ `${JSON.stringify(sessions, null, 2)}\n`,
177
+ );
178
+
179
+ await primeCommand({ agent: "active-builder" });
180
+ const out = output();
181
+
182
+ expect(out).toContain("# Agent Context: active-builder");
183
+ expect(out).toContain("## Activation");
184
+ expect(out).toContain("You have a bound task: **task-001**");
185
+ expect(out).toContain("begin working immediately");
186
+ });
187
+
188
+ test("agent with completed session does NOT show Activation", async () => {
189
+ // Write sessions.json with completed session
190
+ const sessions: AgentSession[] = [
191
+ {
192
+ id: "session-002",
193
+ agentName: "completed-builder",
194
+ capability: "builder",
195
+ worktreePath: join(tempDir, ".agentplate", "worktrees", "completed-builder"),
196
+ branchName: "agentplate/completed-builder/task-002",
197
+ taskId: "task-002",
198
+ tmuxSession: "agentplate-completed-builder",
199
+ state: "completed",
200
+ pid: null,
201
+ parentAgent: null,
202
+ depth: 0,
203
+ runId: null,
204
+ startedAt: new Date(Date.now() - 3600000).toISOString(),
205
+ lastActivity: new Date().toISOString(),
206
+ escalationLevel: 0,
207
+ stalledSince: null,
208
+ transcriptPath: null,
209
+ },
210
+ ];
211
+
212
+ await Bun.write(
213
+ join(tempDir, ".agentplate", "sessions.json"),
214
+ `${JSON.stringify(sessions, null, 2)}\n`,
215
+ );
216
+
217
+ await primeCommand({ agent: "completed-builder" });
218
+ const out = output();
219
+
220
+ expect(out).toContain("# Agent Context: completed-builder");
221
+ expect(out).not.toContain("## Activation");
222
+ expect(out).not.toContain("bound task");
223
+ });
224
+
225
+ test("--compact with checkpoint.json shows Session Recovery", async () => {
226
+ // Write checkpoint.json
227
+ const agentDir = join(tempDir, ".agentplate", "agents", "recovery-agent");
228
+ await Bun.write(
229
+ join(agentDir, "checkpoint.json"),
230
+ `${JSON.stringify(
231
+ {
232
+ agentName: "recovery-agent",
233
+ taskId: "task-003",
234
+ sessionId: "session-003",
235
+ timestamp: new Date().toISOString(),
236
+ progressSummary: "Implemented initial tests for prime command",
237
+ filesModified: ["src/commands/prime.test.ts"],
238
+ currentBranch: "agentplate/recovery-agent/task-003",
239
+ pendingWork: "Add tests for edge cases",
240
+ loamDomains: ["typescript", "testing"],
241
+ },
242
+ null,
243
+ 2,
244
+ )}\n`,
245
+ );
246
+
247
+ // Also need identity to avoid warning
248
+ await Bun.write(
249
+ join(agentDir, "identity.yaml"),
250
+ `name: recovery-agent
251
+ capability: builder
252
+ created: "2026-01-01T00:00:00Z"
253
+ sessionsCompleted: 0
254
+ expertiseDomains: []
255
+ recentTasks: []
256
+ `,
257
+ );
258
+
259
+ await primeCommand({ agent: "recovery-agent", compact: true });
260
+ const out = output();
261
+
262
+ expect(out).toContain("# Agent Context: recovery-agent");
263
+ expect(out).toContain("## Session Recovery");
264
+ expect(out).toContain("Progress so far:** Implemented initial tests for prime command");
265
+ expect(out).toContain("Files modified:** src/commands/prime.test.ts");
266
+ expect(out).toContain("Pending work:** Add tests for edge cases");
267
+ expect(out).toContain("Branch:** agentplate/recovery-agent/task-003");
268
+ });
269
+
270
+ test("--compact skips Expertise section", async () => {
271
+ // Write identity with expertise
272
+ const agentDir = join(tempDir, ".agentplate", "agents", "compact-agent");
273
+ await Bun.write(
274
+ join(agentDir, "identity.yaml"),
275
+ `name: compact-agent
276
+ capability: builder
277
+ created: "2026-01-01T00:00:00Z"
278
+ sessionsCompleted: 1
279
+ expertiseDomains:
280
+ - typescript
281
+ recentTasks: []
282
+ `,
283
+ );
284
+
285
+ await primeCommand({ agent: "compact-agent", compact: true });
286
+ const out = output();
287
+
288
+ expect(out).toContain("# Agent Context: compact-agent");
289
+ expect(out).not.toContain("## Expertise");
290
+ });
291
+ });
292
+
293
+ describe("Session branch capture", () => {
294
+ test("orchestrator prime writes session-branch.txt with current git branch", async () => {
295
+ // Need a real git repo for branch detection
296
+ const gitRepoDir = await createTempGitRepo();
297
+ try {
298
+ const agentplateDir = join(gitRepoDir, ".agentplate");
299
+ await mkdir(agentplateDir, { recursive: true });
300
+ await Bun.write(
301
+ join(agentplateDir, "config.yaml"),
302
+ `project:\n name: branch-test\n root: ${gitRepoDir}\n canonicalBranch: main\nloam:\n enabled: false\n`,
303
+ );
304
+
305
+ // Save and change cwd to the git repo
306
+ process.chdir(gitRepoDir);
307
+
308
+ await primeCommand({});
309
+ const out = output();
310
+
311
+ expect(out).toContain("# Agentplate Context");
312
+
313
+ // Verify session-branch.txt was written
314
+ const sessionBranchPath = join(agentplateDir, "session-branch.txt");
315
+ const content = await Bun.file(sessionBranchPath).text();
316
+ expect(content.trim()).toBe("main");
317
+ } finally {
318
+ process.chdir(originalCwd);
319
+ await cleanupTempDir(gitRepoDir);
320
+ }
321
+ });
322
+
323
+ test("shows session branch in context when different from canonical", async () => {
324
+ const gitRepoDir = await createTempGitRepo();
325
+ try {
326
+ // Create and switch to a feature branch
327
+ const proc = Bun.spawn(["git", "checkout", "-b", "feature/my-work"], {
328
+ cwd: gitRepoDir,
329
+ stdout: "pipe",
330
+ stderr: "pipe",
331
+ });
332
+ await proc.exited;
333
+
334
+ const agentplateDir = join(gitRepoDir, ".agentplate");
335
+ await mkdir(agentplateDir, { recursive: true });
336
+ await Bun.write(
337
+ join(agentplateDir, "config.yaml"),
338
+ `project:\n name: branch-test\n root: ${gitRepoDir}\n canonicalBranch: main\nloam:\n enabled: false\n`,
339
+ );
340
+
341
+ process.chdir(gitRepoDir);
342
+
343
+ await primeCommand({});
344
+ const out = output();
345
+
346
+ expect(out).toContain("Session branch: feature/my-work (merge target)");
347
+
348
+ // Verify session-branch.txt was written with the feature branch
349
+ const sessionBranchPath = join(agentplateDir, "session-branch.txt");
350
+ const content = await Bun.file(sessionBranchPath).text();
351
+ expect(content.trim()).toBe("feature/my-work");
352
+ } finally {
353
+ process.chdir(originalCwd);
354
+ await cleanupTempDir(gitRepoDir);
355
+ }
356
+ });
357
+ });
358
+
359
+ describe("Gitignore auto-heal", () => {
360
+ const expectedGitignore = `# Wildcard+whitelist: ignore everything, whitelist tracked files
361
+ # Auto-healed by ap prime on each session start
362
+ *
363
+ !.gitignore
364
+ !config.yaml
365
+ !agent-manifest.json
366
+ !hooks.json
367
+ !groups.json
368
+ !agent-defs/
369
+ !agent-defs/**
370
+ !README.md
371
+ `;
372
+
373
+ test("creates .agentplate/.gitignore if missing", async () => {
374
+ // The beforeEach creates .agentplate/config.yaml but not .gitignore
375
+ const gitignorePath = join(tempDir, ".agentplate", ".gitignore");
376
+
377
+ // Verify it doesn't exist
378
+ const existsBefore = await Bun.file(gitignorePath).exists();
379
+ expect(existsBefore).toBe(false);
380
+
381
+ // Run primeCommand
382
+ await primeCommand({});
383
+
384
+ // Verify .gitignore was created with correct content
385
+ const content = await Bun.file(gitignorePath).text();
386
+ expect(content).toBe(expectedGitignore);
387
+ });
388
+
389
+ test("overwrites stale .agentplate/.gitignore with current template", async () => {
390
+ // Write an old-style deny-list gitignore
391
+ const gitignorePath = join(tempDir, ".agentplate", ".gitignore");
392
+ const staleContent = `# Old deny-list format
393
+ worktrees/
394
+ logs/
395
+ mail.db
396
+ sessions.db
397
+ `;
398
+ await Bun.write(gitignorePath, staleContent);
399
+
400
+ // Verify stale content is present
401
+ const contentBefore = await Bun.file(gitignorePath).text();
402
+ expect(contentBefore).toBe(staleContent);
403
+
404
+ // Run primeCommand
405
+ await primeCommand({});
406
+
407
+ // Verify .gitignore now has the wildcard+whitelist content
408
+ const contentAfter = await Bun.file(gitignorePath).text();
409
+ expect(contentAfter).toBe(expectedGitignore);
410
+ });
411
+
412
+ test("does not overwrite .agentplate/.gitignore if already correct", async () => {
413
+ // Write the correct AGENTPLATE_GITIGNORE content
414
+ const gitignorePath = join(tempDir, ".agentplate", ".gitignore");
415
+ await Bun.write(gitignorePath, expectedGitignore);
416
+
417
+ // Get file stat before
418
+ const statBefore = await Bun.file(gitignorePath).stat();
419
+ const mtimeBefore = statBefore?.mtime;
420
+
421
+ // Wait a tiny bit to ensure mtime would change if file is rewritten
422
+ await new Promise((resolve) => setTimeout(resolve, 10));
423
+
424
+ // Run primeCommand
425
+ await primeCommand({});
426
+
427
+ // Verify content is still correct
428
+ const contentAfter = await Bun.file(gitignorePath).text();
429
+ expect(contentAfter).toBe(expectedGitignore);
430
+
431
+ // Verify mtime is unchanged (file was not rewritten)
432
+ const statAfter = await Bun.file(gitignorePath).stat();
433
+ const mtimeAfter = statAfter?.mtime;
434
+ expect(mtimeAfter).toEqual(mtimeBefore);
435
+ });
436
+ });
437
+ });
438
+
439
+ describe("formatManifest", () => {
440
+ test("returns 'No agents registered.' for empty agents record", () => {
441
+ const manifest: AgentManifest = {
442
+ version: "1",
443
+ agents: {},
444
+ capabilityIndex: {},
445
+ };
446
+ expect(formatManifest(manifest)).toBe("No agents registered.");
447
+ });
448
+
449
+ test("formats single agent with capabilities", () => {
450
+ const manifest: AgentManifest = {
451
+ version: "1",
452
+ agents: {
453
+ scout: {
454
+ file: "agents/scout.md",
455
+ model: "sonnet",
456
+ tools: ["Read", "Glob"],
457
+ capabilities: ["explore", "analyze"],
458
+ canSpawn: false,
459
+ constraints: [],
460
+ },
461
+ },
462
+ capabilityIndex: { explore: ["scout"], analyze: ["scout"] },
463
+ };
464
+ const result = formatManifest(manifest);
465
+ expect(result).toContain("**scout**");
466
+ expect(result).toContain("[sonnet]");
467
+ expect(result).toContain("explore, analyze");
468
+ expect(result).not.toContain("(can spawn)");
469
+ });
470
+
471
+ test("marks agents that can spawn", () => {
472
+ const manifest: AgentManifest = {
473
+ version: "1",
474
+ agents: {
475
+ lead: {
476
+ file: "agents/lead.md",
477
+ model: "opus",
478
+ tools: ["Read", "Bash"],
479
+ capabilities: ["coordinate"],
480
+ canSpawn: true,
481
+ constraints: [],
482
+ },
483
+ },
484
+ capabilityIndex: { coordinate: ["lead"] },
485
+ };
486
+ const result = formatManifest(manifest);
487
+ expect(result).toContain("(can spawn)");
488
+ });
489
+
490
+ test("formats multiple agents as separate lines", () => {
491
+ const manifest: AgentManifest = {
492
+ version: "1",
493
+ agents: {
494
+ scout: {
495
+ file: "agents/scout.md",
496
+ model: "sonnet",
497
+ tools: [],
498
+ capabilities: ["explore"],
499
+ canSpawn: false,
500
+ constraints: [],
501
+ },
502
+ builder: {
503
+ file: "agents/builder.md",
504
+ model: "opus",
505
+ tools: [],
506
+ capabilities: ["implement"],
507
+ canSpawn: false,
508
+ constraints: [],
509
+ },
510
+ },
511
+ capabilityIndex: {},
512
+ };
513
+ const result = formatManifest(manifest);
514
+ const lines = result.split("\n");
515
+ expect(lines).toHaveLength(2);
516
+ expect(lines[0]).toContain("scout");
517
+ expect(lines[1]).toContain("builder");
518
+ });
519
+ });
520
+
521
+ describe("formatMetrics", () => {
522
+ test("returns 'No recent sessions.' for empty array", () => {
523
+ expect(formatMetrics([])).toBe("No recent sessions.");
524
+ });
525
+
526
+ test("formats a completed session with duration and merge result", () => {
527
+ const sessions: SessionMetrics[] = [
528
+ {
529
+ agentName: "builder-1",
530
+ taskId: "task-001",
531
+ capability: "builder",
532
+ startedAt: "2026-01-01T00:00:00Z",
533
+ completedAt: "2026-01-01T00:05:00Z",
534
+ durationMs: 300_000,
535
+ exitCode: 0,
536
+ mergeResult: "clean-merge",
537
+ parentAgent: "coordinator",
538
+ inputTokens: 0,
539
+ outputTokens: 0,
540
+ cacheReadTokens: 0,
541
+ cacheCreationTokens: 0,
542
+ estimatedCostUsd: null,
543
+ modelUsed: null,
544
+ runId: null,
545
+ },
546
+ ];
547
+ const result = formatMetrics(sessions);
548
+ expect(result).toContain("builder-1");
549
+ expect(result).toContain("(builder)");
550
+ expect(result).toContain("task-001");
551
+ expect(result).toContain("completed");
552
+ expect(result).toContain("(300s)");
553
+ expect(result).toContain("[clean-merge]");
554
+ });
555
+
556
+ test("formats an in-progress session without duration or merge result", () => {
557
+ const sessions: SessionMetrics[] = [
558
+ {
559
+ agentName: "scout-1",
560
+ taskId: "task-002",
561
+ capability: "scout",
562
+ startedAt: "2026-01-01T00:00:00Z",
563
+ completedAt: null,
564
+ durationMs: 0,
565
+ exitCode: null,
566
+ mergeResult: null,
567
+ parentAgent: null,
568
+ inputTokens: 0,
569
+ outputTokens: 0,
570
+ cacheReadTokens: 0,
571
+ cacheCreationTokens: 0,
572
+ estimatedCostUsd: null,
573
+ modelUsed: null,
574
+ runId: null,
575
+ },
576
+ ];
577
+ const result = formatMetrics(sessions);
578
+ expect(result).toContain("scout-1");
579
+ expect(result).toContain("in-progress");
580
+ expect(result).not.toContain("[");
581
+ });
582
+
583
+ test("formats multiple sessions as separate lines", () => {
584
+ const base: SessionMetrics = {
585
+ agentName: "builder-1",
586
+ taskId: "task-001",
587
+ capability: "builder",
588
+ startedAt: "2026-01-01T00:00:00Z",
589
+ completedAt: "2026-01-01T00:05:00Z",
590
+ durationMs: 300_000,
591
+ exitCode: 0,
592
+ mergeResult: null,
593
+ parentAgent: null,
594
+ inputTokens: 0,
595
+ outputTokens: 0,
596
+ cacheReadTokens: 0,
597
+ cacheCreationTokens: 0,
598
+ estimatedCostUsd: null,
599
+ modelUsed: null,
600
+ runId: null,
601
+ };
602
+ const sessions: SessionMetrics[] = [
603
+ base,
604
+ { ...base, agentName: "builder-2", taskId: "task-002" },
605
+ ];
606
+ const result = formatMetrics(sessions);
607
+ const lines = result.split("\n");
608
+ expect(lines).toHaveLength(2);
609
+ expect(lines[0]).toContain("builder-1");
610
+ expect(lines[1]).toContain("builder-2");
611
+ });
612
+ });