@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,648 @@
1
+ /**
2
+ * CLI command: ap inspect <agent-name>
3
+ *
4
+ * Deep per-agent inspection aggregating data from EventStore, SessionStore,
5
+ * MetricsStore, and tmux capture-pane.
6
+ */
7
+
8
+ import { readdir } from "node:fs/promises";
9
+ import { join } from "node:path";
10
+ import { Command } from "commander";
11
+ import { loadConfig } from "../config.ts";
12
+ import { ValidationError } from "../errors.ts";
13
+ import { createEventStore } from "../events/store.ts";
14
+ import { jsonOutput } from "../json.ts";
15
+ import { accent } from "../logging/color.ts";
16
+ import { formatDuration } from "../logging/format.ts";
17
+ import { renderHeader, separator, stateIconColored } from "../logging/theme.ts";
18
+ import { createMetricsStore } from "../metrics/store.ts";
19
+ import { openSessionStore } from "../sessions/compat.ts";
20
+ import type { AgentSession, StoredEvent, ToolStats } from "../types.ts";
21
+ import { TMUX_SOCKET } from "../worktree/tmux.ts";
22
+
23
+ /**
24
+ * Extract current file from most recent Edit/Write/Read tool_start event.
25
+ */
26
+ function extractCurrentFile(events: StoredEvent[]): string | null {
27
+ // Scan backwards for tool_start events with Edit/Write/Read
28
+ const fileTools = ["Edit", "Write", "Read"];
29
+ for (let i = events.length - 1; i >= 0; i--) {
30
+ const event = events[i];
31
+ if (
32
+ event &&
33
+ event.eventType === "tool_start" &&
34
+ event.toolName &&
35
+ fileTools.includes(event.toolName) &&
36
+ event.toolArgs
37
+ ) {
38
+ try {
39
+ const args = JSON.parse(event.toolArgs) as Record<string, unknown>;
40
+ const filePath = (args.file_path as string) ?? (args.path as string);
41
+ if (filePath) {
42
+ return filePath;
43
+ }
44
+ } catch {
45
+ // Failed to parse JSON, continue
46
+ }
47
+ }
48
+ }
49
+ return null;
50
+ }
51
+
52
+ /**
53
+ * Summarize tool arguments for display (truncate long values).
54
+ */
55
+ function summarizeArgs(toolArgs: string | null): string {
56
+ if (!toolArgs) return "";
57
+ try {
58
+ const parsed = JSON.parse(toolArgs) as Record<string, unknown>;
59
+ const entries = Object.entries(parsed)
60
+ .map(([key, value]) => {
61
+ const str = String(value);
62
+ return `${key}=${str.length > 40 ? `${str.slice(0, 37)}...` : str}`;
63
+ })
64
+ .join(", ");
65
+ return entries.length > 100 ? `${entries.slice(0, 97)}...` : entries;
66
+ } catch {
67
+ return toolArgs.length > 100 ? `${toolArgs.slice(0, 97)}...` : toolArgs;
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Capture tmux pane output.
73
+ */
74
+ async function captureTmux(sessionName: string, lines: number): Promise<string | null> {
75
+ try {
76
+ const proc = Bun.spawn(
77
+ ["tmux", "-L", TMUX_SOCKET, "capture-pane", "-t", sessionName, "-p", "-S", `-${lines}`],
78
+ {
79
+ stdout: "pipe",
80
+ stderr: "pipe",
81
+ },
82
+ );
83
+ const exitCode = await proc.exited;
84
+ if (exitCode !== 0) {
85
+ return null;
86
+ }
87
+ const output = await new Response(proc.stdout).text();
88
+ return output.trim();
89
+ } catch {
90
+ return null;
91
+ }
92
+ }
93
+
94
+ /** Parsed data from a headless agent's stdout.log NDJSON event stream. */
95
+ interface StdoutLogData {
96
+ toolCalls: Array<{
97
+ toolName: string;
98
+ argsSummary: string;
99
+ durationMs: number | null;
100
+ timestamp: string;
101
+ }>;
102
+ cumulativeInputTokens: number;
103
+ cumulativeOutputTokens: number;
104
+ cumulativeCacheReadTokens: number;
105
+ lastModel: string;
106
+ lastContextUtilization: number | null;
107
+ currentTurn: number;
108
+ isMidTool: boolean;
109
+ }
110
+
111
+ /**
112
+ * Find the most recent log directory for a headless agent.
113
+ * Looks under logsBaseDir/{agentName}/ and returns the last entry
114
+ * when sorted alphabetically (ISO timestamps sort = chronological).
115
+ */
116
+ async function findLatestLogDir(logsBaseDir: string, agentName: string): Promise<string | null> {
117
+ const agentLogsDir = join(logsBaseDir, agentName);
118
+ try {
119
+ const entries = await readdir(agentLogsDir);
120
+ if (entries.length === 0) return null;
121
+ entries.sort();
122
+ const latest = entries[entries.length - 1];
123
+ if (!latest) return null;
124
+ return join(agentLogsDir, latest);
125
+ } catch {
126
+ return null;
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Parse the last 200 lines of a headless agent's stdout.log NDJSON file.
132
+ *
133
+ * Extracts tool call activity and token usage from Sapling/Codex event streams.
134
+ * Handles partial lines and malformed JSON gracefully.
135
+ *
136
+ * @param logPath - Absolute path to stdout.log
137
+ * @returns Parsed data, or null if file missing or unreadable
138
+ */
139
+ async function parseStdoutLog(logPath: string): Promise<StdoutLogData | null> {
140
+ const file = Bun.file(logPath);
141
+ if (!(await file.exists())) return null;
142
+
143
+ try {
144
+ const text = await file.text();
145
+ const allLines = text.split("\n");
146
+ // Tail last 200 lines for efficiency
147
+ const lines = allLines.length > 200 ? allLines.slice(-200) : allLines;
148
+
149
+ const toolCalls: StdoutLogData["toolCalls"] = [];
150
+
151
+ // Track pending tool_start events for durationMs matching.
152
+ // When tool_end arrives, pop the most recent pending entry with matching toolName.
153
+ const pendingTools: Array<{
154
+ toolName: string;
155
+ argsSummary: string;
156
+ timestamp: string;
157
+ }> = [];
158
+
159
+ let cumulativeInputTokens = 0;
160
+ let cumulativeOutputTokens = 0;
161
+ let cumulativeCacheReadTokens = 0;
162
+ let lastModel = "";
163
+ let lastContextUtilization: number | null = null;
164
+ let currentTurn = 0;
165
+ let lastEventType: string | null = null;
166
+
167
+ for (const line of lines) {
168
+ const trimmed = line.trim();
169
+ if (!trimmed) continue;
170
+
171
+ let event: Record<string, unknown>;
172
+ try {
173
+ event = JSON.parse(trimmed) as Record<string, unknown>;
174
+ } catch {
175
+ continue;
176
+ }
177
+
178
+ const type = typeof event.type === "string" ? event.type : null;
179
+ if (!type) continue;
180
+
181
+ lastEventType = type;
182
+ const timestamp =
183
+ typeof event.timestamp === "string" ? event.timestamp : new Date().toISOString();
184
+
185
+ if (type === "tool_start") {
186
+ const toolName = typeof event.toolName === "string" ? event.toolName : "unknown";
187
+ const argsSummary = typeof event.argsSummary === "string" ? event.argsSummary : "";
188
+ pendingTools.push({ toolName, argsSummary, timestamp });
189
+ } else if (type === "tool_end") {
190
+ const toolName = typeof event.toolName === "string" ? event.toolName : "";
191
+ const durationMs = typeof event.durationMs === "number" ? event.durationMs : null;
192
+
193
+ // Find and pop the most recent matching pending tool
194
+ let pendingIdx = -1;
195
+ for (let i = pendingTools.length - 1; i >= 0; i--) {
196
+ if (pendingTools[i]?.toolName === toolName) {
197
+ pendingIdx = i;
198
+ break;
199
+ }
200
+ }
201
+ if (pendingIdx >= 0) {
202
+ const pending = pendingTools[pendingIdx];
203
+ if (pending) {
204
+ pendingTools.splice(pendingIdx, 1);
205
+ toolCalls.push({
206
+ toolName: pending.toolName,
207
+ argsSummary: pending.argsSummary,
208
+ durationMs,
209
+ timestamp: pending.timestamp,
210
+ });
211
+ }
212
+ }
213
+ } else if (type === "turn_start") {
214
+ const turn = typeof event.turn === "number" ? event.turn : currentTurn + 1;
215
+ currentTurn = turn;
216
+ } else if (type === "turn_end") {
217
+ const inputTokens = typeof event.inputTokens === "number" ? event.inputTokens : 0;
218
+ const outputTokens = typeof event.outputTokens === "number" ? event.outputTokens : 0;
219
+ const cacheReadTokens =
220
+ typeof event.cacheReadTokens === "number" ? event.cacheReadTokens : 0;
221
+ const model = typeof event.model === "string" ? event.model : "";
222
+ const ctxUtil =
223
+ typeof event.contextUtilization === "number" ? event.contextUtilization : null;
224
+
225
+ cumulativeInputTokens += inputTokens;
226
+ cumulativeOutputTokens += outputTokens;
227
+ cumulativeCacheReadTokens += cacheReadTokens;
228
+ if (model) lastModel = model;
229
+ if (ctxUtil !== null) lastContextUtilization = ctxUtil;
230
+ }
231
+ }
232
+
233
+ // Any still-pending tool_starts are mid-execution — include them without durationMs
234
+ for (const pending of pendingTools) {
235
+ toolCalls.push({
236
+ toolName: pending.toolName,
237
+ argsSummary: pending.argsSummary,
238
+ durationMs: null,
239
+ timestamp: pending.timestamp,
240
+ });
241
+ }
242
+
243
+ return {
244
+ toolCalls,
245
+ cumulativeInputTokens,
246
+ cumulativeOutputTokens,
247
+ cumulativeCacheReadTokens,
248
+ lastModel,
249
+ lastContextUtilization,
250
+ currentTurn,
251
+ isMidTool: lastEventType === "tool_start",
252
+ };
253
+ } catch {
254
+ return null;
255
+ }
256
+ }
257
+
258
+ export interface InspectData {
259
+ session: AgentSession;
260
+ timeSinceLastActivity: number;
261
+ recentToolCalls: Array<{
262
+ toolName: string;
263
+ args: string;
264
+ durationMs: number | null;
265
+ timestamp: string;
266
+ }>;
267
+ currentFile: string | null;
268
+ toolStats: ToolStats[];
269
+ tokenUsage: {
270
+ inputTokens: number;
271
+ outputTokens: number;
272
+ cacheReadTokens: number;
273
+ cacheCreationTokens: number;
274
+ estimatedCostUsd: number | null;
275
+ modelUsed: string | null;
276
+ } | null;
277
+ tmuxOutput: string | null;
278
+ /** Turn progress for headless agents (populated from stdout.log). */
279
+ headlessTurnInfo: {
280
+ currentTurn: number;
281
+ contextUtilization: number | null;
282
+ isMidTool: boolean;
283
+ } | null;
284
+ }
285
+
286
+ /**
287
+ * Gather all inspection data for an agent.
288
+ */
289
+ export async function gatherInspectData(
290
+ root: string,
291
+ agentName: string,
292
+ opts: {
293
+ limit?: number;
294
+ noTmux?: boolean;
295
+ tmuxLines?: number;
296
+ } = {},
297
+ ): Promise<InspectData> {
298
+ const agentplateDir = join(root, ".agentplate");
299
+ const { store } = openSessionStore(agentplateDir);
300
+
301
+ let session: AgentSession | null = null;
302
+ try {
303
+ session = store.getByName(agentName);
304
+ if (!session) {
305
+ throw new ValidationError(`Agent not found: ${agentName}`, {
306
+ field: "agent-name",
307
+ value: agentName,
308
+ });
309
+ }
310
+
311
+ const now = Date.now();
312
+ const timeSinceLastActivity = now - new Date(session.lastActivity).getTime();
313
+
314
+ // EventStore: recent tool calls and tool stats
315
+ let recentToolCalls: InspectData["recentToolCalls"] = [];
316
+ let currentFile: string | null = null;
317
+ let toolStats: ToolStats[] = [];
318
+
319
+ const eventsDbPath = join(agentplateDir, "events.db");
320
+ const eventsFile = Bun.file(eventsDbPath);
321
+ if (await eventsFile.exists()) {
322
+ const eventStore = createEventStore(eventsDbPath);
323
+ try {
324
+ // Get recent events for this agent
325
+ const events = eventStore.getByAgent(agentName, { limit: 200 });
326
+
327
+ // Extract current file from most recent Edit/Write/Read tool_start
328
+ currentFile = extractCurrentFile(events);
329
+
330
+ // Filter to tool_start events for recent tool calls display
331
+ const toolStartEvents = events.filter((e) => e.eventType === "tool_start");
332
+ const limit = opts.limit ?? 20;
333
+ recentToolCalls = toolStartEvents.slice(0, limit).map((event) => ({
334
+ toolName: event.toolName ?? "unknown",
335
+ args: summarizeArgs(event.toolArgs),
336
+ durationMs: event.toolDurationMs,
337
+ timestamp: event.createdAt,
338
+ }));
339
+
340
+ // Tool usage statistics
341
+ toolStats = eventStore.getToolStats({ agentName });
342
+ } finally {
343
+ eventStore.close();
344
+ }
345
+ }
346
+
347
+ // MetricsStore: token usage
348
+ let tokenUsage: InspectData["tokenUsage"] = null;
349
+ const metricsDbPath = join(agentplateDir, "metrics.db");
350
+ const metricsFile = Bun.file(metricsDbPath);
351
+ if (await metricsFile.exists()) {
352
+ const metricsStore = createMetricsStore(metricsDbPath);
353
+ try {
354
+ const sessions = metricsStore.getSessionsByAgent(agentName);
355
+ const mostRecent = sessions[0];
356
+ if (mostRecent) {
357
+ tokenUsage = {
358
+ inputTokens: mostRecent.inputTokens,
359
+ outputTokens: mostRecent.outputTokens,
360
+ cacheReadTokens: mostRecent.cacheReadTokens,
361
+ cacheCreationTokens: mostRecent.cacheCreationTokens,
362
+ estimatedCostUsd: mostRecent.estimatedCostUsd,
363
+ modelUsed: mostRecent.modelUsed,
364
+ };
365
+ }
366
+ } finally {
367
+ metricsStore.close();
368
+ }
369
+ }
370
+
371
+ // tmux capture (skipped for headless agents where tmuxSession is empty)
372
+ let tmuxOutput: string | null = null;
373
+ if (!opts.noTmux && session.tmuxSession) {
374
+ const lines = opts.tmuxLines ?? 30;
375
+ tmuxOutput = await captureTmux(session.tmuxSession, lines);
376
+ }
377
+
378
+ // Headless stdout.log fallback: parse NDJSON event stream for rich activity data.
379
+ // Used when tmuxSession is empty (headless agent: sapling, codex, etc.).
380
+ let headlessTurnInfo: InspectData["headlessTurnInfo"] = null;
381
+ if (session.tmuxSession === "") {
382
+ const logsBaseDir = join(agentplateDir, "logs");
383
+ const latestLogDir = await findLatestLogDir(logsBaseDir, agentName);
384
+ if (latestLogDir !== null) {
385
+ const stdoutData = await parseStdoutLog(join(latestLogDir, "stdout.log"));
386
+ if (stdoutData !== null) {
387
+ // Populate recentToolCalls from stdout.log when events.db had nothing.
388
+ if (recentToolCalls.length === 0 && stdoutData.toolCalls.length > 0) {
389
+ const limit = opts.limit ?? 20;
390
+ recentToolCalls = stdoutData.toolCalls.slice(0, limit).map((call) => ({
391
+ toolName: call.toolName,
392
+ args: call.argsSummary,
393
+ durationMs: call.durationMs,
394
+ timestamp: call.timestamp,
395
+ }));
396
+ }
397
+
398
+ // Populate tokenUsage from turn_end events when metrics.db had nothing.
399
+ if (
400
+ tokenUsage === null &&
401
+ (stdoutData.cumulativeInputTokens > 0 || stdoutData.cumulativeOutputTokens > 0)
402
+ ) {
403
+ tokenUsage = {
404
+ inputTokens: stdoutData.cumulativeInputTokens,
405
+ outputTokens: stdoutData.cumulativeOutputTokens,
406
+ cacheReadTokens: stdoutData.cumulativeCacheReadTokens,
407
+ cacheCreationTokens: 0,
408
+ estimatedCostUsd: null,
409
+ modelUsed: stdoutData.lastModel || null,
410
+ };
411
+ }
412
+
413
+ // Always populate turn progress info for headless agents.
414
+ headlessTurnInfo = {
415
+ currentTurn: stdoutData.currentTurn,
416
+ contextUtilization: stdoutData.lastContextUtilization,
417
+ isMidTool: stdoutData.isMidTool,
418
+ };
419
+ }
420
+ }
421
+ }
422
+
423
+ // Headless fallback: show recent events as live output when no tmux
424
+ if (!tmuxOutput && session.tmuxSession === "" && recentToolCalls.length > 0) {
425
+ const lines: string[] = ["[Headless agent — showing recent tool events]", ""];
426
+ for (const call of recentToolCalls.slice(0, 15)) {
427
+ const time = new Date(call.timestamp).toLocaleTimeString();
428
+ const dur = call.durationMs !== null ? `${call.durationMs}ms` : "pending";
429
+ lines.push(` [${time}] ${call.toolName.padEnd(15)} ${dur}`);
430
+ }
431
+ tmuxOutput = lines.join("\n");
432
+ }
433
+
434
+ return {
435
+ session,
436
+ timeSinceLastActivity,
437
+ recentToolCalls,
438
+ currentFile,
439
+ toolStats,
440
+ tokenUsage,
441
+ tmuxOutput,
442
+ headlessTurnInfo,
443
+ };
444
+ } finally {
445
+ store.close();
446
+ }
447
+ }
448
+
449
+ /**
450
+ * Print inspection data in human-readable format.
451
+ */
452
+ export function printInspectData(data: InspectData): void {
453
+ const w = process.stdout.write.bind(process.stdout);
454
+ const { session } = data;
455
+
456
+ w(`\n${renderHeader(`Agent Inspection: ${accent(session.agentName)}`)}\n\n`);
457
+
458
+ // Agent state and metadata
459
+ w(`${stateIconColored(session.state)} State: ${session.state}\n`);
460
+ w(`Last activity: ${formatDuration(data.timeSinceLastActivity)} ago\n`);
461
+ w(`Task: ${accent(session.taskId)}\n`);
462
+ w(`Capability: ${session.capability}\n`);
463
+ w(`Branch: ${accent(session.branchName)}\n`);
464
+ if (session.parentAgent) {
465
+ w(`Parent: ${accent(session.parentAgent)} (depth: ${session.depth})\n`);
466
+ }
467
+ w(`Started: ${session.startedAt}\n`);
468
+ if (session.tmuxSession) {
469
+ w(`Tmux: ${accent(session.tmuxSession)}\n`);
470
+ } else if (session.pid !== null) {
471
+ w(`Process: PID ${accent(String(session.pid))} (headless)\n`);
472
+ }
473
+ w("\n");
474
+
475
+ // Current file
476
+ if (data.currentFile) {
477
+ w(`Current file: ${data.currentFile}\n\n`);
478
+ }
479
+
480
+ // Headless turn progress
481
+ if (data.headlessTurnInfo) {
482
+ const { currentTurn, contextUtilization, isMidTool } = data.headlessTurnInfo;
483
+ w("Turn Progress\n");
484
+ w(`${separator()}\n`);
485
+ if (currentTurn > 0) {
486
+ w(` Current turn: ${currentTurn}\n`);
487
+ }
488
+ if (contextUtilization !== null) {
489
+ const pct = (contextUtilization * 100).toFixed(1);
490
+ w(` Context usage: ${pct}%\n`);
491
+ }
492
+ const status = isMidTool ? "executing tool" : "between turns";
493
+ w(` Status: ${status}\n`);
494
+ w("\n");
495
+ }
496
+
497
+ // Token usage
498
+ if (data.tokenUsage) {
499
+ w("Token Usage\n");
500
+ w(`${separator()}\n`);
501
+ w(` Input: ${data.tokenUsage.inputTokens.toLocaleString()}\n`);
502
+ w(` Output: ${data.tokenUsage.outputTokens.toLocaleString()}\n`);
503
+ w(` Cache read: ${data.tokenUsage.cacheReadTokens.toLocaleString()}\n`);
504
+ w(` Cache created: ${data.tokenUsage.cacheCreationTokens.toLocaleString()}\n`);
505
+ if (data.tokenUsage.estimatedCostUsd !== null) {
506
+ w(` Estimated cost: $${data.tokenUsage.estimatedCostUsd.toFixed(4)}\n`);
507
+ }
508
+ if (data.tokenUsage.modelUsed) {
509
+ w(` Model: ${data.tokenUsage.modelUsed}\n`);
510
+ }
511
+ w("\n");
512
+ }
513
+
514
+ // Tool usage statistics (top 10)
515
+ if (data.toolStats.length > 0) {
516
+ w("Tool Usage (Top 10)\n");
517
+ w(`${separator()}\n`);
518
+ const top10 = data.toolStats.slice(0, 10);
519
+ for (const stat of top10) {
520
+ const avgMs = stat.avgDurationMs.toFixed(0);
521
+ w(` ${stat.toolName.padEnd(20)} ${String(stat.count).padStart(6)} calls `);
522
+ w(`avg: ${String(avgMs).padStart(6)}ms max: ${stat.maxDurationMs}ms\n`);
523
+ }
524
+ w("\n");
525
+ }
526
+
527
+ // Recent tool calls
528
+ if (data.recentToolCalls.length > 0) {
529
+ w(`Recent Tool Calls (last ${data.recentToolCalls.length})\n`);
530
+ w(`${separator()}\n`);
531
+ for (const call of data.recentToolCalls) {
532
+ const time = new Date(call.timestamp).toLocaleTimeString();
533
+ const duration = call.durationMs !== null ? `${call.durationMs}ms` : "pending";
534
+ w(` [${time}] ${call.toolName.padEnd(15)} ${duration.padStart(10)}`);
535
+ if (call.args) {
536
+ w(` ${call.args}`);
537
+ }
538
+ w("\n");
539
+ }
540
+ w("\n");
541
+ }
542
+
543
+ // tmux output (or headless fallback)
544
+ if (data.tmuxOutput) {
545
+ w(data.session.tmuxSession ? "Live Tmux Output\n" : "Recent Activity (headless)\n");
546
+ w(`${separator()}\n`);
547
+ w(`${data.tmuxOutput}\n`);
548
+ w(`${separator()}\n`);
549
+ }
550
+ }
551
+
552
+ interface InspectOpts {
553
+ json?: boolean;
554
+ follow?: boolean;
555
+ interval?: string;
556
+ limit?: string;
557
+ tmux?: boolean; // Commander: --no-tmux sets tmux=false
558
+ }
559
+
560
+ async function executeInspect(agentName: string, opts: InspectOpts): Promise<void> {
561
+ const json = opts.json ?? false;
562
+ const follow = opts.follow ?? false;
563
+ // Commander --no-tmux sets opts.tmux = false
564
+ const noTmux = opts.tmux === false;
565
+
566
+ const intervalStr = opts.interval;
567
+ const interval = intervalStr ? Number.parseInt(intervalStr, 10) : 3000;
568
+ if (Number.isNaN(interval) || interval < 500) {
569
+ throw new ValidationError("--interval must be a number >= 500 (milliseconds)", {
570
+ field: "interval",
571
+ value: intervalStr,
572
+ });
573
+ }
574
+
575
+ const limitStr = opts.limit;
576
+ const limit = limitStr ? Number.parseInt(limitStr, 10) : 20;
577
+ if (Number.isNaN(limit) || limit < 1) {
578
+ throw new ValidationError("--limit must be a number >= 1", {
579
+ field: "limit",
580
+ value: limitStr,
581
+ });
582
+ }
583
+
584
+ const cwd = process.cwd();
585
+ const config = await loadConfig(cwd);
586
+ const root = config.project.root;
587
+
588
+ if (follow) {
589
+ // Polling loop
590
+ while (true) {
591
+ // Clear screen
592
+ process.stdout.write("\x1b[2J\x1b[H");
593
+ const data = await gatherInspectData(root, agentName, {
594
+ limit,
595
+ noTmux,
596
+ tmuxLines: 30,
597
+ });
598
+ if (json) {
599
+ jsonOutput("inspect", data as unknown as Record<string, unknown>);
600
+ } else {
601
+ printInspectData(data);
602
+ }
603
+ await Bun.sleep(interval);
604
+ }
605
+ } else {
606
+ // Single snapshot
607
+ const data = await gatherInspectData(root, agentName, { limit, noTmux, tmuxLines: 30 });
608
+ if (json) {
609
+ jsonOutput("inspect", data as unknown as Record<string, unknown>);
610
+ } else {
611
+ printInspectData(data);
612
+ }
613
+ }
614
+ }
615
+
616
+ export function createInspectCommand(): Command {
617
+ return new Command("inspect")
618
+ .description("Deep inspection of a single agent")
619
+ .argument("<agent-name>", "Agent name to inspect")
620
+ .option("--json", "Output as JSON")
621
+ .option("--follow", "Poll and refresh continuously")
622
+ .option("--interval <ms>", "Polling interval for --follow in milliseconds (default: 3000)")
623
+ .option("--limit <n>", "Number of recent tool calls to show (default: 20)")
624
+ .option("--no-tmux", "Skip tmux capture-pane")
625
+ .action(async (agentName: string, opts: InspectOpts) => {
626
+ await executeInspect(agentName, opts);
627
+ });
628
+ }
629
+
630
+ export async function inspectCommand(args: string[]): Promise<void> {
631
+ const cmd = createInspectCommand();
632
+ cmd.exitOverride();
633
+ try {
634
+ await cmd.parseAsync(args, { from: "user" });
635
+ } catch (err: unknown) {
636
+ if (err && typeof err === "object" && "code" in err) {
637
+ const code = (err as { code: string }).code;
638
+ if (code === "commander.helpDisplayed" || code === "commander.version") {
639
+ return;
640
+ }
641
+ if (code.startsWith("commander.")) {
642
+ const message = err instanceof Error ? err.message : String(err);
643
+ throw new ValidationError(message, { field: "args" });
644
+ }
645
+ }
646
+ throw err;
647
+ }
648
+ }