@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,191 @@
1
+ /**
2
+ * Tests for src/commands/monitor.ts
3
+ *
4
+ * Note: We do NOT test start/stop/status subcommands here because they require
5
+ * tmux session management, which is fragile in test environments and interferes
6
+ * with developer tmux sessions. Those operations are covered by E2E testing.
7
+ */
8
+
9
+ import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test";
10
+ import { mkdir } from "node:fs/promises";
11
+ import { join } from "node:path";
12
+ import { AgentError, ValidationError } from "../errors.ts";
13
+ import { cleanupTempDir, createTempGitRepo } from "../test-helpers.ts";
14
+ import { buildMonitorBeacon, monitorCommand } from "./monitor.ts";
15
+ import { isRunningAsRoot } from "./sling.ts";
16
+
17
+ describe("buildMonitorBeacon", () => {
18
+ test("contains monitor agent name", () => {
19
+ const beacon = buildMonitorBeacon();
20
+ expect(beacon).toContain("monitor");
21
+ });
22
+
23
+ test("contains tier-2 designation", () => {
24
+ const beacon = buildMonitorBeacon();
25
+ expect(beacon).toContain("tier-2");
26
+ });
27
+
28
+ test("contains ISO timestamp with today's date", () => {
29
+ const beacon = buildMonitorBeacon();
30
+ const today = new Date().toISOString().split("T")[0]; // YYYY-MM-DD
31
+ if (!today) {
32
+ throw new Error("Failed to extract date from ISO string");
33
+ }
34
+ expect(beacon).toContain(today);
35
+ });
36
+
37
+ test("contains startup instruction: loam prime", () => {
38
+ const beacon = buildMonitorBeacon();
39
+ expect(beacon).toContain("loam prime");
40
+ });
41
+
42
+ test("contains startup instruction: ap status --json", () => {
43
+ const beacon = buildMonitorBeacon();
44
+ expect(beacon).toContain("ap status --json");
45
+ });
46
+
47
+ test("contains startup instruction: ap mail check --agent monitor", () => {
48
+ const beacon = buildMonitorBeacon();
49
+ expect(beacon).toContain("ap mail check --agent monitor");
50
+ });
51
+
52
+ test("contains startup instruction: patrol loop", () => {
53
+ const beacon = buildMonitorBeacon();
54
+ expect(beacon).toContain("patrol loop");
55
+ });
56
+
57
+ test("contains [AGENTPLATE] prefix", () => {
58
+ const beacon = buildMonitorBeacon();
59
+ expect(beacon).toContain("[AGENTPLATE]");
60
+ });
61
+
62
+ test("contains Depth: 0", () => {
63
+ const beacon = buildMonitorBeacon();
64
+ expect(beacon).toContain("Depth: 0");
65
+ });
66
+
67
+ test("contains Parent: none", () => {
68
+ const beacon = buildMonitorBeacon();
69
+ expect(beacon).toContain("Parent: none");
70
+ });
71
+ });
72
+
73
+ describe("monitorCommand", () => {
74
+ let stdoutSpy: ReturnType<typeof spyOn>;
75
+ let stdoutWrites: string[] = [];
76
+
77
+ beforeEach(() => {
78
+ stdoutWrites = [];
79
+ stdoutSpy = spyOn(process.stdout, "write").mockImplementation((chunk: unknown) => {
80
+ stdoutWrites.push(String(chunk));
81
+ return true;
82
+ });
83
+ });
84
+
85
+ afterEach(() => {
86
+ stdoutSpy.mockRestore();
87
+ });
88
+
89
+ test("--help prints help text containing 'monitor'", async () => {
90
+ await monitorCommand(["--help"]);
91
+ const output = stdoutWrites.join("");
92
+ expect(output).toContain("monitor");
93
+ });
94
+
95
+ test("--help prints help text containing 'start'", async () => {
96
+ await monitorCommand(["--help"]);
97
+ const output = stdoutWrites.join("");
98
+ expect(output).toContain("start");
99
+ });
100
+
101
+ test("--help prints help text containing 'stop'", async () => {
102
+ await monitorCommand(["--help"]);
103
+ const output = stdoutWrites.join("");
104
+ expect(output).toContain("stop");
105
+ });
106
+
107
+ test("--help prints help text containing 'status'", async () => {
108
+ await monitorCommand(["--help"]);
109
+ const output = stdoutWrites.join("");
110
+ expect(output).toContain("status");
111
+ });
112
+
113
+ test("-h prints help text", async () => {
114
+ await monitorCommand(["-h"]);
115
+ const output = stdoutWrites.join("");
116
+ expect(output).toContain("monitor");
117
+ });
118
+
119
+ test("empty args [] shows help (same as --help)", async () => {
120
+ await monitorCommand([]);
121
+ const output = stdoutWrites.join("");
122
+ expect(output).toContain("monitor");
123
+ });
124
+
125
+ test("unknown subcommand 'restart' throws ValidationError", async () => {
126
+ await expect(monitorCommand(["restart"])).rejects.toThrow(ValidationError);
127
+ });
128
+
129
+ test("unknown subcommand error message contains the bad value 'restart'", async () => {
130
+ try {
131
+ await monitorCommand(["restart"]);
132
+ // Should not reach here
133
+ expect(true).toBe(false);
134
+ } catch (err) {
135
+ if (err instanceof ValidationError) {
136
+ expect(err.message).toContain("restart");
137
+ } else {
138
+ throw err;
139
+ }
140
+ }
141
+ });
142
+
143
+ describe("tier2Enabled gate", () => {
144
+ let tempDir: string;
145
+ const originalCwd = process.cwd();
146
+
147
+ beforeEach(async () => {
148
+ process.chdir(originalCwd);
149
+ tempDir = await createTempGitRepo();
150
+ const agentplateDir = join(tempDir, ".agentplate");
151
+ await mkdir(agentplateDir, { recursive: true });
152
+ // Write minimal config — tier2Enabled defaults to false
153
+ await Bun.write(
154
+ join(agentplateDir, "config.yaml"),
155
+ ["project:", " name: test-project", ` root: ${tempDir}`, " canonicalBranch: main"].join(
156
+ "\n",
157
+ ),
158
+ );
159
+ process.chdir(tempDir);
160
+ });
161
+
162
+ afterEach(async () => {
163
+ process.chdir(originalCwd);
164
+ await cleanupTempDir(tempDir);
165
+ });
166
+
167
+ test("monitor start throws AgentError when tier2Enabled is false (default)", async () => {
168
+ await expect(monitorCommand(["start"])).rejects.toThrow(AgentError);
169
+ });
170
+
171
+ test("monitor start error message contains 'disabled' when tier2Enabled is false", async () => {
172
+ try {
173
+ await monitorCommand(["start"]);
174
+ expect(true).toBe(false); // Should not reach here
175
+ } catch (err) {
176
+ if (err instanceof AgentError) {
177
+ expect(err.message).toContain("disabled");
178
+ } else {
179
+ throw err;
180
+ }
181
+ }
182
+ });
183
+ });
184
+ });
185
+
186
+ describe("isRunningAsRoot (imported from sling)", () => {
187
+ test("is accessible from monitor test file", () => {
188
+ expect(isRunningAsRoot(() => 0)).toBe(true);
189
+ expect(isRunningAsRoot(() => 1000)).toBe(false);
190
+ });
191
+ });
@@ -0,0 +1,409 @@
1
+ /**
2
+ * CLI command: ap monitor start|stop|status
3
+ *
4
+ * Manages the persistent Tier 2 monitor agent lifecycle. The monitor runs
5
+ * at the project root (NOT in a worktree), continuously patrols the agent
6
+ * fleet, sends nudges to stalled agents, and reports health summaries to
7
+ * the coordinator.
8
+ *
9
+ * Unlike regular agents spawned by sling, the monitor:
10
+ * - Has no worktree (operates on the main working tree)
11
+ * - Has no task assignment (it monitors, not implements)
12
+ * - Has no overlay CLAUDE.md (context comes via ap status + mail)
13
+ * - Persists across patrol cycles
14
+ */
15
+
16
+ import { mkdir } from "node:fs/promises";
17
+ import { join } from "node:path";
18
+ import { Command } from "commander";
19
+ import { createIdentity, loadIdentity } from "../agents/identity.ts";
20
+ import { createManifestLoader, resolveModel } from "../agents/manifest.ts";
21
+ import { loadConfig } from "../config.ts";
22
+ import { AgentError, ValidationError } from "../errors.ts";
23
+ import { jsonOutput } from "../json.ts";
24
+ import { printHint, printSuccess } from "../logging/color.ts";
25
+ import { getRuntime } from "../runtimes/registry.ts";
26
+ import { openSessionStore } from "../sessions/compat.ts";
27
+ import type { AgentSession } from "../types.ts";
28
+ import {
29
+ createSession,
30
+ isSessionAlive,
31
+ killSession,
32
+ sanitizeTmuxName,
33
+ sendKeys,
34
+ TMUX_SOCKET,
35
+ } from "../worktree/tmux.ts";
36
+ import { isRunningAsRoot } from "./sling.ts";
37
+
38
+ /** Default monitor agent name. */
39
+ const MONITOR_NAME = "monitor";
40
+
41
+ /**
42
+ * Build the tmux session name for the monitor.
43
+ * Includes the project name to prevent cross-project collisions (agentplate-pcef).
44
+ */
45
+ function monitorTmuxSession(projectName: string): string {
46
+ return `agentplate-${sanitizeTmuxName(projectName)}-${MONITOR_NAME}`;
47
+ }
48
+
49
+ /**
50
+ * Build the monitor startup beacon — the first message sent to the monitor
51
+ * via tmux send-keys after Claude Code initializes.
52
+ */
53
+ export function buildMonitorBeacon(): string {
54
+ const timestamp = new Date().toISOString();
55
+ const parts = [
56
+ `[AGENTPLATE] ${MONITOR_NAME} (monitor/tier-2) ${timestamp}`,
57
+ "Depth: 0 | Parent: none | Role: continuous fleet patrol",
58
+ `Startup: run loam prime, check fleet (ap status --json), check mail (ap mail check --agent ${MONITOR_NAME}), then begin patrol loop`,
59
+ ];
60
+ return parts.join(" — ");
61
+ }
62
+
63
+ /**
64
+ * Start the monitor agent.
65
+ *
66
+ * 1. Verify no monitor is already running
67
+ * 2. Load config
68
+ * 3. Deploy hooks to project root's .claude/ (monitor-specific guards)
69
+ * 4. Create agent identity (if first time)
70
+ * 5. Spawn tmux session at project root with Claude Code
71
+ * 6. Send startup beacon
72
+ * 7. Record session in SessionStore (sessions.db)
73
+ */
74
+ async function startMonitor(opts: { json: boolean; attach: boolean }): Promise<void> {
75
+ const { json, attach: shouldAttach } = opts;
76
+
77
+ if (isRunningAsRoot()) {
78
+ throw new AgentError(
79
+ "Cannot spawn agents as root (UID 0). The claude CLI rejects --permission-mode bypassPermissions when run as root, causing the tmux session to die immediately. Run agentplate as a non-root user.",
80
+ );
81
+ }
82
+
83
+ const cwd = process.cwd();
84
+ const config = await loadConfig(cwd);
85
+
86
+ // Gate on tier2Enabled config flag
87
+ if (!config.watchdog.tier2Enabled) {
88
+ throw new AgentError(
89
+ "Monitor agent (Tier 2) is disabled. Set watchdog.tier2Enabled: true in .agentplate/config.yaml to enable.",
90
+ { agentName: MONITOR_NAME },
91
+ );
92
+ }
93
+
94
+ const projectRoot = config.project.root;
95
+ const tmuxSession = monitorTmuxSession(config.project.name);
96
+
97
+ // Check for existing monitor
98
+ const agentplateDir = join(projectRoot, ".agentplate");
99
+ const { store } = openSessionStore(agentplateDir);
100
+ try {
101
+ const existing = store.getByName(MONITOR_NAME);
102
+
103
+ if (
104
+ existing &&
105
+ existing.capability === "monitor" &&
106
+ existing.state !== "completed" &&
107
+ existing.state !== "zombie"
108
+ ) {
109
+ const alive = await isSessionAlive(existing.tmuxSession);
110
+ if (alive) {
111
+ throw new AgentError(
112
+ `Monitor is already running (tmux: ${existing.tmuxSession}, since: ${existing.startedAt})`,
113
+ { agentName: MONITOR_NAME },
114
+ );
115
+ }
116
+ // Session recorded but tmux is dead — mark as completed and continue
117
+ store.updateState(MONITOR_NAME, "completed");
118
+ }
119
+
120
+ // Resolve model and runtime early (needed for deployConfig and spawn)
121
+ const manifestLoader = createManifestLoader(
122
+ join(projectRoot, config.agents.manifestPath),
123
+ join(projectRoot, config.agents.baseDir),
124
+ );
125
+ const manifest = await manifestLoader.load();
126
+ const resolvedModel = resolveModel(config, manifest, "monitor", "sonnet");
127
+ const runtime = getRuntime(undefined, config, "monitor");
128
+
129
+ // Deploy monitor-specific hooks to the project root's .claude/ directory.
130
+ await runtime.deployConfig(projectRoot, undefined, {
131
+ agentName: MONITOR_NAME,
132
+ capability: "monitor",
133
+ worktreePath: projectRoot,
134
+ });
135
+
136
+ // Create monitor identity if first run
137
+ const identityBaseDir = join(projectRoot, ".agentplate", "agents");
138
+ await mkdir(identityBaseDir, { recursive: true });
139
+ const existingIdentity = await loadIdentity(identityBaseDir, MONITOR_NAME);
140
+ if (!existingIdentity) {
141
+ await createIdentity(identityBaseDir, {
142
+ name: MONITOR_NAME,
143
+ capability: "monitor",
144
+ created: new Date().toISOString(),
145
+ sessionsCompleted: 0,
146
+ expertiseDomains: config.loam.enabled ? config.loam.domains : [],
147
+ recentTasks: [],
148
+ });
149
+ }
150
+
151
+ // Spawn tmux session at project root with Claude Code (interactive mode).
152
+ // Pass file path (not content) to avoid tmux "command too long" (agentplate#45).
153
+ const agentDefPath = join(projectRoot, ".agentplate", "agent-defs", "monitor.md");
154
+ const agentDefFile = Bun.file(agentDefPath);
155
+ let appendSystemPromptFile: string | undefined;
156
+ if (await agentDefFile.exists()) {
157
+ appendSystemPromptFile = agentDefPath;
158
+ }
159
+ const spawnCmd = runtime.buildSpawnCommand({
160
+ model: resolvedModel.model,
161
+ permissionMode: "bypass",
162
+ cwd: projectRoot,
163
+ appendSystemPromptFile,
164
+ env: {
165
+ ...runtime.buildEnv(resolvedModel),
166
+ AGENTPLATE_AGENT_NAME: MONITOR_NAME,
167
+ },
168
+ });
169
+ const pid = await createSession(tmuxSession, projectRoot, spawnCmd, {
170
+ ...runtime.buildEnv(resolvedModel),
171
+ AGENTPLATE_AGENT_NAME: MONITOR_NAME,
172
+ });
173
+
174
+ // Record session BEFORE sending the beacon so that hook-triggered
175
+ // updateLastActivity() can find the entry and transition booting->working.
176
+ const session: AgentSession = {
177
+ id: `session-${Date.now()}-${MONITOR_NAME}`,
178
+ agentName: MONITOR_NAME,
179
+ capability: "monitor",
180
+ worktreePath: projectRoot, // Monitor uses project root, not a worktree
181
+ branchName: config.project.canonicalBranch, // Operates on canonical branch
182
+ taskId: "", // No specific task assignment
183
+ tmuxSession,
184
+ state: "booting",
185
+ pid,
186
+ parentAgent: null, // Top of hierarchy (alongside coordinator)
187
+ depth: 0,
188
+ runId: null,
189
+ startedAt: new Date().toISOString(),
190
+ lastActivity: new Date().toISOString(),
191
+ escalationLevel: 0,
192
+ stalledSince: null,
193
+ transcriptPath: null,
194
+ };
195
+
196
+ store.upsert(session);
197
+
198
+ // Send beacon after TUI initialization delay
199
+ await Bun.sleep(3_000);
200
+ const beacon = buildMonitorBeacon();
201
+ await sendKeys(tmuxSession, beacon);
202
+
203
+ // Follow-up Enter to ensure submission (same pattern as sling.ts)
204
+ await Bun.sleep(500);
205
+ await sendKeys(tmuxSession, "");
206
+
207
+ const output = {
208
+ agentName: MONITOR_NAME,
209
+ capability: "monitor",
210
+ tmuxSession,
211
+ projectRoot,
212
+ pid,
213
+ };
214
+
215
+ if (json) {
216
+ jsonOutput("monitor start", output);
217
+ } else {
218
+ printSuccess("Monitor started");
219
+ process.stdout.write(` Tmux: ${tmuxSession}\n`);
220
+ process.stdout.write(` Root: ${projectRoot}\n`);
221
+ process.stdout.write(` PID: ${pid}\n`);
222
+ }
223
+
224
+ if (shouldAttach) {
225
+ Bun.spawnSync(["tmux", "-L", TMUX_SOCKET, "attach-session", "-t", tmuxSession], {
226
+ stdio: ["inherit", "inherit", "inherit"],
227
+ });
228
+ }
229
+ } finally {
230
+ store.close();
231
+ }
232
+ }
233
+
234
+ /**
235
+ * Stop the monitor agent.
236
+ *
237
+ * 1. Find the active monitor session
238
+ * 2. Kill the tmux session (with process tree cleanup)
239
+ * 3. Mark session as completed in SessionStore
240
+ */
241
+ async function stopMonitor(opts: { json: boolean }): Promise<void> {
242
+ const { json } = opts;
243
+ const cwd = process.cwd();
244
+ const config = await loadConfig(cwd);
245
+ const projectRoot = config.project.root;
246
+
247
+ const agentplateDir = join(projectRoot, ".agentplate");
248
+ const { store } = openSessionStore(agentplateDir);
249
+ try {
250
+ const session = store.getByName(MONITOR_NAME);
251
+
252
+ if (
253
+ !session ||
254
+ session.capability !== "monitor" ||
255
+ session.state === "completed" ||
256
+ session.state === "zombie"
257
+ ) {
258
+ throw new AgentError("No active monitor session found", {
259
+ agentName: MONITOR_NAME,
260
+ });
261
+ }
262
+
263
+ // Kill tmux session with process tree cleanup
264
+ const alive = await isSessionAlive(session.tmuxSession);
265
+ if (alive) {
266
+ await killSession(session.tmuxSession);
267
+ }
268
+
269
+ // Update session state
270
+ store.updateState(MONITOR_NAME, "completed");
271
+ store.updateLastActivity(MONITOR_NAME);
272
+
273
+ if (json) {
274
+ jsonOutput("monitor stop", { stopped: true, sessionId: session.id });
275
+ } else {
276
+ printSuccess("Monitor stopped", session.id);
277
+ }
278
+ } finally {
279
+ store.close();
280
+ }
281
+ }
282
+
283
+ /**
284
+ * Show monitor status.
285
+ *
286
+ * Checks session registry and tmux liveness to report actual state.
287
+ */
288
+ async function statusMonitor(opts: { json: boolean }): Promise<void> {
289
+ const { json } = opts;
290
+ const cwd = process.cwd();
291
+ const config = await loadConfig(cwd);
292
+ const projectRoot = config.project.root;
293
+
294
+ const agentplateDir = join(projectRoot, ".agentplate");
295
+ const { store } = openSessionStore(agentplateDir);
296
+ try {
297
+ const session = store.getByName(MONITOR_NAME);
298
+
299
+ if (
300
+ !session ||
301
+ session.capability !== "monitor" ||
302
+ session.state === "completed" ||
303
+ session.state === "zombie"
304
+ ) {
305
+ if (json) {
306
+ jsonOutput("monitor status", { running: false });
307
+ } else {
308
+ printHint("Monitor is not running");
309
+ }
310
+ return;
311
+ }
312
+
313
+ const alive = await isSessionAlive(session.tmuxSession);
314
+
315
+ // Reconcile state: if session says active but tmux is dead, update.
316
+ if (!alive) {
317
+ store.updateState(MONITOR_NAME, "zombie");
318
+ store.updateLastActivity(MONITOR_NAME);
319
+ session.state = "zombie";
320
+ }
321
+
322
+ const status = {
323
+ running: alive,
324
+ sessionId: session.id,
325
+ state: session.state,
326
+ tmuxSession: session.tmuxSession,
327
+ pid: session.pid,
328
+ startedAt: session.startedAt,
329
+ lastActivity: session.lastActivity,
330
+ };
331
+
332
+ if (json) {
333
+ jsonOutput("monitor status", status);
334
+ } else {
335
+ const stateLabel = alive ? "running" : session.state;
336
+ process.stdout.write(`Monitor: ${stateLabel}\n`);
337
+ process.stdout.write(` Session: ${session.id}\n`);
338
+ process.stdout.write(` Tmux: ${session.tmuxSession}\n`);
339
+ process.stdout.write(` PID: ${session.pid}\n`);
340
+ process.stdout.write(` Started: ${session.startedAt}\n`);
341
+ process.stdout.write(` Activity: ${session.lastActivity}\n`);
342
+ }
343
+ } finally {
344
+ store.close();
345
+ }
346
+ }
347
+
348
+ export function createMonitorCommand(): Command {
349
+ const cmd = new Command("monitor").description("Manage the persistent Tier 2 monitor agent");
350
+
351
+ cmd
352
+ .command("start")
353
+ .description("Start the monitor (spawns Claude Code at project root)")
354
+ .option("--attach", "Always attach to tmux session after start")
355
+ .option("--no-attach", "Never attach to tmux session after start")
356
+ .option("--json", "Output as JSON")
357
+ .action(async (opts: { attach?: boolean; json?: boolean }) => {
358
+ // opts.attach = true if --attach, false if --no-attach, undefined if neither
359
+ const shouldAttach = opts.attach !== undefined ? opts.attach : !!process.stdout.isTTY;
360
+ await startMonitor({ json: opts.json ?? false, attach: shouldAttach });
361
+ });
362
+
363
+ cmd
364
+ .command("stop")
365
+ .description("Stop the monitor (kills tmux session)")
366
+ .option("--json", "Output as JSON")
367
+ .action(async (opts: { json?: boolean }) => {
368
+ await stopMonitor({ json: opts.json ?? false });
369
+ });
370
+
371
+ cmd
372
+ .command("status")
373
+ .description("Show monitor state")
374
+ .option("--json", "Output as JSON")
375
+ .action(async (opts: { json?: boolean }) => {
376
+ await statusMonitor({ json: opts.json ?? false });
377
+ });
378
+
379
+ return cmd;
380
+ }
381
+
382
+ /**
383
+ * Entry point for `ap monitor <subcommand>`.
384
+ */
385
+ export async function monitorCommand(args: string[]): Promise<void> {
386
+ const cmd = createMonitorCommand();
387
+ cmd.exitOverride();
388
+
389
+ if (args.length === 0) {
390
+ process.stdout.write(cmd.helpInformation());
391
+ return;
392
+ }
393
+
394
+ try {
395
+ await cmd.parseAsync(args, { from: "user" });
396
+ } catch (err: unknown) {
397
+ if (err && typeof err === "object" && "code" in err) {
398
+ const code = (err as { code: string }).code;
399
+ if (code === "commander.helpDisplayed" || code === "commander.version") {
400
+ return;
401
+ }
402
+ if (code === "commander.unknownCommand") {
403
+ const message = err instanceof Error ? err.message : String(err);
404
+ throw new ValidationError(message, { field: "subcommand" });
405
+ }
406
+ }
407
+ throw err;
408
+ }
409
+ }