@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,205 @@
1
+ /**
2
+ * CLI command: ap trace <target> [--json] [--since <ts>] [--until <ts>] [--limit <n>]
3
+ *
4
+ * Shows a chronological timeline of events for an agent or task.
5
+ * Target can be an agent name or a task ID (resolved to agent name via SessionStore).
6
+ */
7
+
8
+ import { join } from "node:path";
9
+ import { Command } from "commander";
10
+ import { loadConfig } from "../config.ts";
11
+ import { ValidationError } from "../errors.ts";
12
+ import { createEventStore } from "../events/store.ts";
13
+ import { jsonOutput } from "../json.ts";
14
+ import { accent, color } from "../logging/color.ts";
15
+ import {
16
+ buildEventDetail,
17
+ formatAbsoluteTime,
18
+ formatDate,
19
+ formatRelativeTime,
20
+ } from "../logging/format.ts";
21
+ import { eventLabel, renderHeader } from "../logging/theme.ts";
22
+ import { openSessionStore } from "../sessions/compat.ts";
23
+ import type { StoredEvent } from "../types.ts";
24
+
25
+ /**
26
+ * Detect whether a target string looks like a task ID.
27
+ * Task IDs follow the pattern: word-alphanumeric (e.g., "agentplate-rj1k", "myproject-abc1").
28
+ */
29
+ function looksLikeTaskId(target: string): boolean {
30
+ return /^[a-z][a-z0-9]*-[a-z0-9]{3,}$/i.test(target);
31
+ }
32
+
33
+ /**
34
+ * Print events as a formatted timeline with ANSI colors.
35
+ */
36
+ function printTimeline(events: StoredEvent[], agentName: string, useAbsoluteTime: boolean): void {
37
+ const w = process.stdout.write.bind(process.stdout);
38
+
39
+ w(`${renderHeader(`Timeline for ${accent(agentName)}`)}\n`);
40
+
41
+ if (events.length === 0) {
42
+ w(`${color.dim("No events found.")}\n`);
43
+ return;
44
+ }
45
+
46
+ w(`${color.dim(`${events.length} event${events.length === 1 ? "" : "s"}`)}\n\n`);
47
+
48
+ let lastDate = "";
49
+
50
+ for (const event of events) {
51
+ // Print date separator when the date changes
52
+ const date = formatDate(event.createdAt);
53
+ if (date && date !== lastDate) {
54
+ if (lastDate !== "") {
55
+ w("\n");
56
+ }
57
+ w(`${color.dim(`--- ${date} ---`)}\n`);
58
+ lastDate = date;
59
+ }
60
+
61
+ const timeStr = useAbsoluteTime
62
+ ? formatAbsoluteTime(event.createdAt)
63
+ : formatRelativeTime(event.createdAt);
64
+
65
+ const label = eventLabel(event.eventType);
66
+
67
+ const levelColorFn =
68
+ event.level === "error" ? color.red : event.level === "warn" ? color.yellow : null;
69
+ const applyLevel = (text: string) => (levelColorFn ? levelColorFn(text) : text);
70
+
71
+ const detail = buildEventDetail(event);
72
+ const detailSuffix = detail ? ` ${color.dim(detail)}` : "";
73
+
74
+ const agentLabel = event.agentName !== agentName ? ` ${color.dim(`[${event.agentName}]`)}` : "";
75
+
76
+ w(
77
+ `${color.dim(timeStr.padStart(10))} ` +
78
+ `${applyLevel(label.color(color.bold(label.full)))}` +
79
+ `${agentLabel}${detailSuffix}\n`,
80
+ );
81
+ }
82
+ }
83
+
84
+ interface TraceOpts {
85
+ json?: boolean;
86
+ since?: string;
87
+ until?: string;
88
+ limit?: string;
89
+ }
90
+
91
+ async function executeTrace(target: string, opts: TraceOpts): Promise<void> {
92
+ const json = opts.json ?? false;
93
+ const sinceStr = opts.since;
94
+ const untilStr = opts.until;
95
+ const limitStr = opts.limit;
96
+ const limit = limitStr ? Number.parseInt(limitStr, 10) : 100;
97
+
98
+ if (Number.isNaN(limit) || limit < 1) {
99
+ throw new ValidationError("--limit must be a positive integer", {
100
+ field: "limit",
101
+ value: limitStr,
102
+ });
103
+ }
104
+
105
+ // Validate timestamps if provided
106
+ if (sinceStr !== undefined && Number.isNaN(new Date(sinceStr).getTime())) {
107
+ throw new ValidationError("--since must be a valid ISO 8601 timestamp", {
108
+ field: "since",
109
+ value: sinceStr,
110
+ });
111
+ }
112
+ if (untilStr !== undefined && Number.isNaN(new Date(untilStr).getTime())) {
113
+ throw new ValidationError("--until must be a valid ISO 8601 timestamp", {
114
+ field: "until",
115
+ value: untilStr,
116
+ });
117
+ }
118
+
119
+ const cwd = process.cwd();
120
+ const config = await loadConfig(cwd);
121
+ const agentplateDir = join(config.project.root, ".agentplate");
122
+
123
+ // Resolve target to agent name
124
+ let agentName = target;
125
+
126
+ if (looksLikeTaskId(target)) {
127
+ // Try to resolve task ID to agent name via SessionStore
128
+ const { store: sessionStore } = openSessionStore(agentplateDir);
129
+ try {
130
+ const allSessions = sessionStore.getAll();
131
+ const matchingSession = allSessions.find((s) => s.taskId === target);
132
+ if (matchingSession) {
133
+ agentName = matchingSession.agentName;
134
+ } else {
135
+ // No session found for this task ID; treat it as an agent name anyway
136
+ // (the event query will return empty results if no events match)
137
+ agentName = target;
138
+ }
139
+ } finally {
140
+ sessionStore.close();
141
+ }
142
+ }
143
+
144
+ // Open event store and query events
145
+ const eventsDbPath = join(agentplateDir, "events.db");
146
+ const eventsFile = Bun.file(eventsDbPath);
147
+ if (!(await eventsFile.exists())) {
148
+ if (json) {
149
+ jsonOutput("trace", { events: [] });
150
+ } else {
151
+ process.stdout.write("No events data yet.\n");
152
+ }
153
+ return;
154
+ }
155
+
156
+ const eventStore = createEventStore(eventsDbPath);
157
+
158
+ try {
159
+ const events = eventStore.getByAgent(agentName, {
160
+ since: sinceStr,
161
+ until: untilStr,
162
+ limit,
163
+ });
164
+
165
+ if (json) {
166
+ jsonOutput("trace", { events });
167
+ return;
168
+ }
169
+
170
+ // Use absolute time if --since is specified, relative otherwise
171
+ const useAbsoluteTime = sinceStr !== undefined;
172
+ printTimeline(events, agentName, useAbsoluteTime);
173
+ } finally {
174
+ eventStore.close();
175
+ }
176
+ }
177
+
178
+ export function createTraceCommand(): Command {
179
+ return new Command("trace")
180
+ .description("Chronological event timeline for agent or task")
181
+ .argument("<target>", "Agent name or task ID")
182
+ .option("--json", "Output as JSON array of StoredEvent objects")
183
+ .option("--since <timestamp>", "Start time filter (ISO 8601)")
184
+ .option("--until <timestamp>", "End time filter (ISO 8601)")
185
+ .option("--limit <n>", "Max events to show (default: 100)")
186
+ .action(async (target: string, opts: TraceOpts) => {
187
+ await executeTrace(target, opts);
188
+ });
189
+ }
190
+
191
+ export async function traceCommand(args: string[]): Promise<void> {
192
+ const cmd = createTraceCommand();
193
+ cmd.exitOverride();
194
+ try {
195
+ await cmd.parseAsync(args, { from: "user" });
196
+ } catch (err: unknown) {
197
+ if (err && typeof err === "object" && "code" in err) {
198
+ const code = (err as { code: string }).code;
199
+ if (code === "commander.helpDisplayed" || code === "commander.version") {
200
+ return;
201
+ }
202
+ }
203
+ throw err;
204
+ }
205
+ }
@@ -0,0 +1,466 @@
1
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2
+ import { readdir } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { cleanupTempDir, createTempGitRepo } from "../test-helpers.ts";
5
+ import type { Spawner } from "./init.ts";
6
+ import {
7
+ AGENTPLATE_GITIGNORE,
8
+ AGENTPLATE_README,
9
+ buildAgentManifest,
10
+ buildHooksJson,
11
+ initCommand,
12
+ } from "./init.ts";
13
+ import { executeUpdate } from "./update.ts";
14
+
15
+ /**
16
+ * Tests for `ap update` — refresh .agentplate/ managed files.
17
+ *
18
+ * Uses real temp git repos. Suppresses stdout to keep test output clean.
19
+ * Requires a pre-initialized .agentplate/ directory (via initCommand).
20
+ */
21
+
22
+ /** No-op spawner that treats all ecosystem tools as "not installed". */
23
+ const noopSpawner: Spawner = async () => ({ exitCode: 1, stdout: "", stderr: "not found" });
24
+
25
+ const AGENT_DEF_FILES = [
26
+ "ap-co-creation.md",
27
+ "builder.md",
28
+ "coordinator.md",
29
+ "lead.md",
30
+ "merger.md",
31
+ "monitor.md",
32
+ "orchestrator.md",
33
+ "reviewer.md",
34
+ "scout.md",
35
+ ];
36
+
37
+ /** Resolve the source agents directory (same logic as init.ts). */
38
+ const SOURCE_AGENTS_DIR = join(import.meta.dir, "..", "..", "agents");
39
+
40
+ describe("executeUpdate: not initialized", () => {
41
+ let tempDir: string;
42
+ let originalCwd: string;
43
+ let originalWrite: typeof process.stdout.write;
44
+
45
+ beforeEach(async () => {
46
+ tempDir = await createTempGitRepo();
47
+ originalCwd = process.cwd();
48
+ process.chdir(tempDir);
49
+
50
+ originalWrite = process.stdout.write;
51
+ process.stdout.write = (() => true) as typeof process.stdout.write;
52
+ });
53
+
54
+ afterEach(async () => {
55
+ process.chdir(originalCwd);
56
+ process.stdout.write = originalWrite;
57
+ await cleanupTempDir(tempDir);
58
+ });
59
+
60
+ test("errors when .agentplate/config.yaml does not exist", async () => {
61
+ await expect(executeUpdate({})).rejects.toThrow("Not initialized");
62
+ });
63
+
64
+ test("error message hints to run ap init", async () => {
65
+ try {
66
+ await executeUpdate({});
67
+ expect.unreachable("Should have thrown");
68
+ } catch (err) {
69
+ expect((err as Error).message).toContain("ap init");
70
+ }
71
+ });
72
+ });
73
+
74
+ describe("executeUpdate: refresh all (no flags)", () => {
75
+ let tempDir: string;
76
+ let originalCwd: string;
77
+ let originalWrite: typeof process.stdout.write;
78
+
79
+ beforeEach(async () => {
80
+ tempDir = await createTempGitRepo();
81
+ originalCwd = process.cwd();
82
+ process.chdir(tempDir);
83
+
84
+ originalWrite = process.stdout.write;
85
+ process.stdout.write = (() => true) as typeof process.stdout.write;
86
+
87
+ // Initialize .agentplate/
88
+ await initCommand({ _spawner: noopSpawner });
89
+ });
90
+
91
+ afterEach(async () => {
92
+ process.chdir(originalCwd);
93
+ process.stdout.write = originalWrite;
94
+ await cleanupTempDir(tempDir);
95
+ });
96
+
97
+ test("refreshes all managed files when no flags given", async () => {
98
+ // Tamper with agent defs
99
+ const scoutPath = join(tempDir, ".agentplate", "agent-defs", "scout.md");
100
+ await Bun.write(scoutPath, "# tampered\n");
101
+
102
+ // Tamper with manifest
103
+ await Bun.write(join(tempDir, ".agentplate", "agent-manifest.json"), "{}");
104
+
105
+ // Tamper with hooks
106
+ await Bun.write(join(tempDir, ".agentplate", "hooks.json"), "{}");
107
+
108
+ // Tamper with gitignore
109
+ await Bun.write(join(tempDir, ".agentplate", ".gitignore"), "# old\n");
110
+
111
+ // Tamper with readme
112
+ await Bun.write(join(tempDir, ".agentplate", "README.md"), "# old\n");
113
+
114
+ await executeUpdate({});
115
+
116
+ // Verify all files restored
117
+ const scoutContent = await Bun.file(scoutPath).text();
118
+ const sourceScout = await Bun.file(join(SOURCE_AGENTS_DIR, "scout.md")).text();
119
+ expect(scoutContent).toBe(sourceScout);
120
+
121
+ const manifestContent = await Bun.file(
122
+ join(tempDir, ".agentplate", "agent-manifest.json"),
123
+ ).text();
124
+ const expectedManifest = `${JSON.stringify(buildAgentManifest(), null, "\t")}\n`;
125
+ expect(manifestContent).toBe(expectedManifest);
126
+
127
+ const hooksContent = await Bun.file(join(tempDir, ".agentplate", "hooks.json")).text();
128
+ expect(hooksContent).toBe(buildHooksJson());
129
+
130
+ const gitignoreContent = await Bun.file(join(tempDir, ".agentplate", ".gitignore")).text();
131
+ expect(gitignoreContent).toBe(AGENTPLATE_GITIGNORE);
132
+
133
+ const readmeContent = await Bun.file(join(tempDir, ".agentplate", "README.md")).text();
134
+ expect(readmeContent).toBe(AGENTPLATE_README);
135
+ });
136
+
137
+ test("does not touch config.yaml", async () => {
138
+ const configPath = join(tempDir, ".agentplate", "config.yaml");
139
+ const originalConfig = await Bun.file(configPath).text();
140
+
141
+ await executeUpdate({});
142
+
143
+ const afterUpdate = await Bun.file(configPath).text();
144
+ expect(afterUpdate).toBe(originalConfig);
145
+ });
146
+
147
+ test("does not touch databases", async () => {
148
+ // Create fake database files
149
+ const mailDbPath = join(tempDir, ".agentplate", "mail.db");
150
+ const sessionsDbPath = join(tempDir, ".agentplate", "sessions.db");
151
+ await Bun.write(mailDbPath, "fake-mail-db");
152
+ await Bun.write(sessionsDbPath, "fake-sessions-db");
153
+
154
+ await executeUpdate({});
155
+
156
+ const mailDb = await Bun.file(mailDbPath).text();
157
+ expect(mailDb).toBe("fake-mail-db");
158
+
159
+ const sessionsDb = await Bun.file(sessionsDbPath).text();
160
+ expect(sessionsDb).toBe("fake-sessions-db");
161
+ });
162
+
163
+ test("handles already-up-to-date files gracefully (idempotent)", async () => {
164
+ // Run update twice — second should report nothing changed
165
+ await executeUpdate({});
166
+
167
+ // Capture JSON output of second run
168
+ let captured = "";
169
+ const restoreWrite = process.stdout.write;
170
+ process.stdout.write = ((chunk: unknown) => {
171
+ captured += String(chunk);
172
+ return true;
173
+ }) as typeof process.stdout.write;
174
+
175
+ await executeUpdate({ json: true });
176
+
177
+ process.stdout.write = restoreWrite;
178
+
179
+ const parsed = JSON.parse(captured.trim()) as Record<string, unknown>;
180
+ expect(parsed.success).toBe(true);
181
+
182
+ const agentDefs = parsed.agentDefs as { updated: string[]; unchanged: string[] };
183
+ expect(agentDefs.updated).toHaveLength(0);
184
+ expect(agentDefs.unchanged.length).toBeGreaterThan(0);
185
+
186
+ expect(parsed.manifest).toEqual({ updated: false });
187
+ expect(parsed.hooks).toEqual({ updated: false });
188
+ expect(parsed.gitignore).toEqual({ updated: false });
189
+ expect(parsed.readme).toEqual({ updated: false });
190
+ });
191
+ });
192
+
193
+ describe("executeUpdate: granular flags", () => {
194
+ let tempDir: string;
195
+ let originalCwd: string;
196
+ let originalWrite: typeof process.stdout.write;
197
+
198
+ beforeEach(async () => {
199
+ tempDir = await createTempGitRepo();
200
+ originalCwd = process.cwd();
201
+ process.chdir(tempDir);
202
+
203
+ originalWrite = process.stdout.write;
204
+ process.stdout.write = (() => true) as typeof process.stdout.write;
205
+
206
+ await initCommand({ _spawner: noopSpawner });
207
+ });
208
+
209
+ afterEach(async () => {
210
+ process.chdir(originalCwd);
211
+ process.stdout.write = originalWrite;
212
+ await cleanupTempDir(tempDir);
213
+ });
214
+
215
+ test("--agents only refreshes agent-defs", async () => {
216
+ // Tamper with agent def and manifest
217
+ await Bun.write(join(tempDir, ".agentplate", "agent-defs", "scout.md"), "# tampered\n");
218
+ await Bun.write(join(tempDir, ".agentplate", "agent-manifest.json"), "{}");
219
+ await Bun.write(join(tempDir, ".agentplate", "hooks.json"), "{}");
220
+
221
+ await executeUpdate({ agents: true });
222
+
223
+ // Agent def should be restored
224
+ const scoutContent = await Bun.file(
225
+ join(tempDir, ".agentplate", "agent-defs", "scout.md"),
226
+ ).text();
227
+ const sourceScout = await Bun.file(join(SOURCE_AGENTS_DIR, "scout.md")).text();
228
+ expect(scoutContent).toBe(sourceScout);
229
+
230
+ // Manifest should NOT be restored (--agents only)
231
+ const manifestContent = await Bun.file(
232
+ join(tempDir, ".agentplate", "agent-manifest.json"),
233
+ ).text();
234
+ expect(manifestContent).toBe("{}");
235
+
236
+ // Hooks should NOT be restored (--agents only)
237
+ const hooksContent = await Bun.file(join(tempDir, ".agentplate", "hooks.json")).text();
238
+ expect(hooksContent).toBe("{}");
239
+ });
240
+
241
+ test("--manifest only refreshes agent-manifest.json", async () => {
242
+ await Bun.write(join(tempDir, ".agentplate", "agent-manifest.json"), "{}");
243
+ await Bun.write(join(tempDir, ".agentplate", "agent-defs", "scout.md"), "# tampered\n");
244
+
245
+ await executeUpdate({ manifest: true });
246
+
247
+ // Manifest should be restored
248
+ const manifestContent = await Bun.file(
249
+ join(tempDir, ".agentplate", "agent-manifest.json"),
250
+ ).text();
251
+ const expectedManifest = `${JSON.stringify(buildAgentManifest(), null, "\t")}\n`;
252
+ expect(manifestContent).toBe(expectedManifest);
253
+
254
+ // Agent def should NOT be restored
255
+ const scoutContent = await Bun.file(
256
+ join(tempDir, ".agentplate", "agent-defs", "scout.md"),
257
+ ).text();
258
+ expect(scoutContent).toBe("# tampered\n");
259
+ });
260
+
261
+ test("--hooks only refreshes hooks.json", async () => {
262
+ await Bun.write(join(tempDir, ".agentplate", "hooks.json"), "{}");
263
+ await Bun.write(join(tempDir, ".agentplate", "agent-manifest.json"), "{}");
264
+
265
+ await executeUpdate({ hooks: true });
266
+
267
+ // Hooks should be restored
268
+ const hooksContent = await Bun.file(join(tempDir, ".agentplate", "hooks.json")).text();
269
+ expect(hooksContent).toBe(buildHooksJson());
270
+
271
+ // Manifest should NOT be restored
272
+ const manifestContent = await Bun.file(
273
+ join(tempDir, ".agentplate", "agent-manifest.json"),
274
+ ).text();
275
+ expect(manifestContent).toBe("{}");
276
+ });
277
+
278
+ test("granular flags do not refresh gitignore or readme", async () => {
279
+ await Bun.write(join(tempDir, ".agentplate", ".gitignore"), "# old\n");
280
+ await Bun.write(join(tempDir, ".agentplate", "README.md"), "# old\n");
281
+
282
+ await executeUpdate({ agents: true });
283
+
284
+ const gitignoreContent = await Bun.file(join(tempDir, ".agentplate", ".gitignore")).text();
285
+ expect(gitignoreContent).toBe("# old\n");
286
+
287
+ const readmeContent = await Bun.file(join(tempDir, ".agentplate", "README.md")).text();
288
+ expect(readmeContent).toBe("# old\n");
289
+ });
290
+ });
291
+
292
+ describe("executeUpdate: --dry-run", () => {
293
+ let tempDir: string;
294
+ let originalCwd: string;
295
+ let originalWrite: typeof process.stdout.write;
296
+
297
+ beforeEach(async () => {
298
+ tempDir = await createTempGitRepo();
299
+ originalCwd = process.cwd();
300
+ process.chdir(tempDir);
301
+
302
+ originalWrite = process.stdout.write;
303
+ process.stdout.write = (() => true) as typeof process.stdout.write;
304
+
305
+ await initCommand({ _spawner: noopSpawner });
306
+ });
307
+
308
+ afterEach(async () => {
309
+ process.chdir(originalCwd);
310
+ process.stdout.write = originalWrite;
311
+ await cleanupTempDir(tempDir);
312
+ });
313
+
314
+ test("reports changes without writing files", async () => {
315
+ // Tamper with files
316
+ await Bun.write(join(tempDir, ".agentplate", "agent-defs", "scout.md"), "# tampered\n");
317
+ await Bun.write(join(tempDir, ".agentplate", "agent-manifest.json"), "{}");
318
+
319
+ let captured = "";
320
+ const restoreWrite = process.stdout.write;
321
+ process.stdout.write = ((chunk: unknown) => {
322
+ captured += String(chunk);
323
+ return true;
324
+ }) as typeof process.stdout.write;
325
+
326
+ await executeUpdate({ dryRun: true, json: true });
327
+
328
+ process.stdout.write = restoreWrite;
329
+
330
+ const parsed = JSON.parse(captured.trim()) as Record<string, unknown>;
331
+ expect(parsed.success).toBe(true);
332
+ expect(parsed.dryRun).toBe(true);
333
+
334
+ const agentDefs = parsed.agentDefs as { updated: string[]; unchanged: string[] };
335
+ expect(agentDefs.updated).toContain("scout.md");
336
+
337
+ expect(parsed.manifest).toEqual({ updated: true });
338
+
339
+ // Verify files were NOT actually modified
340
+ const scoutContent = await Bun.file(
341
+ join(tempDir, ".agentplate", "agent-defs", "scout.md"),
342
+ ).text();
343
+ expect(scoutContent).toBe("# tampered\n");
344
+
345
+ const manifestContent = await Bun.file(
346
+ join(tempDir, ".agentplate", "agent-manifest.json"),
347
+ ).text();
348
+ expect(manifestContent).toBe("{}");
349
+ });
350
+ });
351
+
352
+ describe("executeUpdate: --json output", () => {
353
+ let tempDir: string;
354
+ let originalCwd: string;
355
+ let originalWrite: typeof process.stdout.write;
356
+
357
+ beforeEach(async () => {
358
+ tempDir = await createTempGitRepo();
359
+ originalCwd = process.cwd();
360
+ process.chdir(tempDir);
361
+
362
+ originalWrite = process.stdout.write;
363
+ process.stdout.write = (() => true) as typeof process.stdout.write;
364
+
365
+ await initCommand({ _spawner: noopSpawner });
366
+ });
367
+
368
+ afterEach(async () => {
369
+ process.chdir(originalCwd);
370
+ process.stdout.write = originalWrite;
371
+ await cleanupTempDir(tempDir);
372
+ });
373
+
374
+ test("outputs correct JSON envelope", async () => {
375
+ let captured = "";
376
+ const restoreWrite = process.stdout.write;
377
+ process.stdout.write = ((chunk: unknown) => {
378
+ captured += String(chunk);
379
+ return true;
380
+ }) as typeof process.stdout.write;
381
+
382
+ await executeUpdate({ json: true });
383
+
384
+ process.stdout.write = restoreWrite;
385
+
386
+ const parsed = JSON.parse(captured.trim()) as Record<string, unknown>;
387
+ expect(parsed.success).toBe(true);
388
+ expect(parsed.command).toBe("update");
389
+ expect(parsed.dryRun).toBe(false);
390
+ expect(parsed.agentDefs).toBeDefined();
391
+ expect(parsed.manifest).toBeDefined();
392
+ expect(parsed.hooks).toBeDefined();
393
+ expect(parsed.gitignore).toBeDefined();
394
+ expect(parsed.readme).toBeDefined();
395
+ });
396
+
397
+ test("JSON envelope includes updated file lists", async () => {
398
+ // Tamper with scout
399
+ await Bun.write(join(tempDir, ".agentplate", "agent-defs", "scout.md"), "# tampered\n");
400
+
401
+ let captured = "";
402
+ const restoreWrite = process.stdout.write;
403
+ process.stdout.write = ((chunk: unknown) => {
404
+ captured += String(chunk);
405
+ return true;
406
+ }) as typeof process.stdout.write;
407
+
408
+ await executeUpdate({ json: true });
409
+
410
+ process.stdout.write = restoreWrite;
411
+
412
+ const parsed = JSON.parse(captured.trim()) as Record<string, unknown>;
413
+ const agentDefs = parsed.agentDefs as { updated: string[]; unchanged: string[] };
414
+ expect(agentDefs.updated).toContain("scout.md");
415
+ expect(agentDefs.unchanged.length).toBeGreaterThan(0);
416
+ });
417
+ });
418
+
419
+ describe("executeUpdate: agent def exclusions", () => {
420
+ let tempDir: string;
421
+ let originalCwd: string;
422
+ let originalWrite: typeof process.stdout.write;
423
+
424
+ beforeEach(async () => {
425
+ tempDir = await createTempGitRepo();
426
+ originalCwd = process.cwd();
427
+ process.chdir(tempDir);
428
+
429
+ originalWrite = process.stdout.write;
430
+ process.stdout.write = (() => true) as typeof process.stdout.write;
431
+
432
+ await initCommand({ _spawner: noopSpawner });
433
+ });
434
+
435
+ afterEach(async () => {
436
+ process.chdir(originalCwd);
437
+ process.stdout.write = originalWrite;
438
+ await cleanupTempDir(tempDir);
439
+ });
440
+
441
+ test("does not deploy supervisor.md (deprecated)", async () => {
442
+ await executeUpdate({ agents: true });
443
+
444
+ const agentDefsDir = join(tempDir, ".agentplate", "agent-defs");
445
+ const files = await readdir(agentDefsDir);
446
+ expect(files).not.toContain("supervisor.md");
447
+ });
448
+
449
+ test("deploys all non-deprecated agent defs", async () => {
450
+ // Delete all agent defs first
451
+ for (const f of AGENT_DEF_FILES) {
452
+ try {
453
+ const { unlink } = await import("node:fs/promises");
454
+ await unlink(join(tempDir, ".agentplate", "agent-defs", f));
455
+ } catch {
456
+ // May not exist
457
+ }
458
+ }
459
+
460
+ await executeUpdate({ agents: true });
461
+
462
+ const agentDefsDir = join(tempDir, ".agentplate", "agent-defs");
463
+ const files = (await readdir(agentDefsDir)).filter((f) => f.endsWith(".md")).sort();
464
+ expect(files).toEqual(AGENT_DEF_FILES);
465
+ });
466
+ });