@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,709 @@
1
+ /**
2
+ * Tests for `agentplate feed` command.
3
+ *
4
+ * Uses real bun:sqlite (temp files) to test the feed 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
+ * 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 type { ColorFn } from "../logging/color.ts";
18
+ import { cleanupTempDir } from "../test-helpers.ts";
19
+ import type { InsertEvent, StoredEvent } from "../types.ts";
20
+ import { feedCommand, pollFeedTick } from "./feed.ts";
21
+
22
+ /** Helper to create an InsertEvent with sensible defaults. */
23
+ function makeEvent(overrides: Partial<InsertEvent> = {}): InsertEvent {
24
+ return {
25
+ runId: "run-001",
26
+ agentName: "builder-1",
27
+ sessionId: "sess-abc",
28
+ eventType: "tool_start",
29
+ toolName: "Read",
30
+ toolArgs: '{"file": "src/index.ts"}',
31
+ toolDurationMs: null,
32
+ level: "info",
33
+ data: null,
34
+ ...overrides,
35
+ };
36
+ }
37
+
38
+ describe("feedCommand", () => {
39
+ let chunks: string[];
40
+ let originalWrite: typeof process.stdout.write;
41
+ let tempDir: string;
42
+ let originalCwd: string;
43
+
44
+ beforeEach(async () => {
45
+ // Spy on stdout
46
+ chunks = [];
47
+ originalWrite = process.stdout.write;
48
+ process.stdout.write = ((chunk: string) => {
49
+ chunks.push(chunk);
50
+ return true;
51
+ }) as typeof process.stdout.write;
52
+
53
+ // Create temp dir with .agentplate/config.yaml structure
54
+ tempDir = await mkdtemp(join(tmpdir(), "feed-test-"));
55
+ const agentplateDir = join(tempDir, ".agentplate");
56
+ await Bun.write(
57
+ join(agentplateDir, "config.yaml"),
58
+ `project:\n name: test\n root: ${tempDir}\n canonicalBranch: main\n`,
59
+ );
60
+
61
+ // Change to temp dir so loadConfig() works
62
+ originalCwd = process.cwd();
63
+ process.chdir(tempDir);
64
+ });
65
+
66
+ afterEach(async () => {
67
+ process.stdout.write = originalWrite;
68
+ process.chdir(originalCwd);
69
+ await cleanupTempDir(tempDir);
70
+ });
71
+
72
+ function output(): string {
73
+ return chunks.join("");
74
+ }
75
+
76
+ // === Help flag ===
77
+
78
+ describe("help flag", () => {
79
+ test("--help shows help text", async () => {
80
+ await feedCommand(["--help"]);
81
+ const out = output();
82
+
83
+ expect(out).toContain("feed");
84
+ expect(out).toContain("--follow");
85
+ expect(out).toContain("--agent");
86
+ expect(out).toContain("--run");
87
+ expect(out).toContain("--since");
88
+ expect(out).toContain("--limit");
89
+ expect(out).toContain("--interval");
90
+ expect(out).toContain("--json");
91
+ });
92
+
93
+ test("-h shows help text", async () => {
94
+ await feedCommand(["-h"]);
95
+ const out = output();
96
+
97
+ expect(out).toContain("feed");
98
+ });
99
+ });
100
+
101
+ // === Argument parsing ===
102
+
103
+ describe("argument parsing", () => {
104
+ test("--limit with non-numeric value throws ValidationError", async () => {
105
+ await expect(feedCommand(["--limit", "abc"])).rejects.toThrow(ValidationError);
106
+ });
107
+
108
+ test("--limit with zero throws ValidationError", async () => {
109
+ await expect(feedCommand(["--limit", "0"])).rejects.toThrow(ValidationError);
110
+ });
111
+
112
+ test("--limit with negative value throws ValidationError", async () => {
113
+ await expect(feedCommand(["--limit", "-5"])).rejects.toThrow(ValidationError);
114
+ });
115
+
116
+ test("--interval with non-numeric value throws ValidationError", async () => {
117
+ await expect(feedCommand(["--interval", "abc"])).rejects.toThrow(ValidationError);
118
+ });
119
+
120
+ test("--interval below 200 throws ValidationError", async () => {
121
+ await expect(feedCommand(["--interval", "100"])).rejects.toThrow(ValidationError);
122
+ });
123
+
124
+ test("--since with invalid timestamp throws ValidationError", async () => {
125
+ await expect(feedCommand(["--since", "not-a-date"])).rejects.toThrow(ValidationError);
126
+ });
127
+ });
128
+
129
+ // === Missing events.db (graceful handling) ===
130
+
131
+ describe("missing events.db", () => {
132
+ test("text mode outputs friendly message when no events.db exists", async () => {
133
+ await feedCommand([]);
134
+ const out = output();
135
+
136
+ expect(out).toBe("No events data yet.\n");
137
+ });
138
+
139
+ test("JSON mode outputs empty array when no events.db exists", async () => {
140
+ await feedCommand(["--json"]);
141
+ const out = output();
142
+
143
+ const parsed = JSON.parse(out.trim()) as {
144
+ success: boolean;
145
+ command: string;
146
+ events: unknown[];
147
+ };
148
+ expect(parsed.success).toBe(true);
149
+ expect(parsed.command).toBe("feed");
150
+ expect(parsed.events).toEqual([]);
151
+ });
152
+ });
153
+
154
+ // === JSON output mode ===
155
+
156
+ describe("JSON output mode", () => {
157
+ test("outputs valid JSON array with events", async () => {
158
+ const dbPath = join(tempDir, ".agentplate", "events.db");
159
+ const store = createEventStore(dbPath);
160
+ store.insert(makeEvent({ agentName: "builder-1", eventType: "session_start" }));
161
+ store.insert(makeEvent({ agentName: "builder-2", eventType: "tool_start" }));
162
+ store.insert(makeEvent({ agentName: "builder-1", eventType: "session_end" }));
163
+ store.close();
164
+
165
+ await feedCommand(["--json"]);
166
+ const out = output();
167
+
168
+ const parsed = JSON.parse(out.trim()) as { events: unknown[] };
169
+ expect(parsed.events).toHaveLength(3);
170
+ expect(Array.isArray(parsed.events)).toBe(true);
171
+ });
172
+
173
+ test("JSON output includes expected fields", async () => {
174
+ const dbPath = join(tempDir, ".agentplate", "events.db");
175
+ const store = createEventStore(dbPath);
176
+ store.insert(
177
+ makeEvent({
178
+ agentName: "builder-1",
179
+ eventType: "tool_start",
180
+ toolName: "Bash",
181
+ level: "info",
182
+ }),
183
+ );
184
+ store.close();
185
+
186
+ await feedCommand(["--json"]);
187
+ const out = output();
188
+
189
+ const parsed = JSON.parse(out.trim()) as { events: Record<string, unknown>[] };
190
+ expect(parsed.events).toHaveLength(1);
191
+ const event = parsed.events[0];
192
+ expect(event).toBeDefined();
193
+ expect(event?.agentName).toBe("builder-1");
194
+ expect(event?.eventType).toBe("tool_start");
195
+ expect(event?.toolName).toBe("Bash");
196
+ expect(event?.level).toBe("info");
197
+ expect(event?.createdAt).toBeTruthy();
198
+ });
199
+
200
+ test("JSON output returns empty array when no events match since filter", async () => {
201
+ const dbPath = join(tempDir, ".agentplate", "events.db");
202
+ const store = createEventStore(dbPath);
203
+ store.insert(makeEvent({ agentName: "builder-1" }));
204
+ store.close();
205
+
206
+ // Query from future date
207
+ await feedCommand(["--json", "--since", "2099-01-01T00:00:00Z"]);
208
+ const out = output();
209
+
210
+ const parsed = JSON.parse(out.trim()) as { events: unknown[] };
211
+ expect(parsed.events).toEqual([]);
212
+ });
213
+ });
214
+
215
+ // === Feed output format ===
216
+
217
+ describe("feed output", () => {
218
+ test("shows events from multiple agents", async () => {
219
+ const dbPath = join(tempDir, ".agentplate", "events.db");
220
+ const store = createEventStore(dbPath);
221
+ store.insert(makeEvent({ agentName: "builder-1" }));
222
+ store.insert(makeEvent({ agentName: "scout-1" }));
223
+ store.insert(makeEvent({ agentName: "builder-2" }));
224
+ store.close();
225
+
226
+ await feedCommand([]);
227
+ const out = output();
228
+
229
+ expect(out).toContain("builder-1");
230
+ expect(out).toContain("scout-1");
231
+ expect(out).toContain("builder-2");
232
+ });
233
+
234
+ test("compact event labels are shown", async () => {
235
+ const dbPath = join(tempDir, ".agentplate", "events.db");
236
+ const store = createEventStore(dbPath);
237
+ store.insert(makeEvent({ agentName: "builder-1", eventType: "session_start" }));
238
+ store.insert(makeEvent({ agentName: "builder-1", eventType: "tool_start" }));
239
+ store.insert(makeEvent({ agentName: "builder-1", eventType: "mail_sent" }));
240
+ store.insert(makeEvent({ agentName: "builder-1", eventType: "error", level: "error" }));
241
+ store.close();
242
+
243
+ await feedCommand([]);
244
+ const out = output();
245
+
246
+ expect(out).toContain("SESS+");
247
+ expect(out).toContain("TOOL+");
248
+ expect(out).toContain("MAIL>");
249
+ expect(out).toContain("ERROR");
250
+ });
251
+
252
+ test("tool name is shown in detail", async () => {
253
+ const dbPath = join(tempDir, ".agentplate", "events.db");
254
+ const store = createEventStore(dbPath);
255
+ store.insert(
256
+ makeEvent({
257
+ agentName: "builder-1",
258
+ eventType: "tool_start",
259
+ toolName: "Bash",
260
+ }),
261
+ );
262
+ store.close();
263
+
264
+ await feedCommand([]);
265
+ const out = output();
266
+
267
+ expect(out).toContain("tool=Bash");
268
+ });
269
+
270
+ test("tool duration is shown in detail", async () => {
271
+ const dbPath = join(tempDir, ".agentplate", "events.db");
272
+ const store = createEventStore(dbPath);
273
+ store.insert(
274
+ makeEvent({
275
+ agentName: "builder-1",
276
+ eventType: "tool_start",
277
+ toolName: "Read",
278
+ toolDurationMs: 42,
279
+ }),
280
+ );
281
+ store.close();
282
+
283
+ await feedCommand([]);
284
+ const out = output();
285
+
286
+ expect(out).toContain("42ms");
287
+ });
288
+
289
+ test("absolute time format is shown (HH:MM:SS)", async () => {
290
+ const dbPath = join(tempDir, ".agentplate", "events.db");
291
+ const store = createEventStore(dbPath);
292
+ store.insert(makeEvent({ agentName: "builder-1" }));
293
+ store.close();
294
+
295
+ await feedCommand([]);
296
+ const out = output();
297
+
298
+ // Should show HH:MM:SS format
299
+ expect(out).toMatch(/\d{2}:\d{2}:\d{2}/);
300
+ });
301
+
302
+ test("no events shows 'No events found' message", async () => {
303
+ const dbPath = join(tempDir, ".agentplate", "events.db");
304
+ const store = createEventStore(dbPath);
305
+ // Create DB but no events
306
+ store.close();
307
+
308
+ await feedCommand([]);
309
+ const out = output();
310
+
311
+ expect(out).toContain("No events found");
312
+ });
313
+ });
314
+
315
+ // === --agent filter ===
316
+
317
+ describe("--agent filter", () => {
318
+ test("filters to single agent", async () => {
319
+ const dbPath = join(tempDir, ".agentplate", "events.db");
320
+ const store = createEventStore(dbPath);
321
+ store.insert(makeEvent({ agentName: "builder-1" }));
322
+ store.insert(makeEvent({ agentName: "scout-1" }));
323
+ store.insert(makeEvent({ agentName: "builder-2" }));
324
+ store.close();
325
+
326
+ await feedCommand(["--agent", "builder-1", "--json"]);
327
+ const out = output();
328
+
329
+ const parsed = JSON.parse(out.trim()) as { events: Record<string, unknown>[] };
330
+ expect(parsed.events).toHaveLength(1);
331
+ expect(parsed.events[0]?.agentName).toBe("builder-1");
332
+ });
333
+
334
+ test("filters to multiple agents", async () => {
335
+ const dbPath = join(tempDir, ".agentplate", "events.db");
336
+ const store = createEventStore(dbPath);
337
+ store.insert(makeEvent({ agentName: "builder-1" }));
338
+ store.insert(makeEvent({ agentName: "scout-1" }));
339
+ store.insert(makeEvent({ agentName: "builder-2" }));
340
+ store.close();
341
+
342
+ await feedCommand(["--agent", "builder-1", "--agent", "scout-1", "--json"]);
343
+ const out = output();
344
+
345
+ const parsed = JSON.parse(out.trim()) as { events: Record<string, unknown>[] };
346
+ expect(parsed.events).toHaveLength(2);
347
+ const agents = parsed.events.map((e) => e.agentName);
348
+ expect(agents).toContain("builder-1");
349
+ expect(agents).toContain("scout-1");
350
+ expect(agents).not.toContain("builder-2");
351
+ });
352
+ });
353
+
354
+ // === --run filter ===
355
+
356
+ describe("--run filter", () => {
357
+ test("filters events by run ID", async () => {
358
+ const dbPath = join(tempDir, ".agentplate", "events.db");
359
+ const store = createEventStore(dbPath);
360
+ store.insert(makeEvent({ runId: "run-001", agentName: "builder-1" }));
361
+ store.insert(makeEvent({ runId: "run-002", agentName: "builder-2" }));
362
+ store.insert(makeEvent({ runId: "run-001", agentName: "scout-1" }));
363
+ store.close();
364
+
365
+ await feedCommand(["--run", "run-001", "--json"]);
366
+ const out = output();
367
+
368
+ const parsed = JSON.parse(out.trim()) as { events: Record<string, unknown>[] };
369
+ expect(parsed.events).toHaveLength(2);
370
+ for (const event of parsed.events) {
371
+ expect(event.runId).toBe("run-001");
372
+ }
373
+ });
374
+ });
375
+
376
+ // === --limit flag ===
377
+
378
+ describe("--limit flag", () => {
379
+ test("limits the number of events returned", async () => {
380
+ const dbPath = join(tempDir, ".agentplate", "events.db");
381
+ const store = createEventStore(dbPath);
382
+ for (let i = 0; i < 100; i++) {
383
+ store.insert(makeEvent({ agentName: "builder-1" }));
384
+ }
385
+ store.close();
386
+
387
+ await feedCommand(["--json", "--limit", "10"]);
388
+ const out = output();
389
+
390
+ const parsed = JSON.parse(out.trim()) as { events: unknown[] };
391
+ expect(parsed.events).toHaveLength(10);
392
+ });
393
+
394
+ test("default limit is 50", async () => {
395
+ const dbPath = join(tempDir, ".agentplate", "events.db");
396
+ const store = createEventStore(dbPath);
397
+ for (let i = 0; i < 100; i++) {
398
+ store.insert(makeEvent({ agentName: "builder-1" }));
399
+ }
400
+ store.close();
401
+
402
+ await feedCommand(["--json"]);
403
+ const out = output();
404
+
405
+ const parsed = JSON.parse(out.trim()) as { events: unknown[] };
406
+ expect(parsed.events).toHaveLength(50);
407
+ });
408
+ });
409
+
410
+ // === --since flag ===
411
+
412
+ describe("--since flag", () => {
413
+ test("--since filters events after a timestamp", async () => {
414
+ const dbPath = join(tempDir, ".agentplate", "events.db");
415
+ const store = createEventStore(dbPath);
416
+ store.insert(makeEvent({ agentName: "builder-1" }));
417
+ store.close();
418
+
419
+ // A future timestamp should return no events
420
+ await feedCommand(["--json", "--since", "2099-01-01T00:00:00Z"]);
421
+ const out = output();
422
+
423
+ const parsed = JSON.parse(out.trim()) as { events: unknown[] };
424
+ expect(parsed.events).toEqual([]);
425
+ });
426
+
427
+ test("--since with past timestamp returns all events", async () => {
428
+ const dbPath = join(tempDir, ".agentplate", "events.db");
429
+ const store = createEventStore(dbPath);
430
+ store.insert(makeEvent({ agentName: "builder-1" }));
431
+ store.insert(makeEvent({ agentName: "builder-2" }));
432
+ store.close();
433
+
434
+ await feedCommand(["--json", "--since", "2020-01-01T00:00:00Z"]);
435
+ const out = output();
436
+
437
+ const parsed = JSON.parse(out.trim()) as { events: unknown[] };
438
+ expect(parsed.events).toHaveLength(2);
439
+ });
440
+
441
+ test("default since is 5 minutes ago", async () => {
442
+ const dbPath = join(tempDir, ".agentplate", "events.db");
443
+ const store = createEventStore(dbPath);
444
+ // Insert event with current timestamp
445
+ store.insert(makeEvent({ agentName: "builder-1" }));
446
+ store.close();
447
+
448
+ // Without --since, should get recent events
449
+ await feedCommand(["--json"]);
450
+ const out = output();
451
+
452
+ const parsed = JSON.parse(out.trim()) as { events: unknown[] };
453
+ expect(parsed.events).toHaveLength(1);
454
+ });
455
+ });
456
+
457
+ // === Event types coverage ===
458
+
459
+ describe("event types coverage", () => {
460
+ test("all event types have compact labels", async () => {
461
+ const dbPath = join(tempDir, ".agentplate", "events.db");
462
+ const store = createEventStore(dbPath);
463
+ const eventTypes = [
464
+ "tool_start",
465
+ "tool_end",
466
+ "session_start",
467
+ "session_end",
468
+ "mail_sent",
469
+ "mail_received",
470
+ "spawn",
471
+ "error",
472
+ "custom",
473
+ "turn_start",
474
+ "turn_end",
475
+ "progress",
476
+ "result",
477
+ ] as const;
478
+ for (const eventType of eventTypes) {
479
+ store.insert(
480
+ makeEvent({
481
+ agentName: "builder-1",
482
+ eventType,
483
+ level: eventType === "error" ? "error" : "info",
484
+ }),
485
+ );
486
+ }
487
+ store.close();
488
+
489
+ await feedCommand([]);
490
+ const out = output();
491
+
492
+ // Verify all compact labels appear
493
+ expect(out).toContain("TOOL+");
494
+ expect(out).toContain("TOOL-");
495
+ expect(out).toContain("SESS+");
496
+ expect(out).toContain("SESS-");
497
+ expect(out).toContain("MAIL>");
498
+ expect(out).toContain("MAIL<");
499
+ expect(out).toContain("SPAWN");
500
+ expect(out).toContain("ERROR");
501
+ expect(out).toContain("CUSTM");
502
+ expect(out).toContain("TURN+");
503
+ expect(out).toContain("TURN-");
504
+ expect(out).toContain("PROG ");
505
+ expect(out).toContain("RSULT");
506
+ });
507
+ });
508
+
509
+ // === Edge cases ===
510
+
511
+ describe("edge cases", () => {
512
+ test("events are ordered chronologically", async () => {
513
+ const dbPath = join(tempDir, ".agentplate", "events.db");
514
+ const store = createEventStore(dbPath);
515
+ store.insert(makeEvent({ agentName: "builder-1", eventType: "session_start" }));
516
+ store.insert(makeEvent({ agentName: "scout-1", eventType: "tool_start" }));
517
+ store.insert(makeEvent({ agentName: "builder-1", eventType: "session_end" }));
518
+ store.close();
519
+
520
+ await feedCommand(["--json"]);
521
+ const out = output();
522
+
523
+ const parsed = JSON.parse(out.trim()) as { events: Record<string, unknown>[] };
524
+ expect(parsed.events).toHaveLength(3);
525
+ expect(parsed.events[0]?.eventType).toBe("session_start");
526
+ expect(parsed.events[1]?.eventType).toBe("tool_start");
527
+ expect(parsed.events[2]?.eventType).toBe("session_end");
528
+ });
529
+
530
+ test("handles event with all null optional fields", async () => {
531
+ const dbPath = join(tempDir, ".agentplate", "events.db");
532
+ const store = createEventStore(dbPath);
533
+ store.insert(
534
+ makeEvent({
535
+ agentName: "builder-1",
536
+ eventType: "session_start",
537
+ runId: null,
538
+ sessionId: null,
539
+ toolName: null,
540
+ toolArgs: null,
541
+ toolDurationMs: null,
542
+ data: null,
543
+ }),
544
+ );
545
+ store.close();
546
+
547
+ // Should not throw
548
+ await feedCommand([]);
549
+ const out = output();
550
+
551
+ expect(out).toContain("SESS+");
552
+ expect(out).toContain("builder-1");
553
+ });
554
+
555
+ test("long data values are truncated in output", async () => {
556
+ const dbPath = join(tempDir, ".agentplate", "events.db");
557
+ const store = createEventStore(dbPath);
558
+ const longValue = "x".repeat(200);
559
+ store.insert(
560
+ makeEvent({
561
+ agentName: "builder-1",
562
+ eventType: "custom",
563
+ toolName: null,
564
+ data: JSON.stringify({ message: longValue }),
565
+ }),
566
+ );
567
+ store.close();
568
+
569
+ await feedCommand([]);
570
+ const out = output();
571
+
572
+ // The full 200-char value should not appear
573
+ expect(out).not.toContain(longValue);
574
+ // But a truncated version with "…" should
575
+ expect(out).toContain("…");
576
+ });
577
+
578
+ test("agent color assignment is stable", async () => {
579
+ const dbPath = join(tempDir, ".agentplate", "events.db");
580
+ const store = createEventStore(dbPath);
581
+ store.insert(makeEvent({ agentName: "builder-1" }));
582
+ store.insert(makeEvent({ agentName: "scout-1" }));
583
+ store.insert(makeEvent({ agentName: "builder-1" }));
584
+ store.close();
585
+
586
+ await feedCommand([]);
587
+ const out = output();
588
+
589
+ // Both builder-1 events should appear
590
+ expect(out).toContain("builder-1");
591
+ // scout-1 should appear
592
+ expect(out).toContain("scout-1");
593
+ });
594
+ });
595
+ });
596
+
597
+ describe("pollFeedTick", () => {
598
+ test("returns same lastSeenId when no new events", () => {
599
+ const queryFn = (): StoredEvent[] => [];
600
+ const colorMap = new Map<string, ColorFn>();
601
+
602
+ const result = pollFeedTick(42, queryFn, colorMap, true);
603
+ expect(result).toBe(42);
604
+ });
605
+
606
+ test("returns max id when new events are found", () => {
607
+ const events: StoredEvent[] = [
608
+ {
609
+ id: 50,
610
+ runId: "run-1",
611
+ agentName: "builder-1",
612
+ sessionId: "s1",
613
+ eventType: "tool_start",
614
+ toolName: "Bash",
615
+ toolArgs: null,
616
+ toolDurationMs: null,
617
+ level: "info",
618
+ data: null,
619
+ createdAt: new Date().toISOString(),
620
+ },
621
+ {
622
+ id: 51,
623
+ runId: "run-1",
624
+ agentName: "builder-1",
625
+ sessionId: "s1",
626
+ eventType: "tool_end",
627
+ toolName: "Bash",
628
+ toolArgs: null,
629
+ toolDurationMs: 100,
630
+ level: "info",
631
+ data: null,
632
+ createdAt: new Date().toISOString(),
633
+ },
634
+ ];
635
+
636
+ const queryFn = (): StoredEvent[] => events;
637
+ const colorMap = new Map<string, ColorFn>();
638
+
639
+ // Capture stdout to avoid test noise
640
+ const origWrite = process.stdout.write;
641
+ const captured: string[] = [];
642
+ process.stdout.write = ((chunk: string) => {
643
+ captured.push(chunk);
644
+ return true;
645
+ }) as typeof process.stdout.write;
646
+
647
+ try {
648
+ const result = pollFeedTick(40, queryFn, colorMap, true);
649
+ expect(result).toBe(51);
650
+ // Should have produced JSON output
651
+ expect(captured.length).toBeGreaterThan(0);
652
+ } finally {
653
+ process.stdout.write = origWrite;
654
+ }
655
+ });
656
+
657
+ test("filters events to those with id > lastSeenId", () => {
658
+ const events: StoredEvent[] = [
659
+ {
660
+ id: 5,
661
+ runId: "run-1",
662
+ agentName: "builder-1",
663
+ sessionId: "s1",
664
+ eventType: "tool_start",
665
+ toolName: "Read",
666
+ toolArgs: null,
667
+ toolDurationMs: null,
668
+ level: "info",
669
+ data: null,
670
+ createdAt: new Date().toISOString(),
671
+ },
672
+ {
673
+ id: 10,
674
+ runId: "run-1",
675
+ agentName: "builder-1",
676
+ sessionId: "s1",
677
+ eventType: "tool_end",
678
+ toolName: "Read",
679
+ toolArgs: null,
680
+ toolDurationMs: 50,
681
+ level: "info",
682
+ data: null,
683
+ createdAt: new Date().toISOString(),
684
+ },
685
+ ];
686
+
687
+ const queryFn = (): StoredEvent[] => events;
688
+ const colorMap = new Map<string, ColorFn>();
689
+
690
+ // With lastSeenId = 5, only event with id=10 should pass
691
+ const origWrite = process.stdout.write;
692
+ const captured: string[] = [];
693
+ process.stdout.write = ((chunk: string) => {
694
+ captured.push(chunk);
695
+ return true;
696
+ }) as typeof process.stdout.write;
697
+
698
+ try {
699
+ const result = pollFeedTick(5, queryFn, colorMap, true);
700
+ expect(result).toBe(10);
701
+ // Only 1 event should be emitted (the one with id > 5)
702
+ // Each JSON event is output on its own line
703
+ const jsonOutputs = captured.filter((c) => c.includes("tool_end"));
704
+ expect(jsonOutputs).toHaveLength(1);
705
+ } finally {
706
+ process.stdout.write = origWrite;
707
+ }
708
+ });
709
+ });