@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,319 @@
1
+ /**
2
+ * CLI command: ap stop <agent-name>
3
+ *
4
+ * Explicitly terminates a running agent by:
5
+ * 1. Looking up the agent session by name
6
+ * 2a. For TUI agents: killing its tmux session (if alive)
7
+ * 2b. For headless agents (tmuxSession === ''): sending SIGTERM to the process tree
8
+ * 3. Marking it as completed in the SessionStore
9
+ * 4. Optionally removing its worktree and branch (--clean-worktree)
10
+ *
11
+ * Completed agents: ap stop <name> without --clean-worktree throws a helpful error.
12
+ * With --clean-worktree, completed agents skip the kill step and proceed to cleanup.
13
+ */
14
+
15
+ import { unlink } from "node:fs/promises";
16
+ import { join } from "node:path";
17
+ import { loadConfig } from "../config.ts";
18
+ import { AgentError, ValidationError } from "../errors.ts";
19
+ import { jsonOutput } from "../json.ts";
20
+ import { printSuccess, printWarning } from "../logging/color.ts";
21
+ import { createMailStore } from "../mail/store.ts";
22
+ import { openSessionStore } from "../sessions/compat.ts";
23
+ import type { MergeReadyPayload } from "../types.ts";
24
+ import { readPidFile } from "../utils/pid.ts";
25
+ import { removeWorktree } from "../worktree/manager.ts";
26
+ import { isProcessAlive, isSessionAlive, killProcessTree, killSession } from "../worktree/tmux.ts";
27
+
28
+ export interface StopOptions {
29
+ force?: boolean;
30
+ cleanWorktree?: boolean;
31
+ json?: boolean;
32
+ }
33
+
34
+ /** Dependency injection for testing. Uses real implementations when omitted. */
35
+ export interface StopDeps {
36
+ _tmux?: {
37
+ isSessionAlive: (name: string) => Promise<boolean>;
38
+ killSession: (name: string) => Promise<void>;
39
+ };
40
+ _worktree?: {
41
+ remove: (
42
+ repoRoot: string,
43
+ path: string,
44
+ options?: { force?: boolean; forceBranch?: boolean },
45
+ ) => Promise<void>;
46
+ };
47
+ _process?: {
48
+ isAlive: (pid: number) => boolean;
49
+ killTree: (pid: number) => Promise<void>;
50
+ };
51
+ _git?: {
52
+ deleteBranch: (repoRoot: string, branch: string) => Promise<boolean>;
53
+ };
54
+ }
55
+
56
+ /**
57
+ * Build the lead_completed nudge subject based on whether the lead actually sent
58
+ * merge_ready before exiting (agentplate-41fe). The merge_ready close-gate
59
+ * (commit 3e21338) prevents leads from running `sr close` without it, but a
60
+ * lead can still exit (process termination, watchdog kill, manual `ap stop`)
61
+ * without ever having sent one. The coordinator's surfacing of this nudge
62
+ * needs to distinguish those two cases.
63
+ */
64
+ function buildLeadCompletedSubject(agentName: string, mailDbPath: string): string {
65
+ let mergeReadyBranches: string[] = [];
66
+ let mergeReadyCount = 0;
67
+ try {
68
+ const store = createMailStore(mailDbPath);
69
+ try {
70
+ const messages = store.getAll({ from: agentName, type: "merge_ready" });
71
+ mergeReadyCount = messages.length;
72
+ for (const msg of messages) {
73
+ if (msg.payload === null) continue;
74
+ try {
75
+ const parsed = JSON.parse(msg.payload) as Partial<MergeReadyPayload>;
76
+ if (typeof parsed.branch === "string" && parsed.branch.length > 0) {
77
+ mergeReadyBranches.push(parsed.branch);
78
+ }
79
+ } catch {
80
+ // Skip messages with unparseable payloads
81
+ }
82
+ }
83
+ } finally {
84
+ store.close();
85
+ }
86
+ } catch {
87
+ // If the mail store can't be opened (corrupt db, permissions), fall back
88
+ // to the historical ambiguous phrasing rather than blocking the stop.
89
+ return `Lead ${agentName} completed — check mail for merge_ready/worker_done`;
90
+ }
91
+
92
+ if (mergeReadyCount === 0) {
93
+ return `Lead ${agentName} exited — no merge_ready sent, needs coordinator follow-up`;
94
+ }
95
+ // Dedupe in case a lead resent merge_ready for the same branch
96
+ mergeReadyBranches = Array.from(new Set(mergeReadyBranches));
97
+ if (mergeReadyBranches.length === 0) {
98
+ return `Lead ${agentName} sent ${mergeReadyCount} merge_ready (branch unknown)`;
99
+ }
100
+ if (mergeReadyBranches.length === 1) {
101
+ return `Lead ${agentName} sent merge_ready for branch ${mergeReadyBranches[0]}`;
102
+ }
103
+ return `Lead ${agentName} sent ${mergeReadyBranches.length} merge_ready (branches: ${mergeReadyBranches.join(", ")})`;
104
+ }
105
+
106
+ /** Delete a git branch (best-effort, non-fatal). */
107
+ async function deleteBranchBestEffort(repoRoot: string, branch: string): Promise<boolean> {
108
+ try {
109
+ const proc = Bun.spawn(["git", "branch", "-D", branch], {
110
+ cwd: repoRoot,
111
+ stdout: "pipe",
112
+ stderr: "pipe",
113
+ });
114
+ const exitCode = await proc.exited;
115
+ return exitCode === 0;
116
+ } catch {
117
+ return false;
118
+ }
119
+ }
120
+
121
+ /**
122
+ * Entry point for `ap stop <agent-name>`.
123
+ *
124
+ * @param agentName - Name of the agent to stop
125
+ * @param opts - Command options
126
+ * @param deps - Optional dependency injection for testing (tmux, worktree, process, git)
127
+ */
128
+ export async function stopCommand(
129
+ agentName: string,
130
+ opts: StopOptions,
131
+ deps: StopDeps = {},
132
+ ): Promise<void> {
133
+ if (!agentName || agentName.trim().length === 0) {
134
+ throw new ValidationError("Missing required argument: <agent-name>", {
135
+ field: "agentName",
136
+ value: "",
137
+ });
138
+ }
139
+
140
+ const json = opts.json ?? false;
141
+ const force = opts.force ?? false;
142
+ const cleanWorktree = opts.cleanWorktree ?? false;
143
+
144
+ const tmux = deps._tmux ?? { isSessionAlive, killSession };
145
+ const worktree = deps._worktree ?? { remove: removeWorktree };
146
+ const proc = deps._process ?? { isAlive: isProcessAlive, killTree: killProcessTree };
147
+ const git = deps._git ?? { deleteBranch: deleteBranchBestEffort };
148
+
149
+ const cwd = process.cwd();
150
+ const config = await loadConfig(cwd);
151
+ const projectRoot = config.project.root;
152
+ const agentplateDir = join(projectRoot, ".agentplate");
153
+
154
+ const { store } = openSessionStore(agentplateDir);
155
+ try {
156
+ const session = store.getByName(agentName);
157
+ if (!session) {
158
+ throw new AgentError(`Agent "${agentName}" not found`, { agentName });
159
+ }
160
+
161
+ const isAlreadyCompleted = session.state === "completed";
162
+
163
+ // Completed agents without --clean-worktree: throw with helpful message
164
+ if (isAlreadyCompleted && !cleanWorktree) {
165
+ throw new AgentError(
166
+ `Agent "${agentName}" is already completed. Use --clean-worktree to remove its worktree.`,
167
+ { agentName },
168
+ );
169
+ }
170
+
171
+ const isZombie = session.state === "zombie";
172
+ // Headless task-scoped agents (Phase 3 spawn-per-turn): tmuxSession is ""
173
+ // and session.pid is null between turns. The live PID for an in-flight
174
+ // turn is published at .agentplate/agents/<name>/turn.pid. Sapling RPC
175
+ // agents still use session.pid for their long-lived process.
176
+ const isHeadless = session.tmuxSession === "";
177
+ const turnPidPath = join(agentplateDir, "agents", agentName, "turn.pid");
178
+
179
+ let tmuxKilled = false;
180
+ let pidKilled = false;
181
+
182
+ // Skip kill operations for already-completed agents (process/tmux already gone)
183
+ if (!isAlreadyCompleted) {
184
+ if (isHeadless) {
185
+ // Prefer the per-turn PID file (Phase 3) — this catches an in-flight
186
+ // claude turn for any task-scoped capability. Fall back to the
187
+ // session row's pid for legacy/long-lived headless runtimes (Sapling).
188
+ const turnPid = await readPidFile(turnPidPath);
189
+ const targetPid = turnPid ?? session.pid;
190
+ if (targetPid !== null && proc.isAlive(targetPid)) {
191
+ await proc.killTree(targetPid);
192
+ pidKilled = true;
193
+ }
194
+ // Reap the turn.pid file so a subsequent ap stop / mail injector
195
+ // doesn't see a stale entry. Idempotent.
196
+ try {
197
+ await unlink(turnPidPath);
198
+ } catch {
199
+ // already gone — non-fatal
200
+ }
201
+ } else {
202
+ // TUI agent: kill via tmux session
203
+ const alive = await tmux.isSessionAlive(session.tmuxSession);
204
+ if (alive) {
205
+ await tmux.killSession(session.tmuxSession);
206
+ tmuxKilled = true;
207
+ }
208
+ }
209
+
210
+ // Mark session as completed via the guarded transition. `completed` is
211
+ // reachable from every non-completed state (including zombie, so `ap
212
+ // stop` can promote a watchdog-flagged zombie to a clean completion),
213
+ // so the only way this rejects is if state is already `completed` —
214
+ // which is the no-op we want anyway. Race-safe under agentplate-a993.
215
+ store.tryTransitionState(agentName, "completed");
216
+ store.updateLastActivity(agentName);
217
+
218
+ // Auto-nudge coordinator when a lead truly completes so it wakes up
219
+ // to process merge_ready / worker_done messages without waiting for
220
+ // user input. Fires from `ap stop` (real completion signal) rather
221
+ // than the per-turn Stop hook, which was spamming the coordinator
222
+ // (agentplate-49a7).
223
+ if (session.capability === "lead") {
224
+ try {
225
+ const mailDbPath = join(agentplateDir, "mail.db");
226
+ const subject = buildLeadCompletedSubject(agentName, mailDbPath);
227
+ const nudgesDir = join(agentplateDir, "pending-nudges");
228
+ const { mkdir } = await import("node:fs/promises");
229
+ await mkdir(nudgesDir, { recursive: true });
230
+ const markerPath = join(nudgesDir, "coordinator.json");
231
+ const marker = {
232
+ from: agentName,
233
+ reason: "lead_completed",
234
+ subject,
235
+ messageId: `auto-nudge-${agentName}-${Date.now()}`,
236
+ createdAt: new Date().toISOString(),
237
+ };
238
+ await Bun.write(markerPath, `${JSON.stringify(marker, null, "\t")}\n`);
239
+ } catch {
240
+ // Non-fatal: nudge failure should not break stop
241
+ }
242
+ }
243
+ }
244
+
245
+ // Optionally remove worktree and branch (best-effort, non-fatal)
246
+ let worktreeRemoved = false;
247
+ let branchDeleted = false;
248
+ if (cleanWorktree) {
249
+ if (session.worktreePath) {
250
+ try {
251
+ await worktree.remove(projectRoot, session.worktreePath, {
252
+ force,
253
+ forceBranch: false,
254
+ });
255
+ worktreeRemoved = true;
256
+ } catch (err) {
257
+ const msg = err instanceof Error ? err.message : String(err);
258
+ if (!json) printWarning("Failed to remove worktree", msg);
259
+ }
260
+ }
261
+
262
+ // Delete the branch after removing the worktree (best-effort, non-fatal)
263
+ if (session.branchName) {
264
+ try {
265
+ branchDeleted = await git.deleteBranch(projectRoot, session.branchName);
266
+ } catch {
267
+ branchDeleted = false;
268
+ }
269
+ }
270
+ }
271
+
272
+ if (json) {
273
+ jsonOutput("stop", {
274
+ stopped: true,
275
+ agentName,
276
+ sessionId: session.id,
277
+ capability: session.capability,
278
+ tmuxKilled,
279
+ pidKilled,
280
+ worktreeRemoved,
281
+ branchDeleted,
282
+ force,
283
+ wasZombie: isZombie,
284
+ wasCompleted: isAlreadyCompleted,
285
+ });
286
+ } else {
287
+ printSuccess("Agent stopped", agentName);
288
+ if (!isAlreadyCompleted) {
289
+ if (isHeadless) {
290
+ if (pidKilled) {
291
+ process.stdout.write(` Process tree killed: PID ${session.pid}\n`);
292
+ } else {
293
+ process.stdout.write(` Process was already dead (PID ${session.pid})\n`);
294
+ }
295
+ } else {
296
+ if (tmuxKilled) {
297
+ process.stdout.write(` Tmux session killed: ${session.tmuxSession}\n`);
298
+ } else {
299
+ process.stdout.write(` Tmux session was already dead\n`);
300
+ }
301
+ }
302
+ }
303
+ if (isZombie) {
304
+ process.stdout.write(` Zombie agent cleaned up (state → completed)\n`);
305
+ }
306
+ if (isAlreadyCompleted) {
307
+ process.stdout.write(` Agent was already completed (skipped kill)\n`);
308
+ }
309
+ if (cleanWorktree && worktreeRemoved) {
310
+ process.stdout.write(` Worktree removed: ${session.worktreePath}\n`);
311
+ }
312
+ if (cleanWorktree && branchDeleted) {
313
+ process.stdout.write(` Branch deleted: ${session.branchName}\n`);
314
+ }
315
+ }
316
+ } finally {
317
+ store.close();
318
+ }
319
+ }
@@ -0,0 +1,185 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { isRunningAsRoot } from "./sling.ts";
3
+ import { buildSupervisorBeacon, supervisorCommand } from "./supervisor.ts";
4
+
5
+ /**
6
+ * Tests for supervisor command functions.
7
+ *
8
+ * Session persistence is now handled by SessionStore (SQLite).
9
+ * Those tests live in src/sessions/store.test.ts and src/sessions/compat.test.ts.
10
+ * Here we test beacon generation.
11
+ */
12
+
13
+ describe("buildSupervisorBeacon", () => {
14
+ test("contains agent name and taskId from opts", () => {
15
+ const beacon = buildSupervisorBeacon({
16
+ name: "supervisor-1",
17
+ taskId: "task-abc123",
18
+ depth: 1,
19
+ parent: "coordinator",
20
+ });
21
+
22
+ expect(beacon).toContain("supervisor-1");
23
+ expect(beacon).toContain("task-abc123");
24
+ });
25
+
26
+ test("contains [AGENTPLATE] prefix", () => {
27
+ const beacon = buildSupervisorBeacon({
28
+ name: "supervisor-1",
29
+ taskId: "task-1",
30
+ depth: 1,
31
+ parent: "coordinator",
32
+ });
33
+
34
+ expect(beacon).toContain("[AGENTPLATE]");
35
+ });
36
+
37
+ test("contains (supervisor) designation", () => {
38
+ const beacon = buildSupervisorBeacon({
39
+ name: "supervisor-1",
40
+ taskId: "task-1",
41
+ depth: 1,
42
+ parent: "coordinator",
43
+ });
44
+
45
+ expect(beacon).toContain("(supervisor)");
46
+ });
47
+
48
+ test("contains depth and parent info from opts", () => {
49
+ const beacon = buildSupervisorBeacon({
50
+ name: "supervisor-1",
51
+ taskId: "task-1",
52
+ depth: 2,
53
+ parent: "lead-cli",
54
+ });
55
+
56
+ expect(beacon).toContain("Depth: 2");
57
+ expect(beacon).toContain("Parent: lead-cli");
58
+ });
59
+
60
+ test("contains startup instructions", () => {
61
+ const beacon = buildSupervisorBeacon({
62
+ name: "supervisor-1",
63
+ taskId: "task-1",
64
+ depth: 1,
65
+ parent: "coordinator",
66
+ });
67
+
68
+ // Should include loam prime
69
+ expect(beacon).toContain("loam prime");
70
+
71
+ // Should include mail check with agent name
72
+ expect(beacon).toContain("ap mail check --agent supervisor-1");
73
+
74
+ // Should include bd show with taskId
75
+ expect(beacon).toContain("bd show task-1");
76
+ });
77
+
78
+ test("contains ISO timestamp", () => {
79
+ const before = new Date();
80
+ const beacon = buildSupervisorBeacon({
81
+ name: "supervisor-1",
82
+ taskId: "task-1",
83
+ depth: 1,
84
+ parent: "coordinator",
85
+ });
86
+ const after = new Date();
87
+
88
+ // Extract timestamp from beacon (format: [AGENTPLATE] {name} (supervisor) {timestamp} task:{taskId})
89
+ const timestampMatch = beacon.match(/\(supervisor\)\s+(\S+)\s+task:/);
90
+ expect(timestampMatch).toBeTruthy();
91
+
92
+ if (timestampMatch?.[1]) {
93
+ const timestamp = new Date(timestampMatch[1]);
94
+ expect(timestamp.getTime()).toBeGreaterThanOrEqual(before.getTime());
95
+ expect(timestamp.getTime()).toBeLessThanOrEqual(after.getTime());
96
+
97
+ // Verify ISO format (should parse correctly)
98
+ expect(timestamp.toISOString()).toBeTruthy();
99
+ }
100
+ });
101
+ });
102
+
103
+ describe("supervisorCommand", () => {
104
+ test("--help prints help containing required keywords", async () => {
105
+ const originalWrite = process.stdout.write;
106
+ let output = "";
107
+ process.stdout.write = ((chunk: string) => {
108
+ output += chunk;
109
+ return true;
110
+ }) as typeof process.stdout.write;
111
+
112
+ try {
113
+ await supervisorCommand(["--help"]);
114
+ expect(output).toContain("supervisor");
115
+ expect(output).toContain("start");
116
+ expect(output).toContain("stop");
117
+ expect(output).toContain("status");
118
+ } finally {
119
+ process.stdout.write = originalWrite;
120
+ }
121
+ });
122
+
123
+ test("-h prints help", async () => {
124
+ const originalWrite = process.stdout.write;
125
+ let output = "";
126
+ process.stdout.write = ((chunk: string) => {
127
+ output += chunk;
128
+ return true;
129
+ }) as typeof process.stdout.write;
130
+
131
+ try {
132
+ await supervisorCommand(["-h"]);
133
+ expect(output).toContain("supervisor");
134
+ } finally {
135
+ process.stdout.write = originalWrite;
136
+ }
137
+ });
138
+
139
+ test("empty args [] shows help", async () => {
140
+ const originalWrite = process.stdout.write;
141
+ let output = "";
142
+ process.stdout.write = ((chunk: string) => {
143
+ output += chunk;
144
+ return true;
145
+ }) as typeof process.stdout.write;
146
+
147
+ try {
148
+ await supervisorCommand([]);
149
+ expect(output).toContain("supervisor");
150
+ } finally {
151
+ process.stdout.write = originalWrite;
152
+ }
153
+ });
154
+
155
+ test("unknown subcommand throws ValidationError with bad value in message", async () => {
156
+ expect(async () => {
157
+ await supervisorCommand(["invalid-subcommand"]);
158
+ }).toThrow(/invalid-subcommand/);
159
+ });
160
+
161
+ test("start without --task throws ValidationError", async () => {
162
+ expect(async () => {
163
+ await supervisorCommand(["start", "--name", "supervisor-1"]);
164
+ }).toThrow(/--task/);
165
+ });
166
+
167
+ test("start without --name throws ValidationError", async () => {
168
+ expect(async () => {
169
+ await supervisorCommand(["start", "--task", "task-1"]);
170
+ }).toThrow(/--name/);
171
+ });
172
+
173
+ test("stop without --name throws ValidationError", async () => {
174
+ expect(async () => {
175
+ await supervisorCommand(["stop"]);
176
+ }).toThrow(/--name/);
177
+ });
178
+ });
179
+
180
+ describe("isRunningAsRoot (imported from sling)", () => {
181
+ test("is accessible from supervisor test file", () => {
182
+ expect(isRunningAsRoot(() => 0)).toBe(true);
183
+ expect(isRunningAsRoot(() => 1000)).toBe(false);
184
+ });
185
+ });