@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,257 @@
1
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2
+ import { mkdtemp } from "node:fs/promises";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { cleanupTempDir } from "../test-helpers.ts";
6
+ import type { HealthCheck } from "../types.ts";
7
+ import { formatCheck, watchCommand } from "./watch.ts";
8
+
9
+ /**
10
+ * Tests for `agentplate watch` command.
11
+ *
12
+ * IMPORTANT: We CANNOT test the actual daemon loop (it would hang the test).
13
+ * Focus on:
14
+ * - Help output (safe, returns immediately)
15
+ * - Background mode: already-running detection
16
+ * - Background mode: stale PID cleanup
17
+ *
18
+ * We do NOT test:
19
+ * - Foreground mode (blocks forever with await new Promise(() => {}))
20
+ * - Actual health check loop behavior
21
+ */
22
+
23
+ describe("watchCommand", () => {
24
+ let chunks: string[];
25
+ let stderrChunks: string[];
26
+ let originalWrite: typeof process.stdout.write;
27
+ let originalStderrWrite: typeof process.stderr.write;
28
+ let tempDir: string;
29
+ let originalCwd: string;
30
+
31
+ beforeEach(async () => {
32
+ // Spy on stdout
33
+ chunks = [];
34
+ originalWrite = process.stdout.write;
35
+ process.stdout.write = ((chunk: string) => {
36
+ chunks.push(chunk);
37
+ return true;
38
+ }) as typeof process.stdout.write;
39
+
40
+ // Spy on stderr
41
+ stderrChunks = [];
42
+ originalStderrWrite = process.stderr.write;
43
+ process.stderr.write = ((chunk: string) => {
44
+ stderrChunks.push(chunk);
45
+ return true;
46
+ }) as typeof process.stderr.write;
47
+
48
+ process.exitCode = 0;
49
+
50
+ // Create temp dir with .agentplate/config.yaml structure
51
+ tempDir = await mkdtemp(join(tmpdir(), "watch-test-"));
52
+ const agentplateDir = join(tempDir, ".agentplate");
53
+ await Bun.write(
54
+ join(agentplateDir, "config.yaml"),
55
+ `project:\n name: test\n root: ${tempDir}\n canonicalBranch: main\n`,
56
+ );
57
+
58
+ // Change to temp dir so loadConfig() works
59
+ originalCwd = process.cwd();
60
+ process.chdir(tempDir);
61
+ });
62
+
63
+ afterEach(async () => {
64
+ process.stdout.write = originalWrite;
65
+ process.stderr.write = originalStderrWrite;
66
+ // Unconditionally clear to 0 rather than restoring a captured "original" that
67
+ // could itself have been polluted by a parallel test file in the same bun
68
+ // process. `watchCommand` sets `process.exitCode = 1` as a side effect, so
69
+ // without this clear the 1 can leak all the way to bun test's shutdown and
70
+ // turn a fully-green run into exit 1.
71
+ process.exitCode = 0;
72
+ process.chdir(originalCwd);
73
+ await cleanupTempDir(tempDir);
74
+ });
75
+
76
+ function output(): string {
77
+ return chunks.join("");
78
+ }
79
+
80
+ function stderr(): string {
81
+ return stderrChunks.join("");
82
+ }
83
+
84
+ test("--help flag shows help text with key info", async () => {
85
+ await watchCommand(["--help"]);
86
+ const out = output();
87
+
88
+ expect(out).toContain("watch");
89
+ expect(out).toContain("--interval");
90
+ expect(out).toContain("--background");
91
+ expect(out).toContain("--kill-others");
92
+ expect(out).toContain("Tier 0");
93
+ });
94
+
95
+ test("-h flag shows help text", async () => {
96
+ await watchCommand(["-h"]);
97
+ const out = output();
98
+
99
+ expect(out).toContain("watch");
100
+ expect(out).toContain("Tier 0");
101
+ });
102
+
103
+ test("background mode: already running detection", async () => {
104
+ // Write a PID file with a running process (use our own PID)
105
+ const pidFilePath = join(tempDir, ".agentplate", "watchdog.pid");
106
+ await Bun.write(pidFilePath, `${process.pid}\n`);
107
+
108
+ // Try to start in background mode — should fail with "already running"
109
+ await watchCommand(["--background"]);
110
+
111
+ const err = stderr();
112
+ expect(err).toContain("already running");
113
+ expect(err).toContain(`${process.pid}`);
114
+ expect(process.exitCode).toBe(1);
115
+ });
116
+
117
+ test("foreground mode: refuses when a live foreign PID owns the lock", async () => {
118
+ // Spawn a long-running child to act as the "foreign live process". Its
119
+ // PID will not match our own, so acquirePidLock should refuse rather
120
+ // than treat the existing PID as idempotent self-ownership. The
121
+ // foreground path used to overwrite this file unconditionally — the
122
+ // agentplate-8ef6 fix forces it to refuse.
123
+ const sleeper = Bun.spawn(["sleep", "30"], {
124
+ stdout: "ignore",
125
+ stderr: "ignore",
126
+ });
127
+ try {
128
+ const pidFilePath = join(tempDir, ".agentplate", "watchdog.pid");
129
+ await Bun.write(pidFilePath, `${sleeper.pid}\n`);
130
+
131
+ // --json for structured output. No --background, so this exercises
132
+ // the foreground exclusion path. A correctly contested lock returns
133
+ // immediately (exit 1) without starting the daemon loop.
134
+ await watchCommand(["--json"]);
135
+
136
+ const out = output();
137
+ const jsonLine = out
138
+ .split("\n")
139
+ .map((l) => l.trim())
140
+ .find((l) => l.startsWith("{"));
141
+ expect(jsonLine).toBeDefined();
142
+ if (jsonLine) {
143
+ const parsed = JSON.parse(jsonLine);
144
+ expect(parsed.running).toBe(true);
145
+ expect(parsed.pid).toBe(sleeper.pid);
146
+ expect(parsed.error).toContain("already running");
147
+ }
148
+ expect(process.exitCode).toBe(1);
149
+
150
+ // PID file untouched — still the foreign owner's PID.
151
+ const fileContent = await Bun.file(pidFilePath).text();
152
+ expect(fileContent.trim()).toBe(`${sleeper.pid}`);
153
+ } finally {
154
+ sleeper.kill("SIGTERM");
155
+ await sleeper.exited.catch(() => {});
156
+ }
157
+ });
158
+
159
+ test("background mode: stale PID cleanup", async () => {
160
+ // Write a PID file with a non-running process (999999 is very unlikely to exist)
161
+ const pidFilePath = join(tempDir, ".agentplate", "watchdog.pid");
162
+ await Bun.write(pidFilePath, "999999\n");
163
+
164
+ // Verify the stale PID file exists before the test
165
+ const fileBeforeExists = await Bun.file(pidFilePath).exists();
166
+ expect(fileBeforeExists).toBe(true);
167
+
168
+ // Try to start in background mode
169
+ // This will clean up the stale PID file, then attempt to spawn.
170
+ // The spawn will fail because there's no real agentplate binary in test env,
171
+ // but the important part is that the stale PID file gets removed.
172
+ try {
173
+ await watchCommand(["--background"]);
174
+ } catch {
175
+ // Expected to fail when trying to spawn — that's OK
176
+ }
177
+
178
+ // The stale PID file should have been removed during the check
179
+ // (Even if the spawn itself failed, the cleanup happens before spawn)
180
+ // Actually, looking at the code: if existingPid is not null but not running,
181
+ // it removes the PID file. Then it tries to spawn. So the file should be gone
182
+ // OR replaced with a new PID.
183
+
184
+ // Let's check: the file should either not exist, OR contain a different PID
185
+ const fileAfterExists = await Bun.file(pidFilePath).exists();
186
+ if (fileAfterExists) {
187
+ const content = await Bun.file(pidFilePath).text();
188
+ expect(content.trim()).not.toBe("999999");
189
+ }
190
+ // If it doesn't exist, that's also valid (spawn failed before writing new PID)
191
+ });
192
+ });
193
+
194
+ describe("formatCheck", () => {
195
+ function makeCheck(overrides: Partial<HealthCheck>): HealthCheck {
196
+ return {
197
+ agentName: "test-agent",
198
+ timestamp: new Date().toISOString(),
199
+ processAlive: true,
200
+ tmuxAlive: true,
201
+ pidAlive: true,
202
+ lastActivity: new Date().toISOString(),
203
+ state: "working",
204
+ action: "none",
205
+ reconciliationNote: null,
206
+ ...overrides,
207
+ };
208
+ }
209
+
210
+ test("terminate action uses x icon", () => {
211
+ const result = formatCheck(makeCheck({ action: "terminate" }));
212
+ expect(result).toMatch(/^x /);
213
+ });
214
+
215
+ test("escalate action uses ! icon", () => {
216
+ const result = formatCheck(makeCheck({ action: "escalate" }));
217
+ expect(result).toMatch(/^! /);
218
+ });
219
+
220
+ test("investigate action uses > icon", () => {
221
+ const result = formatCheck(makeCheck({ action: "investigate" }));
222
+ expect(result).toMatch(/^> /);
223
+ });
224
+
225
+ test("pidAlive true shows up", () => {
226
+ const result = formatCheck(makeCheck({ pidAlive: true }));
227
+ expect(result).toContain("pid=up");
228
+ });
229
+
230
+ test("pidAlive false shows down", () => {
231
+ const result = formatCheck(makeCheck({ pidAlive: false }));
232
+ expect(result).toContain("pid=down");
233
+ });
234
+
235
+ test("pidAlive null shows n/a", () => {
236
+ const result = formatCheck(makeCheck({ pidAlive: null }));
237
+ expect(result).toContain("pid=n/a");
238
+ });
239
+
240
+ test("includes reconciliation note when present", () => {
241
+ const result = formatCheck(makeCheck({ reconciliationNote: "stale session" }));
242
+ expect(result).toContain("[stale session]");
243
+ });
244
+
245
+ test("no reconciliation note brackets when null", () => {
246
+ const result = formatCheck(makeCheck({ reconciliationNote: null }));
247
+ expect(result).not.toContain("[");
248
+ });
249
+
250
+ test("includes agent name and state", () => {
251
+ const result = formatCheck(makeCheck({ agentName: "builder-1", state: "stalled" }));
252
+ expect(result).toContain("builder-1");
253
+ expect(result).toContain("stalled");
254
+ });
255
+ });
256
+
257
+ // PID and bin utility tests moved to src/utils/pid.test.ts and src/utils/bin.test.ts
@@ -0,0 +1,308 @@
1
+ /**
2
+ * CLI command: agentplate watch [--interval <ms>] [--background]
3
+ *
4
+ * Starts the Tier 0 mechanical watchdog daemon. Foreground mode shows real-time status.
5
+ * Background mode spawns a detached process via Bun.spawn and writes a PID file.
6
+ * Interval configurable, default 30000ms.
7
+ */
8
+
9
+ import { join } from "node:path";
10
+ import { Command } from "commander";
11
+ import { loadConfig } from "../config.ts";
12
+ import { jsonOutput } from "../json.ts";
13
+ import { printError, printHint, printSuccess } from "../logging/color.ts";
14
+ import type { HealthCheck } from "../types.ts";
15
+ import { resolveAgentplateBin } from "../utils/bin.ts";
16
+ import {
17
+ type AcquirePidLockResult,
18
+ acquirePidLock,
19
+ readPidFile,
20
+ removePidFile,
21
+ } from "../utils/pid.ts";
22
+ import { findRunningWatchdogProcesses, type WatchdogProcess } from "../utils/process-scan.ts";
23
+ import { startDaemon } from "../watchdog/daemon.ts";
24
+ import { isProcessRunning } from "../watchdog/health.ts";
25
+
26
+ /**
27
+ * Format a health check for display.
28
+ * @internal Exported for testing.
29
+ */
30
+ export function formatCheck(check: HealthCheck): string {
31
+ const actionIcon =
32
+ check.action === "terminate"
33
+ ? "x"
34
+ : check.action === "escalate"
35
+ ? "!"
36
+ : check.action === "investigate"
37
+ ? ">"
38
+ : "x";
39
+ const pidLabel = check.pidAlive === null ? "n/a" : check.pidAlive ? "up" : "down";
40
+ let line = `${actionIcon} ${check.agentName}: ${check.state} (tmux=${check.tmuxAlive ? "up" : "down"}, pid=${pidLabel})`;
41
+ if (check.reconciliationNote) {
42
+ line += ` [${check.reconciliationNote}]`;
43
+ }
44
+ return line;
45
+ }
46
+
47
+ /**
48
+ * Format a "lock contested" error consistently across foreground/background.
49
+ */
50
+ function formatLockContestedError(existingPid: number, pidFilePath: string): string {
51
+ if (existingPid <= 0) {
52
+ return `Watchdog PID file at ${pidFilePath} is owned by another process (could not read PID). Run 'ap watch --kill-others' or remove the file.`;
53
+ }
54
+ return `Watchdog already running (PID: ${existingPid}). Kill it first, run 'ap watch --kill-others', or remove ${pidFilePath}`;
55
+ }
56
+
57
+ /**
58
+ * Kill running `ap watch` daemons that are NOT the given excludedPid.
59
+ * Returns the list of PIDs killed (after a SIGTERM was issued — not waited).
60
+ */
61
+ async function killForeignWatchdogs(
62
+ excludedPid: number | null,
63
+ ): Promise<{ killed: number[]; surveyed: WatchdogProcess[] }> {
64
+ const surveyed = await findRunningWatchdogProcesses();
65
+ const killed: number[] = [];
66
+ for (const proc of surveyed) {
67
+ if (excludedPid !== null && proc.pid === excludedPid) {
68
+ continue;
69
+ }
70
+ try {
71
+ process.kill(proc.pid, "SIGTERM");
72
+ killed.push(proc.pid);
73
+ } catch {
74
+ // Process already gone — not an error.
75
+ }
76
+ }
77
+ return { killed, surveyed };
78
+ }
79
+
80
+ /**
81
+ * Core implementation for the watch command.
82
+ */
83
+ async function runWatch(opts: {
84
+ interval?: string;
85
+ background?: boolean;
86
+ json?: boolean;
87
+ killOthers?: boolean;
88
+ }): Promise<void> {
89
+ const cwd = process.cwd();
90
+ const config = await loadConfig(cwd);
91
+
92
+ const intervalMs = opts.interval
93
+ ? Number.parseInt(opts.interval, 10)
94
+ : config.watchdog.tier0IntervalMs;
95
+
96
+ const staleThresholdMs = config.watchdog.staleThresholdMs;
97
+ const zombieThresholdMs = config.watchdog.zombieThresholdMs;
98
+ const pidFilePath = join(config.project.root, ".agentplate", "watchdog.pid");
99
+
100
+ const useJson = opts.json ?? false;
101
+
102
+ // --kill-others: kill any pre-existing `ap watch` daemons before claiming
103
+ // the lock. Useful when an earlier release allowed multi-daemon state.
104
+ if (opts.killOthers) {
105
+ const { killed } = await killForeignWatchdogs(null);
106
+
107
+ // Wait for the just-killed processes to actually exit before reclaiming
108
+ // the PID file. Without this, the next acquirePidLock call sees a still-
109
+ // alive PID in the file and refuses, even though we issued SIGTERM
110
+ // nanoseconds earlier. Poll for up to ~2s.
111
+ const killedSet = new Set(killed);
112
+ if (killedSet.size > 0) {
113
+ const deadline = Date.now() + 2000;
114
+ while (Date.now() < deadline) {
115
+ const stillAlive = killed.filter((p) => isProcessRunning(p));
116
+ if (stillAlive.length === 0) break;
117
+ await Bun.sleep(50);
118
+ }
119
+ }
120
+
121
+ // Reclaim the PID file if it pointed at a process we just killed (it is
122
+ // either already dead or in flight to dead) or at any other dead PID.
123
+ const existingPid = await readPidFile(pidFilePath);
124
+ if (existingPid !== null) {
125
+ if (killedSet.has(existingPid) || !isProcessRunning(existingPid)) {
126
+ await removePidFile(pidFilePath);
127
+ }
128
+ }
129
+
130
+ if (killed.length > 0) {
131
+ if (useJson) {
132
+ jsonOutput("watch", { killed });
133
+ } else {
134
+ printSuccess(`Killed ${killed.length} foreign watchdog process(es): ${killed.join(", ")}`);
135
+ }
136
+ } else if (!useJson) {
137
+ printHint("No foreign watchdog processes found.");
138
+ }
139
+ }
140
+
141
+ if (opts.background) {
142
+ // Build the args for the child process, forwarding --interval but not --background
143
+ const childArgs: string[] = ["watch"];
144
+ if (opts.interval) {
145
+ childArgs.push("--interval", opts.interval);
146
+ }
147
+
148
+ // Resolve the agentplate binary path
149
+ const agentplateBin = await resolveAgentplateBin();
150
+
151
+ // Pre-check: surface "already running" before paying the cost of a spawn.
152
+ // This is only for friendly errors — the authoritative exclusion happens
153
+ // in the atomic acquirePidLock call below.
154
+ const existingPid = await readPidFile(pidFilePath);
155
+ if (existingPid !== null && isProcessRunning(existingPid)) {
156
+ if (useJson) {
157
+ jsonOutput("watch", { running: true, pid: existingPid, error: "Watchdog already running" });
158
+ } else {
159
+ printError(formatLockContestedError(existingPid, pidFilePath));
160
+ }
161
+ process.exitCode = 1;
162
+ return;
163
+ }
164
+
165
+ // Spawn the detached background daemon (foreground mode in the child).
166
+ const child = Bun.spawn(["bun", "run", agentplateBin, ...childArgs], {
167
+ cwd,
168
+ stdout: "ignore",
169
+ stderr: "ignore",
170
+ stdin: "ignore",
171
+ });
172
+
173
+ const childPid = child.pid;
174
+
175
+ // Atomically acquire the lock with the child's PID. If another writer
176
+ // raced in between our pre-check and the spawn, we have to kill our
177
+ // child and report contention.
178
+ const lockResult = await acquirePidLock(pidFilePath, childPid, isProcessRunning);
179
+ if (!lockResult.acquired) {
180
+ try {
181
+ child.kill("SIGTERM");
182
+ } catch {
183
+ // Already exited — not an error.
184
+ }
185
+ if (useJson) {
186
+ jsonOutput("watch", {
187
+ running: true,
188
+ pid: lockResult.existingPid,
189
+ error: "Watchdog already running",
190
+ });
191
+ } else {
192
+ printError(formatLockContestedError(lockResult.existingPid, pidFilePath));
193
+ }
194
+ process.exitCode = 1;
195
+ return;
196
+ }
197
+
198
+ // Lock is ours. Detach so this parent invocation can exit independently.
199
+ child.unref();
200
+
201
+ if (useJson) {
202
+ jsonOutput("watch", { pid: childPid, intervalMs, pidFile: pidFilePath });
203
+ } else {
204
+ printSuccess("Watchdog started in background", `PID: ${childPid}, interval: ${intervalMs}ms`);
205
+ printHint(`PID file: ${pidFilePath}`);
206
+ }
207
+ return;
208
+ }
209
+
210
+ // Foreground mode: acquire the lock atomically before announcing anything.
211
+ // In the background-spawn case the parent has already written this PID into
212
+ // the lock file; acquirePidLock detects own-PID and returns acquired=true
213
+ // idempotently.
214
+ const lockResult: AcquirePidLockResult = await acquirePidLock(
215
+ pidFilePath,
216
+ process.pid,
217
+ isProcessRunning,
218
+ );
219
+ if (!lockResult.acquired) {
220
+ if (useJson) {
221
+ jsonOutput("watch", {
222
+ running: true,
223
+ pid: lockResult.existingPid,
224
+ error: "Watchdog already running",
225
+ });
226
+ } else {
227
+ printError(formatLockContestedError(lockResult.existingPid, pidFilePath));
228
+ }
229
+ process.exitCode = 1;
230
+ return;
231
+ }
232
+
233
+ if (useJson) {
234
+ jsonOutput("watch", { pid: process.pid, intervalMs, mode: "foreground" });
235
+ } else {
236
+ printSuccess("Watchdog running", `interval: ${intervalMs}ms`);
237
+ printHint("Press Ctrl+C to stop.");
238
+ }
239
+
240
+ const { stop } = startDaemon({
241
+ root: config.project.root,
242
+ intervalMs,
243
+ staleThresholdMs,
244
+ zombieThresholdMs,
245
+ nudgeIntervalMs: config.watchdog.nudgeIntervalMs,
246
+ tier1Enabled: config.watchdog.tier1Enabled,
247
+ notifyParentOnDeath: config.watchdog.notifyParentOnDeath ?? true,
248
+ onHealthCheck(check) {
249
+ const timestamp = new Date().toISOString().slice(11, 19);
250
+ process.stdout.write(`[${timestamp}] ${formatCheck(check)}\n`);
251
+ },
252
+ });
253
+
254
+ // Keep running until interrupted
255
+ await new Promise<void>((resolve) => {
256
+ process.on("SIGINT", () => {
257
+ stop();
258
+ // Clean up PID file on graceful shutdown
259
+ removePidFile(pidFilePath).finally(() => {
260
+ printSuccess("Watchdog stopped.");
261
+ process.exitCode = 0;
262
+ resolve();
263
+ });
264
+ });
265
+ });
266
+ }
267
+
268
+ export function createWatchCommand(): Command {
269
+ return new Command("watch")
270
+ .description("Start Tier 0 mechanical watchdog daemon")
271
+ .option("--interval <ms>", "Health check interval in milliseconds")
272
+ .option("--background", "Daemonize (run in background)")
273
+ .option(
274
+ "--kill-others",
275
+ "Kill any pre-existing 'ap watch' processes before starting (for cleanup of multi-daemon state)",
276
+ )
277
+ .option("--json", "Output as JSON")
278
+ .action(
279
+ async (opts: {
280
+ interval?: string;
281
+ background?: boolean;
282
+ killOthers?: boolean;
283
+ json?: boolean;
284
+ }) => {
285
+ await runWatch(opts);
286
+ },
287
+ );
288
+ }
289
+
290
+ /**
291
+ * Entry point for `agentplate watch [--interval <ms>] [--background]`.
292
+ */
293
+ export async function watchCommand(args: string[]): Promise<void> {
294
+ const cmd = createWatchCommand();
295
+ cmd.exitOverride();
296
+
297
+ try {
298
+ await cmd.parseAsync(args, { from: "user" });
299
+ } catch (err: unknown) {
300
+ if (err && typeof err === "object" && "code" in err) {
301
+ const code = (err as { code: string }).code;
302
+ if (code === "commander.helpDisplayed" || code === "commander.version") {
303
+ return;
304
+ }
305
+ }
306
+ throw err;
307
+ }
308
+ }