@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,112 @@
1
+ import type { Command } from "commander";
2
+ import { c, errorOut, fmt, humanOut, jsonOut } from "../output.ts";
3
+ import { ExitError } from "../types.ts";
4
+
5
+ export default async function sync(args: string[], json: boolean): Promise<void> {
6
+ const cwd = process.cwd();
7
+
8
+ if (args.includes("--help") || args.includes("-h")) {
9
+ humanOut(`Usage: tl sync [options]
10
+
11
+ Options:
12
+ --status Check sync status without committing
13
+ --json Output as JSON`);
14
+ return;
15
+ }
16
+
17
+ const statusOnly = args.includes("--status");
18
+
19
+ // Check git status of .trellis/
20
+ const statusResult = Bun.spawnSync(["git", "status", "--porcelain", ".trellis/"], { cwd });
21
+
22
+ if (statusResult.exitCode !== 0) {
23
+ const stderr = statusResult.stderr.toString();
24
+ if (json) {
25
+ jsonOut({ success: false, command: "sync", error: stderr });
26
+ } else {
27
+ errorOut(`git error: ${stderr}`);
28
+ }
29
+ throw new ExitError(1);
30
+ }
31
+
32
+ const statusOutput = statusResult.stdout.toString().trim();
33
+ const changedFiles = statusOutput
34
+ ? statusOutput.split("\n").map((line) => line.trim().split(" ").pop() ?? "")
35
+ : [];
36
+
37
+ if (statusOnly) {
38
+ if (json) {
39
+ jsonOut({
40
+ success: true,
41
+ command: "sync",
42
+ uncommitted: changedFiles.length > 0,
43
+ files: changedFiles,
44
+ });
45
+ } else {
46
+ if (changedFiles.length === 0) {
47
+ humanOut(fmt.success(".trellis/ is clean (no uncommitted changes)"));
48
+ } else {
49
+ humanOut(fmt.warning(`${changedFiles.length} uncommitted file(s)`));
50
+ for (const f of changedFiles) {
51
+ humanOut(` ${f}`);
52
+ }
53
+ }
54
+ }
55
+ return;
56
+ }
57
+
58
+ if (changedFiles.length === 0) {
59
+ if (json) {
60
+ jsonOut({ success: true, command: "sync", committed: false, message: "Nothing to commit" });
61
+ } else {
62
+ humanOut(c.dim("Nothing to commit in .trellis/"));
63
+ }
64
+ return;
65
+ }
66
+
67
+ // Stage .trellis/ changes
68
+ const addResult = Bun.spawnSync(["git", "add", ".trellis/"], { cwd });
69
+ if (addResult.exitCode !== 0) {
70
+ const err = addResult.stderr.toString();
71
+ if (json) {
72
+ jsonOut({ success: false, command: "sync", error: err });
73
+ } else {
74
+ errorOut(`git add failed: ${err}`);
75
+ }
76
+ throw new ExitError(1);
77
+ }
78
+
79
+ // Commit
80
+ const msg = `trellis: sync ${new Date().toISOString().slice(0, 10)}`;
81
+ const commitResult = Bun.spawnSync(["git", "commit", "-m", msg], { cwd });
82
+
83
+ if (commitResult.exitCode !== 0) {
84
+ const err = commitResult.stderr.toString();
85
+ if (json) {
86
+ jsonOut({ success: false, command: "sync", error: err });
87
+ } else {
88
+ errorOut(`git commit failed: ${err}`);
89
+ }
90
+ throw new ExitError(1);
91
+ }
92
+
93
+ if (json) {
94
+ jsonOut({ success: true, command: "sync", committed: true, files: changedFiles, message: msg });
95
+ } else {
96
+ humanOut(fmt.success(`Committed ${changedFiles.length} file(s): ${msg}`));
97
+ }
98
+ }
99
+
100
+ export function registerSyncCommand(program: Command): void {
101
+ program
102
+ .command("sync")
103
+ .description("Stage and commit .trellis/ changes")
104
+ .option("--status", "Check sync status without committing")
105
+ .action(async (options: { status?: boolean }) => {
106
+ const json: boolean = program.opts().json ?? false;
107
+ const args: string[] = [];
108
+ if (options.status) args.push("--status");
109
+ if (json) args.push("--json");
110
+ await sync(args, json);
111
+ });
112
+ }
@@ -0,0 +1,123 @@
1
+ import { join } from "node:path";
2
+ import type { Command } from "commander";
3
+ import { c, errorOut, humanOut, jsonOut } from "../output.ts";
4
+ import { dedupById, readJsonl } from "../store.ts";
5
+ import type { Prompt } from "../types.ts";
6
+ import { ExitError } from "../types.ts";
7
+
8
+ export default async function tree(args: string[], json: boolean): Promise<void> {
9
+ const cwd = process.cwd();
10
+ const promptsPath = join(cwd, ".trellis", "prompts.jsonl");
11
+
12
+ if (args.includes("--help") || args.includes("-h")) {
13
+ humanOut(`Usage: tl tree <name> [options]
14
+
15
+ Options:
16
+ --json Output as JSON`);
17
+ return;
18
+ }
19
+
20
+ const name = args.filter((a) => !a.startsWith("--"))[0];
21
+ if (!name) {
22
+ if (json) {
23
+ jsonOut({ success: false, command: "tree", error: "Prompt name required" });
24
+ } else {
25
+ errorOut("Usage: tl tree <name>");
26
+ }
27
+ throw new ExitError(1);
28
+ }
29
+
30
+ const allRecords = await readJsonl<Prompt>(promptsPath);
31
+ const current = dedupById(allRecords);
32
+
33
+ const prompt = current.find((p) => p.name === name);
34
+ if (!prompt) {
35
+ if (json) {
36
+ jsonOut({ success: false, command: "tree", error: `Prompt '${name}' not found` });
37
+ } else {
38
+ errorOut(`Prompt '${name}' not found`);
39
+ }
40
+ throw new ExitError(1);
41
+ }
42
+
43
+ // Build ancestry chain (parents via extends)
44
+ const ancestors: string[] = [];
45
+ let cur: Prompt | undefined = prompt;
46
+ while (cur?.extends) {
47
+ const parentName: string = cur.extends;
48
+ if (ancestors.includes(parentName)) break; // cycle guard
49
+ ancestors.push(parentName);
50
+ cur = current.find((p) => p.name === parentName);
51
+ }
52
+ ancestors.reverse();
53
+
54
+ // Collect mixins for the focal prompt
55
+ const mixins: string[] = prompt.mixins ?? [];
56
+
57
+ // Find all descendants (via extends or mixins)
58
+ function getChildren(pname: string): string[] {
59
+ return current.filter((p) => p.extends === pname && p.name !== pname).map((p) => p.name);
60
+ }
61
+
62
+ function getMixinUsers(pname: string): string[] {
63
+ return current.filter((p) => p.mixins?.includes(pname) && p.name !== pname).map((p) => p.name);
64
+ }
65
+
66
+ if (json) {
67
+ const buildTree = (pname: string): object => ({
68
+ name: pname,
69
+ children: getChildren(pname).map(buildTree),
70
+ mixinUsers: getMixinUsers(pname),
71
+ });
72
+
73
+ jsonOut({
74
+ success: true,
75
+ command: "tree",
76
+ name,
77
+ ancestors,
78
+ mixins,
79
+ tree: buildTree(name),
80
+ });
81
+ return;
82
+ }
83
+
84
+ // Render ancestors
85
+ for (let i = 0; i < ancestors.length; i++) {
86
+ const indent = " ".repeat(i);
87
+ humanOut(`${indent}${c.dim(ancestors[i] ?? "")}`);
88
+ }
89
+
90
+ // Render focal node
91
+ const focalIndent = " ".repeat(ancestors.length);
92
+ const mixinLabel = mixins.length > 0 ? c.dim(` + ${mixins.join(", ")}`) : "";
93
+ humanOut(`${focalIndent}${c.bold(c.cyan(name))} ${c.dim(`(v${prompt.version})`)}${mixinLabel}`);
94
+
95
+ // Render children recursively
96
+ function renderChildren(pname: string, depth: number) {
97
+ const children = getChildren(pname);
98
+ for (const child of children) {
99
+ const indent = " ".repeat(depth);
100
+ const childPrompt = current.find((p) => p.name === child);
101
+ const ver = childPrompt ? c.dim(` v${childPrompt.version}`) : "";
102
+ const childMixins = childPrompt?.mixins?.length
103
+ ? c.dim(` + ${childPrompt.mixins.join(", ")}`)
104
+ : "";
105
+ humanOut(`${indent}├── ${child}${ver}${childMixins}`);
106
+ renderChildren(child, depth + 1);
107
+ }
108
+ }
109
+
110
+ renderChildren(name, ancestors.length + 1);
111
+ }
112
+
113
+ export function registerTreeCommand(program: Command): void {
114
+ program
115
+ .command("tree")
116
+ .description("Show inheritance tree for a prompt")
117
+ .argument("<name>", "Prompt name")
118
+ .action(async (name: string) => {
119
+ const json: boolean = program.opts().json ?? false;
120
+ const args = [name, ...(json ? ["--json"] : [])];
121
+ await tree(args, json);
122
+ });
123
+ }
@@ -0,0 +1,330 @@
1
+ import { join } from "node:path";
2
+ import type { Command } from "commander";
3
+ import { c, errorOut, fmt, humanOut, jsonOut } from "../output.ts";
4
+ import { acquireLock, appendJsonl, dedupById, readJsonl, releaseLock } from "../store.ts";
5
+ import type { Prompt } from "../types.ts";
6
+ import { ExitError } from "../types.ts";
7
+
8
+ export default async function update(args: string[], json: boolean): Promise<void> {
9
+ const cwd = process.cwd();
10
+ const promptsPath = join(cwd, ".trellis", "prompts.jsonl");
11
+
12
+ if (args.includes("--help") || args.includes("-h")) {
13
+ humanOut(`Usage: tl update <name> [options]
14
+
15
+ Options:
16
+ --name <name> Rename prompt
17
+ --description <text> Update description
18
+ --section <name> --body <text> Update section body
19
+ --section <name>=<text> Update section (shorthand)
20
+ --add-section <name> --body <text> Add new section with body (repeatable)
21
+ --add-section <name>=<text> Add new section (shorthand, repeatable)
22
+ --remove-section <name> Remove section (empty body, repeatable)
23
+ --tag <tag> Add tag (repeatable)
24
+ --untag <tag> Remove tag (repeatable)
25
+ --schema <name> Assign schema
26
+ --extends <name> Change parent
27
+ --mixin <name> Add mixin (repeatable)
28
+ --remove-mixin <name> Remove mixin (repeatable)
29
+ --emit-as <filename> Custom emit filename
30
+ --emit-dir <path> Custom emit directory
31
+ --fm <key=value> Set frontmatter field (repeatable)
32
+ --remove-fm <key> Remove frontmatter field (repeatable)
33
+ --status draft|active|archived Change status
34
+ --json Output as JSON`);
35
+ return;
36
+ }
37
+
38
+ const nameArg = args.filter((a) => !a.startsWith("--"))[0];
39
+ if (!nameArg) {
40
+ if (json) {
41
+ jsonOut({ success: false, command: "update", error: "Prompt name required" });
42
+ } else {
43
+ errorOut("Usage: tl update <name> [options]");
44
+ }
45
+ throw new ExitError(1);
46
+ }
47
+
48
+ // Parse flags
49
+ let sectionName: string | undefined;
50
+ let sectionBody: string | undefined;
51
+ const addSections: Array<{ name: string; body: string }> = [];
52
+ const removeSections: string[] = [];
53
+ const addTags: string[] = [];
54
+ const removeTags: string[] = [];
55
+ let newDescription: string | undefined;
56
+ let newSchema: string | undefined;
57
+ let newExtends: string | undefined;
58
+ const addMixins: string[] = [];
59
+ const removeMixins: string[] = [];
60
+ let newEmitAs: string | undefined;
61
+ let newEmitDir: string | undefined;
62
+ let newStatus: string | undefined;
63
+ let newName: string | undefined;
64
+ const fmUpdates: Record<string, string> = {};
65
+ const fmRemovals: string[] = [];
66
+
67
+ for (let i = 0; i < args.length; i++) {
68
+ const arg = args[i];
69
+ if (arg === "--section" && args[i + 1]) {
70
+ const next = args[++i] ?? "";
71
+ const eqIdx = next.indexOf("=");
72
+ if (eqIdx !== -1) {
73
+ // --section name=body shorthand
74
+ sectionName = next.slice(0, eqIdx);
75
+ sectionBody = next.slice(eqIdx + 1);
76
+ } else {
77
+ sectionName = next;
78
+ }
79
+ } else if (arg === "--body" && args[i + 1] !== undefined) {
80
+ // body may be empty string
81
+ sectionBody = args[++i];
82
+ } else if (arg === "--add-section" && args[i + 1]) {
83
+ const next = args[++i] ?? "";
84
+ const eqIdx = next.indexOf("=");
85
+ if (eqIdx !== -1) {
86
+ // --add-section name=body shorthand
87
+ addSections.push({ name: next.slice(0, eqIdx), body: next.slice(eqIdx + 1) });
88
+ } else {
89
+ let body = "";
90
+ // lookahead for --body
91
+ if (args[i + 1] === "--body" && args[i + 2] !== undefined) {
92
+ i++; // skip --body
93
+ body = args[++i] ?? "";
94
+ }
95
+ addSections.push({ name: next, body });
96
+ }
97
+ } else if (arg === "--remove-section" && args[i + 1]) {
98
+ removeSections.push(args[++i] ?? "");
99
+ } else if (arg === "--tag" && args[i + 1]) {
100
+ addTags.push(args[++i] ?? "");
101
+ } else if (arg === "--untag" && args[i + 1]) {
102
+ removeTags.push(args[++i] ?? "");
103
+ } else if (arg === "--description" && args[i + 1]) {
104
+ newDescription = args[++i];
105
+ } else if (arg === "--schema" && args[i + 1]) {
106
+ newSchema = args[++i];
107
+ } else if (arg === "--extends" && args[i + 1]) {
108
+ newExtends = args[++i];
109
+ } else if (arg === "--mixin" && args[i + 1]) {
110
+ addMixins.push(args[++i] ?? "");
111
+ } else if (arg === "--remove-mixin" && args[i + 1]) {
112
+ removeMixins.push(args[++i] ?? "");
113
+ } else if (arg === "--emit-as" && args[i + 1]) {
114
+ newEmitAs = args[++i];
115
+ } else if (arg === "--emit-dir" && args[i + 1]) {
116
+ newEmitDir = args[++i];
117
+ } else if (arg === "--status" && args[i + 1]) {
118
+ newStatus = args[++i];
119
+ } else if (arg === "--name" && args[i + 1]) {
120
+ newName = args[++i];
121
+ } else if (arg === "--fm" && args[i + 1]) {
122
+ const fmArg = args[++i] ?? "";
123
+ const eqIdx = fmArg.indexOf("=");
124
+ if (eqIdx !== -1) {
125
+ fmUpdates[fmArg.slice(0, eqIdx)] = fmArg.slice(eqIdx + 1);
126
+ }
127
+ } else if (arg === "--remove-fm" && args[i + 1]) {
128
+ fmRemovals.push(args[++i] ?? "");
129
+ }
130
+ }
131
+
132
+ await acquireLock(promptsPath);
133
+ try {
134
+ const allRecords = await readJsonl<Prompt>(promptsPath);
135
+ const current = dedupById(allRecords);
136
+
137
+ const prompt = current.find((p) => p.name === nameArg);
138
+ if (!prompt) {
139
+ if (json) {
140
+ jsonOut({ success: false, command: "update", error: `Prompt '${nameArg}' not found` });
141
+ } else {
142
+ errorOut(`Prompt '${nameArg}' not found`);
143
+ }
144
+ throw new ExitError(1);
145
+ }
146
+
147
+ // Clone and apply mutations
148
+ const updated: Prompt = {
149
+ ...prompt,
150
+ sections: [...prompt.sections.map((s) => ({ ...s }))],
151
+ version: prompt.version + 1,
152
+ updatedAt: new Date().toISOString(),
153
+ };
154
+
155
+ // Update section body
156
+ if (sectionName !== undefined && sectionBody !== undefined) {
157
+ const idx = updated.sections.findIndex((s) => s.name === sectionName);
158
+ if (idx !== -1) {
159
+ const existing = updated.sections[idx];
160
+ if (existing) updated.sections[idx] = { ...existing, body: sectionBody };
161
+ } else {
162
+ updated.sections.push({ name: sectionName, body: sectionBody });
163
+ }
164
+ }
165
+
166
+ // Add new sections
167
+ for (const { name: addName, body: addBody } of addSections) {
168
+ const existingIdx = updated.sections.findIndex((s) => s.name === addName);
169
+ if (existingIdx !== -1) {
170
+ const existingSec = updated.sections[existingIdx];
171
+ if (existingSec) updated.sections[existingIdx] = { ...existingSec, body: addBody };
172
+ } else {
173
+ updated.sections.push({ name: addName, body: addBody });
174
+ }
175
+ }
176
+
177
+ // Remove sections
178
+ for (const removeName of removeSections) {
179
+ const idx = updated.sections.findIndex((s) => s.name === removeName);
180
+ if (updated.extends) {
181
+ // Inheriting prompt: use empty body to suppress inherited section in render
182
+ if (idx !== -1) {
183
+ const existing = updated.sections[idx];
184
+ if (existing) updated.sections[idx] = { ...existing, body: "" };
185
+ } else {
186
+ updated.sections.push({ name: removeName, body: "" });
187
+ }
188
+ } else {
189
+ // Non-inheriting prompt: splice section out entirely
190
+ if (idx !== -1) {
191
+ updated.sections.splice(idx, 1);
192
+ }
193
+ }
194
+ }
195
+
196
+ // Tags
197
+ const currentTags = new Set(updated.tags ?? []);
198
+ for (const t of addTags) currentTags.add(t);
199
+ for (const t of removeTags) currentTags.delete(t);
200
+ updated.tags = currentTags.size > 0 ? Array.from(currentTags) : undefined;
201
+
202
+ // Frontmatter updates
203
+ const currentFm = { ...(updated.frontmatter ?? {}) };
204
+ for (const [k, v] of Object.entries(fmUpdates)) currentFm[k] = v;
205
+ for (const k of fmRemovals) delete currentFm[k];
206
+ updated.frontmatter = Object.keys(currentFm).length > 0 ? currentFm : undefined;
207
+
208
+ // Mixins
209
+ if (addMixins.length > 0 || removeMixins.length > 0) {
210
+ const currentMixins = new Set(updated.mixins ?? []);
211
+ for (const m of addMixins) currentMixins.add(m);
212
+ for (const m of removeMixins) currentMixins.delete(m);
213
+ updated.mixins = currentMixins.size > 0 ? Array.from(currentMixins) : undefined;
214
+ }
215
+
216
+ if (newDescription !== undefined) updated.description = newDescription;
217
+ if (newSchema !== undefined) updated.schema = newSchema;
218
+ if (newExtends !== undefined) updated.extends = newExtends;
219
+ if (newEmitAs !== undefined) updated.emitAs = newEmitAs;
220
+ if (newEmitDir !== undefined) updated.emitDir = newEmitDir;
221
+ if (newStatus === "draft" || newStatus === "active" || newStatus === "archived") {
222
+ updated.status = newStatus;
223
+ }
224
+ if (newName !== undefined) updated.name = newName;
225
+
226
+ await appendJsonl(promptsPath, updated);
227
+
228
+ if (json) {
229
+ jsonOut({
230
+ success: true,
231
+ command: "update",
232
+ id: updated.id,
233
+ name: updated.name,
234
+ version: updated.version,
235
+ });
236
+ } else {
237
+ humanOut(`${fmt.success("Updated")} ${c.bold(updated.name)} → v${updated.version}`);
238
+ }
239
+ } finally {
240
+ releaseLock(promptsPath);
241
+ }
242
+ }
243
+
244
+ export function registerUpdateCommand(program: Command): void {
245
+ program
246
+ .command("update")
247
+ .description("Update a prompt (creates new version)")
248
+ .argument("<name>", "Prompt name")
249
+ .option("--name <name>", "Rename prompt")
250
+ .option("--description <text>", "Update description")
251
+ .option("--section <name>", "Section to update (use with --body or name=body shorthand)")
252
+ .option("--body <text>", "New body for the section specified by --section")
253
+ .option(
254
+ "--add-section <name>",
255
+ "Add a new section (use name=body shorthand, repeatable)",
256
+ (v: string, a: string[]) => a.concat([v]),
257
+ [] as string[],
258
+ )
259
+ .option(
260
+ "--remove-section <name>",
261
+ "Remove a section (sets body to empty, repeatable)",
262
+ (v: string, a: string[]) => a.concat([v]),
263
+ [] as string[],
264
+ )
265
+ .option(
266
+ "--tag <tag>",
267
+ "Add tag (repeatable)",
268
+ (v: string, a: string[]) => a.concat([v]),
269
+ [] as string[],
270
+ )
271
+ .option(
272
+ "--untag <tag>",
273
+ "Remove tag (repeatable)",
274
+ (v: string, a: string[]) => a.concat([v]),
275
+ [] as string[],
276
+ )
277
+ .option("--schema <name>", "Assign schema")
278
+ .option("--extends <name>", "Change parent prompt")
279
+ .option(
280
+ "--mixin <name>",
281
+ "Add mixin (repeatable)",
282
+ (v: string, a: string[]) => a.concat([v]),
283
+ [] as string[],
284
+ )
285
+ .option(
286
+ "--remove-mixin <name>",
287
+ "Remove mixin (repeatable)",
288
+ (v: string, a: string[]) => a.concat([v]),
289
+ [] as string[],
290
+ )
291
+ .option("--emit-as <filename>", "Custom emit filename")
292
+ .option("--emit-dir <path>", "Custom emit directory")
293
+ .option("--status <status>", "Change status (draft|active|archived)")
294
+ .option(
295
+ "--fm <key=value>",
296
+ "Set frontmatter field (repeatable)",
297
+ (v: string, a: string[]) => a.concat([v]),
298
+ [] as string[],
299
+ )
300
+ .option(
301
+ "--remove-fm <key>",
302
+ "Remove frontmatter field (repeatable)",
303
+ (v: string, a: string[]) => a.concat([v]),
304
+ [] as string[],
305
+ )
306
+ .action(async (nameArg: string, opts: Record<string, unknown>) => {
307
+ const json: boolean = program.opts().json ?? false;
308
+ const args: string[] = [nameArg];
309
+ if (opts.name) args.push("--name", opts.name as string);
310
+ if (opts.description) args.push("--description", opts.description as string);
311
+ if (opts.section) {
312
+ args.push("--section", opts.section as string);
313
+ if (opts.body !== undefined) args.push("--body", opts.body as string);
314
+ }
315
+ for (const sec of opts.addSection as string[]) args.push("--add-section", sec);
316
+ for (const sec of opts.removeSection as string[]) args.push("--remove-section", sec);
317
+ for (const tag of opts.tag as string[]) args.push("--tag", tag);
318
+ for (const tag of opts.untag as string[]) args.push("--untag", tag);
319
+ if (opts.schema) args.push("--schema", opts.schema as string);
320
+ if (opts.extends) args.push("--extends", opts.extends as string);
321
+ for (const mixin of opts.mixin as string[]) args.push("--mixin", mixin);
322
+ for (const mixin of opts.removeMixin as string[]) args.push("--remove-mixin", mixin);
323
+ if (opts.emitAs) args.push("--emit-as", opts.emitAs as string);
324
+ if (opts.emitDir) args.push("--emit-dir", opts.emitDir as string);
325
+ if (opts.status) args.push("--status", opts.status as string);
326
+ for (const fm of opts.fm as string[]) args.push("--fm", fm);
327
+ for (const key of opts.removeFm as string[]) args.push("--remove-fm", key);
328
+ await update(args, json);
329
+ });
330
+ }
@@ -0,0 +1,95 @@
1
+ import chalk from "chalk";
2
+ import type { Command } from "commander";
3
+ import { VERSION } from "../../../version.ts";
4
+ import { humanOut, jsonOut } from "../output.ts";
5
+
6
+ // trellis is bundled in @ag-eco/agentplate-cli; upgrading is `ap upgrade`.
7
+ const PACKAGE_NAME = "@ag-eco/agentplate-cli";
8
+
9
+ async function getCurrentVersion(): Promise<string> {
10
+ return VERSION;
11
+ }
12
+
13
+ async function fetchLatestVersion(): Promise<string> {
14
+ const res = await fetch(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`);
15
+ if (!res.ok) throw new Error(`Failed to fetch npm registry: ${res.status} ${res.statusText}`);
16
+ const data = (await res.json()) as { version: string };
17
+ return data.version;
18
+ }
19
+
20
+ export async function run(args: string[]): Promise<void> {
21
+ const jsonMode = args.includes("--json");
22
+ const checkOnly = args.includes("--check");
23
+
24
+ const [current, latest] = await Promise.all([getCurrentVersion(), fetchLatestVersion()]);
25
+ const upToDate = current === latest;
26
+
27
+ if (checkOnly) {
28
+ if (jsonMode) {
29
+ jsonOut({ success: true, command: "upgrade", current, latest, upToDate });
30
+ } else {
31
+ if (upToDate) {
32
+ humanOut(`${chalk.green("✔")} Already up to date (${current})`);
33
+ } else {
34
+ humanOut(`${chalk.yellow("!")} Update available: ${current} → ${latest}`);
35
+ process.exitCode = 1;
36
+ }
37
+ }
38
+ return;
39
+ }
40
+
41
+ if (upToDate) {
42
+ if (jsonMode) {
43
+ jsonOut({
44
+ success: true,
45
+ command: "upgrade",
46
+ current,
47
+ latest,
48
+ upToDate: true,
49
+ updated: false,
50
+ });
51
+ } else {
52
+ humanOut(`${chalk.green("✔")} Already up to date (${current})`);
53
+ }
54
+ return;
55
+ }
56
+
57
+ if (!jsonMode) {
58
+ humanOut(`Upgrading ${PACKAGE_NAME} from ${current} to ${latest}...`);
59
+ }
60
+
61
+ const result = Bun.spawnSync(["bun", "install", "-g", `${PACKAGE_NAME}@latest`], {
62
+ stdout: "inherit",
63
+ stderr: "inherit",
64
+ });
65
+
66
+ if (result.exitCode !== 0) {
67
+ throw new Error(`bun install failed with exit code ${result.exitCode}`);
68
+ }
69
+
70
+ if (jsonMode) {
71
+ jsonOut({
72
+ success: true,
73
+ command: "upgrade",
74
+ current,
75
+ latest,
76
+ upToDate: false,
77
+ updated: true,
78
+ });
79
+ } else {
80
+ humanOut(`${chalk.green("✔")} Upgraded to ${latest}`);
81
+ }
82
+ }
83
+
84
+ export function registerUpgradeCommand(program: Command): void {
85
+ program
86
+ .command("upgrade")
87
+ .description("Upgrade trellis to the latest version from npm")
88
+ .option("--check", "Check for updates without installing")
89
+ .action(async (opts: { check?: boolean }) => {
90
+ const args: string[] = [];
91
+ if (opts.check) args.push("--check");
92
+ if (program.opts().json) args.push("--json");
93
+ await run(args);
94
+ });
95
+ }