@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,762 @@
1
+ /**
2
+ * Tests for `agentplate trace` command.
3
+ *
4
+ * Uses real bun:sqlite (temp files) to test the trace command end-to-end.
5
+ * Captures process.stdout.write to verify output formatting.
6
+ *
7
+ * Real implementations used for: filesystem (temp dirs), SQLite (EventStore,
8
+ * SessionStore). No mocks needed -- all dependencies are cheap and local.
9
+ */
10
+
11
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
12
+ import { mkdtemp } from "node:fs/promises";
13
+ import { tmpdir } from "node:os";
14
+ import { join } from "node:path";
15
+ import { ValidationError } from "../errors.ts";
16
+ import { createEventStore } from "../events/store.ts";
17
+ import { stripAnsi } from "../logging/color.ts";
18
+ import { createSessionStore } from "../sessions/store.ts";
19
+ import { cleanupTempDir } from "../test-helpers.ts";
20
+ import type { InsertEvent } from "../types.ts";
21
+ import { traceCommand } from "./trace.ts";
22
+
23
+ /** Helper to create an InsertEvent with sensible defaults. */
24
+ function makeEvent(overrides: Partial<InsertEvent> = {}): InsertEvent {
25
+ return {
26
+ runId: "run-001",
27
+ agentName: "builder-1",
28
+ sessionId: "sess-abc",
29
+ eventType: "tool_start",
30
+ toolName: "Read",
31
+ toolArgs: '{"file": "src/index.ts"}',
32
+ toolDurationMs: null,
33
+ level: "info",
34
+ data: null,
35
+ ...overrides,
36
+ };
37
+ }
38
+
39
+ describe("traceCommand", () => {
40
+ let chunks: string[];
41
+ let originalWrite: typeof process.stdout.write;
42
+ let tempDir: string;
43
+ let originalCwd: string;
44
+
45
+ beforeEach(async () => {
46
+ // Spy on stdout
47
+ chunks = [];
48
+ originalWrite = process.stdout.write;
49
+ process.stdout.write = ((chunk: string) => {
50
+ chunks.push(chunk);
51
+ return true;
52
+ }) as typeof process.stdout.write;
53
+
54
+ // Create temp dir with .agentplate/config.yaml structure
55
+ tempDir = await mkdtemp(join(tmpdir(), "trace-test-"));
56
+ const agentplateDir = join(tempDir, ".agentplate");
57
+ await Bun.write(
58
+ join(agentplateDir, "config.yaml"),
59
+ `project:\n name: test\n root: ${tempDir}\n canonicalBranch: main\n`,
60
+ );
61
+
62
+ // Change to temp dir so loadConfig() works
63
+ originalCwd = process.cwd();
64
+ process.chdir(tempDir);
65
+ });
66
+
67
+ afterEach(async () => {
68
+ process.stdout.write = originalWrite;
69
+ process.chdir(originalCwd);
70
+ await cleanupTempDir(tempDir);
71
+ });
72
+
73
+ function output(): string {
74
+ return chunks.join("");
75
+ }
76
+
77
+ // === Help flag ===
78
+
79
+ describe("help flag", () => {
80
+ test("--help shows help text", async () => {
81
+ await traceCommand(["--help"]);
82
+ const out = output();
83
+
84
+ expect(out).toContain("trace");
85
+ expect(out).toContain("<target>");
86
+ expect(out).toContain("--json");
87
+ expect(out).toContain("--since");
88
+ expect(out).toContain("--until");
89
+ expect(out).toContain("--limit");
90
+ });
91
+
92
+ test("-h shows help text", async () => {
93
+ await traceCommand(["-h"]);
94
+ const out = output();
95
+
96
+ expect(out).toContain("trace");
97
+ });
98
+ });
99
+
100
+ // === Argument parsing ===
101
+
102
+ describe("argument parsing", () => {
103
+ test("missing target throws an error", async () => {
104
+ await expect(traceCommand([])).rejects.toThrow();
105
+ });
106
+
107
+ test("missing target error mentions the argument name", async () => {
108
+ try {
109
+ await traceCommand([]);
110
+ expect.unreachable("should have thrown");
111
+ } catch (err) {
112
+ expect(err).toBeInstanceOf(Error);
113
+ expect((err as Error).message).toContain("target");
114
+ }
115
+ });
116
+
117
+ test("only flags with no target throws an error", async () => {
118
+ await expect(traceCommand(["--json"])).rejects.toThrow();
119
+ });
120
+
121
+ test("--limit with non-numeric value throws ValidationError", async () => {
122
+ await expect(traceCommand(["builder-1", "--limit", "abc"])).rejects.toThrow(ValidationError);
123
+ });
124
+
125
+ test("--limit with zero throws ValidationError", async () => {
126
+ await expect(traceCommand(["builder-1", "--limit", "0"])).rejects.toThrow(ValidationError);
127
+ });
128
+
129
+ test("--limit with negative value throws ValidationError", async () => {
130
+ await expect(traceCommand(["builder-1", "--limit", "-5"])).rejects.toThrow(ValidationError);
131
+ });
132
+
133
+ test("--since with invalid timestamp throws ValidationError", async () => {
134
+ await expect(traceCommand(["builder-1", "--since", "not-a-date"])).rejects.toThrow(
135
+ ValidationError,
136
+ );
137
+ });
138
+
139
+ test("--until with invalid timestamp throws ValidationError", async () => {
140
+ await expect(traceCommand(["builder-1", "--until", "not-a-date"])).rejects.toThrow(
141
+ ValidationError,
142
+ );
143
+ });
144
+
145
+ test("target is extracted correctly when flags come first", async () => {
146
+ // Create events.db with an event so the command runs to completion
147
+ const dbPath = join(tempDir, ".agentplate", "events.db");
148
+ const store = createEventStore(dbPath);
149
+ store.insert(makeEvent({ agentName: "my-agent" }));
150
+ store.close();
151
+
152
+ await traceCommand(["--json", "--limit", "50", "my-agent"]);
153
+ const out = output();
154
+ const parsed = JSON.parse(out.trim()) as { events: unknown[] };
155
+ expect(parsed.events).toHaveLength(1);
156
+ });
157
+
158
+ test("target is extracted correctly when flags come after", async () => {
159
+ const dbPath = join(tempDir, ".agentplate", "events.db");
160
+ const store = createEventStore(dbPath);
161
+ store.insert(makeEvent({ agentName: "my-agent" }));
162
+ store.close();
163
+
164
+ await traceCommand(["my-agent", "--json", "--limit", "50"]);
165
+ const out = output();
166
+ const parsed = JSON.parse(out.trim()) as { events: unknown[] };
167
+ expect(parsed.events).toHaveLength(1);
168
+ });
169
+ });
170
+
171
+ // === Missing events.db (graceful handling) ===
172
+
173
+ describe("missing events.db", () => {
174
+ test("text mode outputs friendly message when no events.db exists", async () => {
175
+ await traceCommand(["builder-1"]);
176
+ const out = output();
177
+
178
+ expect(out).toBe("No events data yet.\n");
179
+ });
180
+
181
+ test("JSON mode outputs empty array when no events.db exists", async () => {
182
+ await traceCommand(["builder-1", "--json"]);
183
+ const out = output();
184
+
185
+ const parsed = JSON.parse(out.trim()) as {
186
+ success: boolean;
187
+ command: string;
188
+ events: unknown[];
189
+ };
190
+ expect(parsed.success).toBe(true);
191
+ expect(parsed.command).toBe("trace");
192
+ expect(parsed.events).toEqual([]);
193
+ });
194
+ });
195
+
196
+ // === JSON output mode ===
197
+
198
+ describe("JSON output mode", () => {
199
+ test("outputs valid JSON array with events", async () => {
200
+ const dbPath = join(tempDir, ".agentplate", "events.db");
201
+ const store = createEventStore(dbPath);
202
+ store.insert(makeEvent({ agentName: "builder-1", eventType: "session_start" }));
203
+ store.insert(makeEvent({ agentName: "builder-1", eventType: "tool_start" }));
204
+ store.insert(makeEvent({ agentName: "builder-1", eventType: "session_end" }));
205
+ store.close();
206
+
207
+ await traceCommand(["builder-1", "--json"]);
208
+ const out = output();
209
+
210
+ const parsed = JSON.parse(out.trim()) as { events: unknown[] };
211
+ expect(parsed.events).toHaveLength(3);
212
+ expect(Array.isArray(parsed.events)).toBe(true);
213
+ });
214
+
215
+ test("JSON output includes expected fields", async () => {
216
+ const dbPath = join(tempDir, ".agentplate", "events.db");
217
+ const store = createEventStore(dbPath);
218
+ store.insert(
219
+ makeEvent({
220
+ agentName: "builder-1",
221
+ eventType: "tool_start",
222
+ toolName: "Bash",
223
+ level: "info",
224
+ }),
225
+ );
226
+ store.close();
227
+
228
+ await traceCommand(["builder-1", "--json"]);
229
+ const out = output();
230
+
231
+ const parsed = JSON.parse(out.trim()) as { events: Record<string, unknown>[] };
232
+ expect(parsed.events).toHaveLength(1);
233
+ const event = parsed.events[0];
234
+ expect(event).toBeDefined();
235
+ expect(event?.agentName).toBe("builder-1");
236
+ expect(event?.eventType).toBe("tool_start");
237
+ expect(event?.toolName).toBe("Bash");
238
+ expect(event?.level).toBe("info");
239
+ expect(event?.createdAt).toBeTruthy();
240
+ });
241
+
242
+ test("JSON output returns empty array when no events match agent", async () => {
243
+ const dbPath = join(tempDir, ".agentplate", "events.db");
244
+ const store = createEventStore(dbPath);
245
+ store.insert(makeEvent({ agentName: "other-agent" }));
246
+ store.close();
247
+
248
+ await traceCommand(["builder-1", "--json"]);
249
+ const out = output();
250
+
251
+ const parsed = JSON.parse(out.trim()) as { events: unknown[] };
252
+ expect(parsed.events).toEqual([]);
253
+ });
254
+ });
255
+
256
+ // === Timeline output format ===
257
+
258
+ describe("timeline output", () => {
259
+ test("shows header with agent name", async () => {
260
+ const dbPath = join(tempDir, ".agentplate", "events.db");
261
+ const store = createEventStore(dbPath);
262
+ store.insert(makeEvent({ agentName: "builder-1" }));
263
+ store.close();
264
+
265
+ await traceCommand(["builder-1"]);
266
+ const out = output();
267
+
268
+ expect(stripAnsi(out)).toContain("Timeline for builder-1");
269
+ });
270
+
271
+ test("shows event count", async () => {
272
+ const dbPath = join(tempDir, ".agentplate", "events.db");
273
+ const store = createEventStore(dbPath);
274
+ store.insert(makeEvent({ agentName: "builder-1", eventType: "session_start" }));
275
+ store.insert(makeEvent({ agentName: "builder-1", eventType: "tool_start" }));
276
+ store.insert(makeEvent({ agentName: "builder-1", eventType: "session_end" }));
277
+ store.close();
278
+
279
+ await traceCommand(["builder-1"]);
280
+ const out = output();
281
+
282
+ expect(out).toContain("3 events");
283
+ });
284
+
285
+ test("shows singular event count", async () => {
286
+ const dbPath = join(tempDir, ".agentplate", "events.db");
287
+ const store = createEventStore(dbPath);
288
+ store.insert(makeEvent({ agentName: "builder-1" }));
289
+ store.close();
290
+
291
+ await traceCommand(["builder-1"]);
292
+ const out = output();
293
+
294
+ expect(out).toContain("1 event");
295
+ // Should NOT say "1 events"
296
+ expect(out).not.toMatch(/1 events/);
297
+ });
298
+
299
+ test("no events shows 'No events found' message", async () => {
300
+ const dbPath = join(tempDir, ".agentplate", "events.db");
301
+ const store = createEventStore(dbPath);
302
+ // Create the DB but don't insert anything for builder-1
303
+ store.insert(makeEvent({ agentName: "other-agent" }));
304
+ store.close();
305
+
306
+ await traceCommand(["builder-1"]);
307
+ const out = output();
308
+
309
+ expect(out).toContain("No events found");
310
+ });
311
+
312
+ test("shows separator line", async () => {
313
+ const dbPath = join(tempDir, ".agentplate", "events.db");
314
+ const store = createEventStore(dbPath);
315
+ store.insert(makeEvent({ agentName: "builder-1" }));
316
+ store.close();
317
+
318
+ await traceCommand(["builder-1"]);
319
+ const out = output();
320
+
321
+ expect(out).toContain("─".repeat(70));
322
+ });
323
+
324
+ test("event type labels are shown", async () => {
325
+ const dbPath = join(tempDir, ".agentplate", "events.db");
326
+ const store = createEventStore(dbPath);
327
+ store.insert(makeEvent({ agentName: "builder-1", eventType: "session_start" }));
328
+ store.insert(makeEvent({ agentName: "builder-1", eventType: "tool_start" }));
329
+ store.insert(makeEvent({ agentName: "builder-1", eventType: "error", level: "error" }));
330
+ store.close();
331
+
332
+ await traceCommand(["builder-1"]);
333
+ const out = output();
334
+
335
+ expect(out).toContain("SESSION +");
336
+ expect(out).toContain("TOOL START");
337
+ expect(out).toContain("ERROR");
338
+ });
339
+
340
+ test("tool name is shown in detail", async () => {
341
+ const dbPath = join(tempDir, ".agentplate", "events.db");
342
+ const store = createEventStore(dbPath);
343
+ store.insert(
344
+ makeEvent({
345
+ agentName: "builder-1",
346
+ eventType: "tool_start",
347
+ toolName: "Bash",
348
+ }),
349
+ );
350
+ store.close();
351
+
352
+ await traceCommand(["builder-1"]);
353
+ const out = output();
354
+
355
+ expect(out).toContain("tool=Bash");
356
+ });
357
+
358
+ test("tool duration is shown in detail", async () => {
359
+ const dbPath = join(tempDir, ".agentplate", "events.db");
360
+ const store = createEventStore(dbPath);
361
+ store.insert(
362
+ makeEvent({
363
+ agentName: "builder-1",
364
+ eventType: "tool_start",
365
+ toolName: "Read",
366
+ toolDurationMs: 42,
367
+ }),
368
+ );
369
+ store.close();
370
+
371
+ await traceCommand(["builder-1"]);
372
+ const out = output();
373
+
374
+ expect(out).toContain("dur=42ms");
375
+ });
376
+
377
+ test("custom data fields are shown in detail", async () => {
378
+ const dbPath = join(tempDir, ".agentplate", "events.db");
379
+ const store = createEventStore(dbPath);
380
+ store.insert(
381
+ makeEvent({
382
+ agentName: "builder-1",
383
+ eventType: "custom",
384
+ toolName: null,
385
+ data: '{"reason":"testing","count":5}',
386
+ }),
387
+ );
388
+ store.close();
389
+
390
+ await traceCommand(["builder-1"]);
391
+ const out = output();
392
+
393
+ expect(out).toContain('data={"reason":"testing","count":5}');
394
+ });
395
+
396
+ test("date separator appears in timeline", async () => {
397
+ const dbPath = join(tempDir, ".agentplate", "events.db");
398
+ const store = createEventStore(dbPath);
399
+ store.insert(makeEvent({ agentName: "builder-1" }));
400
+ store.close();
401
+
402
+ await traceCommand(["builder-1"]);
403
+ const out = output();
404
+
405
+ // Should contain a date separator with --- prefix
406
+ expect(out).toMatch(/---\s+\d{4}-\d{2}-\d{2}\s+---/);
407
+ });
408
+ });
409
+
410
+ // === --limit flag ===
411
+
412
+ describe("--limit flag", () => {
413
+ test("limits the number of events returned", async () => {
414
+ const dbPath = join(tempDir, ".agentplate", "events.db");
415
+ const store = createEventStore(dbPath);
416
+ for (let i = 0; i < 10; i++) {
417
+ store.insert(makeEvent({ agentName: "builder-1" }));
418
+ }
419
+ store.close();
420
+
421
+ await traceCommand(["builder-1", "--json", "--limit", "3"]);
422
+ const out = output();
423
+
424
+ const parsed = JSON.parse(out.trim()) as { events: unknown[] };
425
+ expect(parsed.events).toHaveLength(3);
426
+ });
427
+
428
+ test("default limit is 100", async () => {
429
+ const dbPath = join(tempDir, ".agentplate", "events.db");
430
+ const store = createEventStore(dbPath);
431
+ for (let i = 0; i < 120; i++) {
432
+ store.insert(makeEvent({ agentName: "builder-1" }));
433
+ }
434
+ store.close();
435
+
436
+ await traceCommand(["builder-1", "--json"]);
437
+ const out = output();
438
+
439
+ const parsed = JSON.parse(out.trim()) as { events: unknown[] };
440
+ expect(parsed.events).toHaveLength(100);
441
+ });
442
+ });
443
+
444
+ // === --since and --until flags ===
445
+
446
+ describe("--since and --until flags", () => {
447
+ test("--since filters events after a timestamp", async () => {
448
+ const dbPath = join(tempDir, ".agentplate", "events.db");
449
+ const store = createEventStore(dbPath);
450
+
451
+ // Insert events -- all get "now" timestamps from SQLite
452
+ store.insert(makeEvent({ agentName: "builder-1" }));
453
+ store.close();
454
+
455
+ // A future timestamp should return no events
456
+ await traceCommand(["builder-1", "--json", "--since", "2099-01-01T00:00:00Z"]);
457
+ const out = output();
458
+
459
+ const parsed = JSON.parse(out.trim()) as { events: unknown[] };
460
+ expect(parsed.events).toEqual([]);
461
+ });
462
+
463
+ test("--since with past timestamp returns all events", async () => {
464
+ const dbPath = join(tempDir, ".agentplate", "events.db");
465
+ const store = createEventStore(dbPath);
466
+ store.insert(makeEvent({ agentName: "builder-1" }));
467
+ store.insert(makeEvent({ agentName: "builder-1" }));
468
+ store.close();
469
+
470
+ await traceCommand(["builder-1", "--json", "--since", "2020-01-01T00:00:00Z"]);
471
+ const out = output();
472
+
473
+ const parsed = JSON.parse(out.trim()) as { events: unknown[] };
474
+ expect(parsed.events).toHaveLength(2);
475
+ });
476
+
477
+ test("--until with past timestamp returns no events", async () => {
478
+ const dbPath = join(tempDir, ".agentplate", "events.db");
479
+ const store = createEventStore(dbPath);
480
+ store.insert(makeEvent({ agentName: "builder-1" }));
481
+ store.close();
482
+
483
+ await traceCommand(["builder-1", "--json", "--until", "2000-01-01T00:00:00Z"]);
484
+ const out = output();
485
+
486
+ const parsed = JSON.parse(out.trim()) as { events: unknown[] };
487
+ expect(parsed.events).toEqual([]);
488
+ });
489
+
490
+ test("--since causes absolute timestamps in text mode", async () => {
491
+ const dbPath = join(tempDir, ".agentplate", "events.db");
492
+ const store = createEventStore(dbPath);
493
+ store.insert(makeEvent({ agentName: "builder-1" }));
494
+ store.close();
495
+
496
+ await traceCommand(["builder-1", "--since", "2020-01-01T00:00:00Z"]);
497
+ const out = output();
498
+
499
+ // Absolute timestamps show HH:MM:SS format
500
+ expect(out).toMatch(/\d{2}:\d{2}:\d{2}/);
501
+ });
502
+
503
+ test("valid --since timestamp is accepted", async () => {
504
+ const dbPath = join(tempDir, ".agentplate", "events.db");
505
+ const store = createEventStore(dbPath);
506
+ store.insert(makeEvent({ agentName: "builder-1" }));
507
+ store.close();
508
+
509
+ // Should not throw
510
+ await traceCommand(["builder-1", "--json", "--since", "2024-06-15T12:00:00Z"]);
511
+ const out = output();
512
+ // Should be valid JSON
513
+ JSON.parse(out.trim());
514
+ });
515
+ });
516
+
517
+ // === Target resolution ===
518
+
519
+ describe("target resolution", () => {
520
+ test("agent name is used as-is when not a task ID pattern", async () => {
521
+ const dbPath = join(tempDir, ".agentplate", "events.db");
522
+ const store = createEventStore(dbPath);
523
+ store.insert(makeEvent({ agentName: "my-custom-agent" }));
524
+ store.close();
525
+
526
+ await traceCommand(["my-custom-agent", "--json"]);
527
+ const out = output();
528
+
529
+ const parsed = JSON.parse(out.trim()) as { events: Record<string, unknown>[] };
530
+ expect(parsed.events).toHaveLength(1);
531
+ expect(parsed.events[0]?.agentName).toBe("my-custom-agent");
532
+ });
533
+
534
+ test("task ID pattern is detected and resolved to agent name via SessionStore", async () => {
535
+ // Create a session that maps task ID to agent name
536
+ const sessDbPath = join(tempDir, ".agentplate", "sessions.db");
537
+ const sessionStore = createSessionStore(sessDbPath);
538
+ sessionStore.upsert({
539
+ id: "sess-001",
540
+ agentName: "builder-for-task",
541
+ capability: "builder",
542
+ worktreePath: "/tmp/wt",
543
+ branchName: "feat/task",
544
+ taskId: "agentplate-rj1k",
545
+ tmuxSession: "tmux-001",
546
+ state: "completed",
547
+ pid: null,
548
+ parentAgent: null,
549
+ depth: 0,
550
+ runId: null,
551
+ startedAt: new Date().toISOString(),
552
+ lastActivity: new Date().toISOString(),
553
+ escalationLevel: 0,
554
+ stalledSince: null,
555
+ transcriptPath: null,
556
+ });
557
+ sessionStore.close();
558
+
559
+ // Create events for the agent name that the bead resolves to
560
+ const eventsDbPath = join(tempDir, ".agentplate", "events.db");
561
+ const eventStore = createEventStore(eventsDbPath);
562
+ eventStore.insert(makeEvent({ agentName: "builder-for-task" }));
563
+ eventStore.close();
564
+
565
+ await traceCommand(["agentplate-rj1k", "--json"]);
566
+ const out = output();
567
+
568
+ const parsed = JSON.parse(out.trim()) as { events: Record<string, unknown>[] };
569
+ expect(parsed.events).toHaveLength(1);
570
+ expect(parsed.events[0]?.agentName).toBe("builder-for-task");
571
+ });
572
+
573
+ test("unresolved task ID falls back to using task ID as agent name", async () => {
574
+ // Create sessions.db but with no matching bead
575
+ const sessDbPath = join(tempDir, ".agentplate", "sessions.db");
576
+ const sessionStore = createSessionStore(sessDbPath);
577
+ sessionStore.close();
578
+
579
+ // Create events.db (empty for this bead)
580
+ const eventsDbPath = join(tempDir, ".agentplate", "events.db");
581
+ const eventStore = createEventStore(eventsDbPath);
582
+ eventStore.close();
583
+
584
+ await traceCommand(["myproj-abc1", "--json"]);
585
+ const out = output();
586
+
587
+ const parsed = JSON.parse(out.trim()) as { events: unknown[] };
588
+ expect(parsed.events).toEqual([]);
589
+ });
590
+
591
+ test("short agent names without task pattern are not resolved as task IDs", async () => {
592
+ const dbPath = join(tempDir, ".agentplate", "events.db");
593
+ const store = createEventStore(dbPath);
594
+ store.insert(makeEvent({ agentName: "scout" }));
595
+ store.close();
596
+
597
+ // "scout" does not match task pattern word-alphanumeric
598
+ await traceCommand(["scout", "--json"]);
599
+ const out = output();
600
+
601
+ const parsed = JSON.parse(out.trim()) as { events: Record<string, unknown>[] };
602
+ expect(parsed.events).toHaveLength(1);
603
+ expect(parsed.events[0]?.agentName).toBe("scout");
604
+ });
605
+ });
606
+
607
+ // === Event filtering edge cases ===
608
+
609
+ describe("edge cases", () => {
610
+ test("only returns events for the specified agent", async () => {
611
+ const dbPath = join(tempDir, ".agentplate", "events.db");
612
+ const store = createEventStore(dbPath);
613
+ store.insert(makeEvent({ agentName: "builder-1" }));
614
+ store.insert(makeEvent({ agentName: "builder-2" }));
615
+ store.insert(makeEvent({ agentName: "builder-1" }));
616
+ store.insert(makeEvent({ agentName: "scout-1" }));
617
+ store.close();
618
+
619
+ await traceCommand(["builder-1", "--json"]);
620
+ const out = output();
621
+
622
+ const parsed = JSON.parse(out.trim()) as { events: Record<string, unknown>[] };
623
+ expect(parsed.events).toHaveLength(2);
624
+ for (const event of parsed.events) {
625
+ expect(event.agentName).toBe("builder-1");
626
+ }
627
+ });
628
+
629
+ test("all event types have labeled output", async () => {
630
+ const dbPath = join(tempDir, ".agentplate", "events.db");
631
+ const store = createEventStore(dbPath);
632
+ const eventTypes = [
633
+ "tool_start",
634
+ "tool_end",
635
+ "session_start",
636
+ "session_end",
637
+ "mail_sent",
638
+ "mail_received",
639
+ "spawn",
640
+ "error",
641
+ "custom",
642
+ "turn_start",
643
+ "turn_end",
644
+ "progress",
645
+ "result",
646
+ ] as const;
647
+ for (const eventType of eventTypes) {
648
+ store.insert(
649
+ makeEvent({
650
+ agentName: "builder-1",
651
+ eventType,
652
+ level: eventType === "error" ? "error" : "info",
653
+ }),
654
+ );
655
+ }
656
+ store.close();
657
+
658
+ await traceCommand(["builder-1"]);
659
+ const out = output();
660
+
661
+ // Verify all expected labels appear
662
+ expect(out).toContain("TOOL START");
663
+ expect(out).toContain("TOOL END");
664
+ expect(out).toContain("SESSION +");
665
+ expect(out).toContain("SESSION -");
666
+ expect(out).toContain("MAIL SENT");
667
+ expect(out).toContain("MAIL RECV");
668
+ expect(out).toContain("SPAWN");
669
+ expect(out).toContain("ERROR");
670
+ expect(out).toContain("CUSTOM");
671
+ expect(out).toContain("TURN START");
672
+ expect(out).toContain("TURN END");
673
+ expect(out).toContain("PROGRESS");
674
+ expect(out).toContain("RESULT");
675
+ });
676
+
677
+ test("long data values are truncated", async () => {
678
+ const dbPath = join(tempDir, ".agentplate", "events.db");
679
+ const store = createEventStore(dbPath);
680
+ const longValue = "x".repeat(200);
681
+ store.insert(
682
+ makeEvent({
683
+ agentName: "builder-1",
684
+ eventType: "custom",
685
+ toolName: null,
686
+ data: JSON.stringify({ message: longValue }),
687
+ }),
688
+ );
689
+ store.close();
690
+
691
+ await traceCommand(["builder-1"]);
692
+ const out = output();
693
+
694
+ // The full 200-char value should not appear
695
+ expect(out).not.toContain(longValue);
696
+ // But a truncated version with "…" should
697
+ expect(out).toContain("…");
698
+ });
699
+
700
+ test("non-JSON data is shown raw if short", async () => {
701
+ const dbPath = join(tempDir, ".agentplate", "events.db");
702
+ const store = createEventStore(dbPath);
703
+ store.insert(
704
+ makeEvent({
705
+ agentName: "builder-1",
706
+ eventType: "custom",
707
+ toolName: null,
708
+ data: "simple text data",
709
+ }),
710
+ );
711
+ store.close();
712
+
713
+ await traceCommand(["builder-1"]);
714
+ const out = output();
715
+
716
+ expect(out).toContain("simple text data");
717
+ });
718
+
719
+ test("events are ordered chronologically", async () => {
720
+ const dbPath = join(tempDir, ".agentplate", "events.db");
721
+ const store = createEventStore(dbPath);
722
+ store.insert(makeEvent({ agentName: "builder-1", eventType: "session_start" }));
723
+ store.insert(makeEvent({ agentName: "builder-1", eventType: "tool_start" }));
724
+ store.insert(makeEvent({ agentName: "builder-1", eventType: "session_end" }));
725
+ store.close();
726
+
727
+ await traceCommand(["builder-1", "--json"]);
728
+ const out = output();
729
+
730
+ const parsed = JSON.parse(out.trim()) as { events: Record<string, unknown>[] };
731
+ expect(parsed.events).toHaveLength(3);
732
+ expect(parsed.events[0]?.eventType).toBe("session_start");
733
+ expect(parsed.events[1]?.eventType).toBe("tool_start");
734
+ expect(parsed.events[2]?.eventType).toBe("session_end");
735
+ });
736
+
737
+ test("handles event with all null optional fields", async () => {
738
+ const dbPath = join(tempDir, ".agentplate", "events.db");
739
+ const store = createEventStore(dbPath);
740
+ store.insert(
741
+ makeEvent({
742
+ agentName: "builder-1",
743
+ eventType: "session_start",
744
+ runId: null,
745
+ sessionId: null,
746
+ toolName: null,
747
+ toolArgs: null,
748
+ toolDurationMs: null,
749
+ data: null,
750
+ }),
751
+ );
752
+ store.close();
753
+
754
+ // Should not throw
755
+ await traceCommand(["builder-1"]);
756
+ const out = output();
757
+
758
+ expect(stripAnsi(out)).toContain("Timeline for builder-1");
759
+ expect(out).toContain("1 event");
760
+ });
761
+ });
762
+ });