@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,81 @@
1
+ import { existsSync, mkdirSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import type { Command } from "commander";
4
+ import { saveConfig } from "../config.ts";
5
+ import { errorOut, humanOut, jsonOut } from "../output.ts";
6
+ import { ExitError } from "../types.ts";
7
+
8
+ export default async function init(_args: string[], json: boolean): Promise<void> {
9
+ const cwd = process.cwd();
10
+ const trellisDir = join(cwd, ".trellis");
11
+
12
+ if (_args.includes("--help") || _args.includes("-h")) {
13
+ humanOut(`Usage: tl init
14
+
15
+ Initializes .trellis/ in the current directory with config and empty JSONL stores.`);
16
+ return;
17
+ }
18
+
19
+ if (existsSync(trellisDir)) {
20
+ if (json) {
21
+ jsonOut({ success: false, command: "init", error: ".trellis/ already exists" });
22
+ } else {
23
+ errorOut(".trellis/ already exists");
24
+ }
25
+ throw new ExitError(1);
26
+ }
27
+
28
+ mkdirSync(trellisDir, { recursive: true });
29
+
30
+ // Write default config
31
+ await saveConfig(cwd, {
32
+ project: "trellis",
33
+ version: "1",
34
+ targets: {
35
+ default: { dir: "agents", default: true },
36
+ },
37
+ });
38
+
39
+ // Write .gitignore for .trellis/
40
+ await Bun.write(join(trellisDir, ".gitignore"), "*.lock\n");
41
+
42
+ // Create empty JSONL files
43
+ await Bun.write(join(trellisDir, "prompts.jsonl"), "");
44
+ await Bun.write(join(trellisDir, "schemas.jsonl"), "");
45
+
46
+ // Append .gitattributes to project root
47
+ const gitattrsPath = join(cwd, ".gitattributes");
48
+ const gitattrsEntry = ".trellis/prompts.jsonl merge=union\n.trellis/schemas.jsonl merge=union\n";
49
+
50
+ let existing = "";
51
+ try {
52
+ existing = await Bun.file(gitattrsPath).text();
53
+ } catch {
54
+ existing = "";
55
+ }
56
+
57
+ if (!existing.includes(".trellis/prompts.jsonl")) {
58
+ await Bun.write(gitattrsPath, existing + gitattrsEntry);
59
+ }
60
+
61
+ if (json) {
62
+ jsonOut({ success: true, command: "init", dir: trellisDir });
63
+ } else {
64
+ humanOut(`Initialized .trellis/ in ${cwd}`);
65
+ humanOut(" config.yaml: project=trellis, targets: default → agents/");
66
+ humanOut(" prompts.jsonl created");
67
+ humanOut(" schemas.jsonl created");
68
+ humanOut(" .gitattributes updated with merge=union");
69
+ }
70
+ }
71
+
72
+ export function registerInitCommand(program: Command): void {
73
+ program
74
+ .command("init")
75
+ .description("Initialize .trellis/ in current directory")
76
+ .action(async () => {
77
+ const json: boolean = program.opts().json ?? false;
78
+ const args = json ? ["--json"] : [];
79
+ await init(args, json);
80
+ });
81
+ }
@@ -0,0 +1,103 @@
1
+ import { join } from "node:path";
2
+ import type { Command } from "commander";
3
+ import { c, humanOut, jsonOut } from "../output.ts";
4
+ import { dedupById, readJsonl } from "../store.ts";
5
+ import type { Prompt } from "../types.ts";
6
+
7
+ export default async function list(args: string[], json: boolean): Promise<void> {
8
+ const cwd = process.cwd();
9
+ const promptsPath = join(cwd, ".trellis", "prompts.jsonl");
10
+
11
+ if (args.includes("--help") || args.includes("-h")) {
12
+ humanOut(`Usage: tl list [options]
13
+
14
+ Options:
15
+ --tag <tag> Filter by tag
16
+ --status <s> Filter by status (draft|active|archived)
17
+ --mixin <name> Filter by mixin
18
+ --json Output as JSON`);
19
+ return;
20
+ }
21
+
22
+ // Parse filters
23
+ let filterTag: string | undefined;
24
+ let filterStatus: string | undefined;
25
+ let filterExtends: string | undefined;
26
+ let filterMixin: string | undefined;
27
+
28
+ for (let i = 0; i < args.length; i++) {
29
+ if (args[i] === "--tag" && args[i + 1]) {
30
+ filterTag = args[++i];
31
+ } else if (args[i] === "--status" && args[i + 1]) {
32
+ filterStatus = args[++i];
33
+ } else if (args[i] === "--extends" && args[i + 1]) {
34
+ filterExtends = args[++i];
35
+ } else if (args[i] === "--mixin" && args[i + 1]) {
36
+ filterMixin = args[++i];
37
+ }
38
+ }
39
+
40
+ const allRecords = await readJsonl<Prompt>(promptsPath);
41
+ let prompts = dedupById(allRecords);
42
+
43
+ // Default: exclude archived unless explicitly requested
44
+ if (!filterStatus) {
45
+ prompts = prompts.filter((p) => p.status !== "archived");
46
+ } else {
47
+ prompts = prompts.filter((p) => p.status === filterStatus);
48
+ }
49
+
50
+ if (filterTag) {
51
+ prompts = prompts.filter((p) => p.tags?.includes(filterTag as string));
52
+ }
53
+
54
+ if (filterExtends) {
55
+ prompts = prompts.filter((p) => p.extends === filterExtends);
56
+ }
57
+
58
+ if (filterMixin) {
59
+ prompts = prompts.filter((p) => p.mixins?.includes(filterMixin as string));
60
+ }
61
+
62
+ if (json) {
63
+ jsonOut({ success: true, command: "list", prompts, count: prompts.length });
64
+ } else {
65
+ if (prompts.length === 0) {
66
+ humanOut("No prompts found.");
67
+ return;
68
+ }
69
+
70
+ for (const p of prompts) {
71
+ const tags = p.tags?.length ? c.dim(` [${p.tags.join(", ")}]`) : "";
72
+ const ext = p.extends ? c.dim(` → ${p.extends}`) : "";
73
+ const mix = p.mixins?.length ? c.dim(` + ${p.mixins.join(", ")}`) : "";
74
+ const pin = p.pinned !== undefined ? c.yellow(` (pinned @${p.pinned})`) : "";
75
+ humanOut(
76
+ `${c.bold(p.name)}${ext}${mix}${tags}${pin} ${c.dim(`v${p.version} · ${p.status} · ${p.id}`)}`,
77
+ );
78
+ }
79
+ humanOut(c.dim(`\n${prompts.length} prompt${prompts.length === 1 ? "" : "s"}`));
80
+ }
81
+ }
82
+
83
+ export function registerListCommand(program: Command): void {
84
+ program
85
+ .command("list")
86
+ .description("List prompts")
87
+ .option("--tag <tag>", "Filter by tag")
88
+ .option("--status <status>", "Filter by status (draft|active|archived)")
89
+ .option("--extends <name>", "Filter by parent prompt")
90
+ .option("--mixin <name>", "Filter by mixin")
91
+ .action(
92
+ async (options: { tag?: string; status?: string; extends?: string; mixin?: string }) => {
93
+ const json: boolean = program.opts().json ?? false;
94
+ const args: string[] = [];
95
+ if (options.tag) args.push("--tag", options.tag);
96
+ if (options.status) args.push("--status", options.status);
97
+ if (options.extends) args.push("--extends", options.extends);
98
+ if (options.mixin) args.push("--mixin", options.mixin);
99
+ if (json) args.push("--json");
100
+ await list(args, json);
101
+ },
102
+ );
103
+ }
@@ -0,0 +1,156 @@
1
+ import { existsSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import type { Command } from "commander";
4
+ import { detectStatus, replaceMarkerSection, VERSION_MARKER, wrapInMarkers } from "../markers.ts";
5
+ import { humanOut, isQuiet, jsonOut } from "../output.ts";
6
+
7
+ const CANDIDATE_FILES = ["CLAUDE.md", ".claude/CLAUDE.md", "AGENTS.md"] as const;
8
+
9
+ function onboardSnippet(): string {
10
+ return `## Prompt Management (Trellis)
11
+ ${VERSION_MARKER}
12
+
13
+ This project uses [Trellis](https://github.com/jayminwest/trellis) for git-native prompt management.
14
+
15
+ **At the start of every session**, run:
16
+ \`\`\`
17
+ tl prime
18
+ \`\`\`
19
+
20
+ This injects prompt workflow context: commands, conventions, and common workflows.
21
+
22
+ **Quick reference:**
23
+ - \`tl list\` — List all prompts
24
+ - \`tl render <name>\` — View rendered prompt (resolves inheritance)
25
+ - \`tl emit --all\` — Render prompts to files
26
+ - \`tl update <name>\` — Update a prompt (creates new version)
27
+ - \`tl sync\` — Stage and commit .trellis/ changes
28
+
29
+ **Do not manually edit emitted files.** Use \`tl update\` to modify prompts, then \`tl emit\` to regenerate.
30
+
31
+ **Loam metadata:** Prompts can declare expertise dependencies via \`loam.prime.domains\`, \`loam.prime.files\`, \`loam.budget\`, \`loam.on_empty\`, plus a top-level \`extends_loam\` flag (override-by-default; merge with parent when \`true\`). Trellis never shells out to \`ml\` — \`tl render --json\` surfaces the resolved declaration in a top-level \`loam\` field for consumers to act on. See SPEC.md "Loam Metadata".`;
32
+ }
33
+
34
+ function findTargetFile(projectRoot: string): string | null {
35
+ for (const candidate of CANDIDATE_FILES) {
36
+ const fullPath = join(projectRoot, candidate);
37
+ if (existsSync(fullPath)) {
38
+ return fullPath;
39
+ }
40
+ }
41
+ return null;
42
+ }
43
+
44
+ export default async function onboard(args: string[], json: boolean): Promise<void> {
45
+ if (args.includes("--help") || args.includes("-h")) {
46
+ humanOut(`Usage: tl onboard [options]
47
+
48
+ Adds a trellis section to CLAUDE.md (or creates it) so AI agents discover prompt workflows.
49
+
50
+ Options:
51
+ --check Report status without writing (missing, current, outdated)
52
+ --stdout Print snippet to stdout without writing to file
53
+ --json Output as JSON`);
54
+ return;
55
+ }
56
+
57
+ const stdoutMode = args.includes("--stdout");
58
+ const checkMode = args.includes("--check");
59
+ const cwd = process.cwd();
60
+
61
+ const targetPath = findTargetFile(cwd);
62
+ const snippet = onboardSnippet();
63
+
64
+ // --check mode: report status only
65
+ if (checkMode) {
66
+ if (!targetPath) {
67
+ if (json) {
68
+ jsonOut({ success: true, command: "onboard", status: "missing", file: null });
69
+ } else {
70
+ humanOut("Status: missing (no CLAUDE.md found)");
71
+ }
72
+ return;
73
+ }
74
+ const content = await Bun.file(targetPath).text();
75
+ const status = detectStatus(content);
76
+ if (json) {
77
+ jsonOut({ success: true, command: "onboard", status, file: targetPath });
78
+ } else {
79
+ humanOut(`Status: ${status} (${targetPath})`);
80
+ }
81
+ return;
82
+ }
83
+
84
+ // --stdout mode: print what would be written
85
+ if (stdoutMode) {
86
+ if (!isQuiet()) {
87
+ process.stdout.write(wrapInMarkers(snippet));
88
+ process.stdout.write("\n");
89
+ }
90
+ return;
91
+ }
92
+
93
+ // Default mode: write to file
94
+ const filePath = targetPath ?? join(cwd, "CLAUDE.md");
95
+ const fileExists = existsSync(filePath);
96
+ const wrappedSnippet = wrapInMarkers(snippet);
97
+
98
+ if (!fileExists) {
99
+ await Bun.write(filePath, `${wrappedSnippet}\n`);
100
+ if (json) {
101
+ jsonOut({ success: true, command: "onboard", action: "created", file: filePath });
102
+ } else {
103
+ humanOut(`Created ${filePath} with trellis section`);
104
+ }
105
+ return;
106
+ }
107
+
108
+ const content = await Bun.file(filePath).text();
109
+ const status = detectStatus(content);
110
+
111
+ if (status === "current") {
112
+ if (json) {
113
+ jsonOut({ success: true, command: "onboard", action: "unchanged", file: filePath });
114
+ } else {
115
+ humanOut("Trellis section is already up to date");
116
+ }
117
+ return;
118
+ }
119
+
120
+ if (status === "outdated") {
121
+ const updated = replaceMarkerSection(content, snippet);
122
+ if (updated) {
123
+ await Bun.write(filePath, updated);
124
+ if (json) {
125
+ jsonOut({ success: true, command: "onboard", action: "updated", file: filePath });
126
+ } else {
127
+ humanOut(`Updated trellis section in ${filePath}`);
128
+ }
129
+ }
130
+ return;
131
+ }
132
+
133
+ // status === "missing": append
134
+ const separator = content.endsWith("\n") ? "\n" : "\n\n";
135
+ await Bun.write(filePath, `${content}${separator}${wrappedSnippet}\n`);
136
+ if (json) {
137
+ jsonOut({ success: true, command: "onboard", action: "appended", file: filePath });
138
+ } else {
139
+ humanOut(`Added trellis section to ${filePath}`);
140
+ }
141
+ }
142
+
143
+ export function registerOnboardCommand(program: Command): void {
144
+ program
145
+ .command("onboard")
146
+ .description("Add trellis section to CLAUDE.md for AI agent discovery")
147
+ .option("--check", "Report status without writing (missing, current, outdated)")
148
+ .option("--stdout", "Print snippet to stdout without writing to file")
149
+ .action(async (opts: Record<string, unknown>) => {
150
+ const json: boolean = program.opts().json ?? false;
151
+ const args: string[] = [];
152
+ if (opts.check) args.push("--check");
153
+ if (opts.stdout) args.push("--stdout");
154
+ await onboard(args, json);
155
+ });
156
+ }
@@ -0,0 +1,172 @@
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 pin(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 pin <name>@<version> [options]
14
+
15
+ Options:
16
+ --json Output as JSON`);
17
+ return;
18
+ }
19
+
20
+ const nameArg = args.filter((a) => !a.startsWith("--"))[0];
21
+ if (!nameArg) {
22
+ if (json) {
23
+ jsonOut({ success: false, command: "pin", error: "Usage: tl pin <name>@<version>" });
24
+ } else {
25
+ errorOut("Usage: tl pin <name>@<version>");
26
+ }
27
+ throw new ExitError(1);
28
+ }
29
+
30
+ // Parse name@version
31
+ const atIdx = nameArg.lastIndexOf("@");
32
+ if (atIdx === -1) {
33
+ if (json) {
34
+ jsonOut({ success: false, command: "pin", error: "Usage: tl pin <name>@<version>" });
35
+ } else {
36
+ errorOut("Usage: tl pin <name>@<version>");
37
+ }
38
+ throw new ExitError(1);
39
+ }
40
+
41
+ const name = nameArg.slice(0, atIdx);
42
+ const version = Number.parseInt(nameArg.slice(atIdx + 1), 10);
43
+
44
+ if (Number.isNaN(version)) {
45
+ errorOut("Version must be an integer");
46
+ throw new ExitError(1);
47
+ }
48
+
49
+ await acquireLock(promptsPath);
50
+ try {
51
+ const allRecords = await readJsonl<Prompt>(promptsPath);
52
+ const current = dedupById(allRecords);
53
+
54
+ const prompt = current.find((p) => p.name === name);
55
+ if (!prompt) {
56
+ if (json) {
57
+ jsonOut({ success: false, command: "pin", error: `Prompt '${name}' not found` });
58
+ } else {
59
+ errorOut(`Prompt '${name}' not found`);
60
+ }
61
+ throw new ExitError(1);
62
+ }
63
+
64
+ // Verify the target version exists
65
+ const targetVersion = allRecords.find((p) => p.id === prompt.id && p.version === version);
66
+ if (!targetVersion) {
67
+ if (json) {
68
+ jsonOut({
69
+ success: false,
70
+ command: "pin",
71
+ error: `Version ${version} of '${name}' not found`,
72
+ });
73
+ } else {
74
+ errorOut(`Version ${version} of '${name}' not found`);
75
+ }
76
+ throw new ExitError(1);
77
+ }
78
+
79
+ const updated: Prompt = {
80
+ ...prompt,
81
+ pinned: version,
82
+ version: prompt.version + 1,
83
+ updatedAt: new Date().toISOString(),
84
+ };
85
+
86
+ await appendJsonl(promptsPath, updated);
87
+
88
+ if (json) {
89
+ jsonOut({ success: true, command: "pin", name, pinned: version });
90
+ } else {
91
+ humanOut(`${fmt.success("Pinned")} ${c.bold(name)} to v${version}`);
92
+ }
93
+ } finally {
94
+ releaseLock(promptsPath);
95
+ }
96
+ }
97
+
98
+ export async function defaultUnpin(args: string[], json: boolean): Promise<void> {
99
+ const cwd = process.cwd();
100
+ const promptsPath = join(cwd, ".trellis", "prompts.jsonl");
101
+
102
+ if (args.includes("--help") || args.includes("-h")) {
103
+ humanOut(`Usage: tl unpin <name> [options]
104
+
105
+ Options:
106
+ --json Output as JSON`);
107
+ return;
108
+ }
109
+
110
+ const name = args.filter((a) => !a.startsWith("--"))[0];
111
+ if (!name) {
112
+ if (json) {
113
+ jsonOut({ success: false, command: "unpin", error: "Prompt name required" });
114
+ } else {
115
+ errorOut("Usage: tl unpin <name>");
116
+ }
117
+ throw new ExitError(1);
118
+ }
119
+
120
+ await acquireLock(promptsPath);
121
+ try {
122
+ const allRecords = await readJsonl<Prompt>(promptsPath);
123
+ const current = dedupById(allRecords);
124
+
125
+ const prompt = current.find((p) => p.name === name);
126
+ if (!prompt) {
127
+ if (json) {
128
+ jsonOut({ success: false, command: "unpin", error: `Prompt '${name}' not found` });
129
+ } else {
130
+ errorOut(`Prompt '${name}' not found`);
131
+ }
132
+ throw new ExitError(1);
133
+ }
134
+
135
+ const updated: Prompt = {
136
+ ...prompt,
137
+ version: prompt.version + 1,
138
+ updatedAt: new Date().toISOString(),
139
+ };
140
+ updated.pinned = undefined;
141
+
142
+ await appendJsonl(promptsPath, updated);
143
+
144
+ if (json) {
145
+ jsonOut({ success: true, command: "unpin", name });
146
+ } else {
147
+ humanOut(`${fmt.success("Unpinned")} ${c.bold(name)}`);
148
+ }
149
+ } finally {
150
+ releaseLock(promptsPath);
151
+ }
152
+ }
153
+
154
+ export function registerPinCommand(program: Command): void {
155
+ program
156
+ .command("pin")
157
+ .description("Pin prompt to a specific version")
158
+ .argument("<name@version>", "Prompt name and version (e.g. my-prompt@2)")
159
+ .action(async (nameAtVersion: string) => {
160
+ const json: boolean = program.opts().json ?? false;
161
+ await pin([nameAtVersion], json);
162
+ });
163
+
164
+ program
165
+ .command("unpin")
166
+ .description("Remove version pin from a prompt")
167
+ .argument("<name>", "Prompt name")
168
+ .action(async (name: string) => {
169
+ const json: boolean = program.opts().json ?? false;
170
+ await defaultUnpin([name], json);
171
+ });
172
+ }
@@ -0,0 +1,193 @@
1
+ import { join } from "node:path";
2
+ import type { Command } from "commander";
3
+ import { isQuiet, jsonOut } from "../output.ts";
4
+
5
+ const PRIME_FILE = "PRIME.md";
6
+
7
+ function defaultPrimeContent(compact: boolean): string {
8
+ if (compact) {
9
+ return compactContent();
10
+ }
11
+ return fullContent();
12
+ }
13
+
14
+ function compactContent(): string {
15
+ return `# Trellis Quick Reference
16
+
17
+ \`\`\`
18
+ tl list # List all prompts
19
+ tl show <name> # View prompt record
20
+ tl render <name> # Render prompt (resolve inheritance)
21
+ tl emit <name> # Render and write to file
22
+ tl emit --all # Emit all active prompts
23
+ tl create --name "..." # Create a new prompt
24
+ tl update <name> # Update prompt (new version)
25
+ tl import <path> # Import existing .md as prompt
26
+ tl sync # Stage + commit .trellis/
27
+ \`\`\`
28
+
29
+ **Do not manually edit emitted files.** Use \`tl update\` to modify prompts.
30
+ `;
31
+ }
32
+
33
+ function fullContent(): string {
34
+ return `# Trellis Workflow Context
35
+
36
+ > **Context Recovery**: Run \`tl prime\` after compaction, clear, or new session
37
+
38
+ ## Core Rules
39
+ - **Storage**: Prompts live in \`.trellis/prompts.jsonl\` — never edit this file by hand
40
+ - **Emit files are generated**: Do NOT manually edit files in the emit directory; use \`tl update\` instead
41
+ - **Composition**: Prompts are composed via sections and inheritance, not duplicated
42
+ - **Git-native**: JSONL storage is diffable/mergeable; \`merge=union\` gitattribute handles branch merges
43
+
44
+ ## Essential Commands
45
+
46
+ ### Viewing Prompts
47
+ - \`tl list\` — List all prompts (name, version, status, tags)
48
+ - \`tl show <name>\` — Show full prompt record (sections, metadata)
49
+ - \`tl show <name>@<v>\` — Show specific version
50
+ - \`tl render <name>\` — Render full prompt (resolves inheritance chain)
51
+ - \`tl tree <name>\` — Show inheritance tree
52
+ - \`tl history <name>\` — Show version timeline
53
+ - \`tl diff <name> <v1> <v2>\` — Section-aware diff between versions
54
+
55
+ ### Creating & Updating
56
+ - \`tl create --name "..." --section "role:You are..." --tag agent\` — Create prompt
57
+ - \`tl update <name> --section "role:Updated content"\` — Update (creates new version)
58
+ - \`tl import <path>\` — Import existing .md file as a prompt
59
+ - \`tl archive <name>\` — Archive a prompt
60
+
61
+ ### Emitting
62
+ - \`tl emit <name>\` — Render and write prompt to a file
63
+ - \`tl emit --all\` — Emit all active prompts
64
+ - \`tl emit --check\` — Check if emitted files are up to date
65
+
66
+ ### Schemas & Validation
67
+ - \`tl schema create --name "..." --required "role,task"\` — Create validation schema
68
+ - \`tl validate <name>\` — Validate prompt against its schema
69
+ - \`tl validate --all\` — Validate all prompts with schemas
70
+
71
+ ### Versioning
72
+ - \`tl pin <name>@<version>\` — Pin prompt to a specific version
73
+ - \`tl unpin <name>\` — Remove version pin
74
+
75
+ ### Sync
76
+ - \`tl sync\` — Stage and commit .trellis/ changes
77
+
78
+ ## Loam Metadata
79
+
80
+ Prompts can declare loam (expertise) dependencies inline. Trellis stores the declaration as metadata and surfaces it in the render envelope — **trellis never shells out to \`ml\`**. Consumers (warren, overstory) read the resolved \`loam\` field and run \`ml prime\` themselves.
81
+
82
+ Schema fields on a prompt record:
83
+ - \`loam.prime.domains[]\` — domain names to prime
84
+ - \`loam.prime.files[]\` — file globs that scope priming
85
+ - \`loam.budget\` — non-negative number, consumer-defined units
86
+ - \`loam.on_empty\` — \`"skip"\` | \`"warn"\` | \`"error"\`
87
+ - \`extends_loam\` — boolean (default false): when true, child merges with parent's loam (union domains/files, last-wins budget/on_empty); when false, child wholesale overrides
88
+
89
+ \`tl render <name> --json\` includes a top-level \`loam\` field whenever any role in the chain declared one. The field is **omitted entirely** (not null, not {}) when no role declared loam — absent vs. empty is semantically distinct.
90
+
91
+ \`tl doctor\` runs an optional \`loam-domains\` check that warns on domains not declared in \`.loam/loam.config.yaml\`. The check is a typo guard, not a hard gate.
92
+
93
+ See SPEC.md "Loam Metadata" for full semantics.
94
+
95
+ ## Common Workflows
96
+
97
+ **Viewing what's available:**
98
+ \`\`\`bash
99
+ tl list # See all prompts
100
+ tl render <name> # See full rendered output
101
+ tl tree <name> # Understand inheritance
102
+ \`\`\`
103
+
104
+ **Creating a new prompt:**
105
+ \`\`\`bash
106
+ tl create --name "agent-v1" --section "role:You are a helpful assistant" --tag agent
107
+ tl emit agent-v1 # Write to agents/agent-v1.md
108
+ tl sync # Commit changes
109
+ \`\`\`
110
+
111
+ **Updating an existing prompt:**
112
+ \`\`\`bash
113
+ tl show <name> # Review current state
114
+ tl update <name> --section "role:Updated instructions"
115
+ tl emit <name> # Re-emit with changes
116
+ tl sync # Commit changes
117
+ \`\`\`
118
+
119
+ **Importing from an existing file:**
120
+ \`\`\`bash
121
+ tl import path/to/prompt.md # Import as trellis prompt
122
+ tl emit <name> # Verify emit output
123
+ \`\`\`
124
+ `;
125
+ }
126
+
127
+ export default async function prime(args: string[], json: boolean): Promise<void> {
128
+ if (args.includes("--help") || args.includes("-h")) {
129
+ if (!isQuiet()) {
130
+ process.stdout.write(`Usage: tl prime [options]
131
+
132
+ Outputs trellis workflow context for AI agent sessions.
133
+
134
+ Options:
135
+ --compact Output minimal quick-reference
136
+ --export Output default template (ignores custom PRIME.md)
137
+ --json Output as JSON
138
+ `);
139
+ }
140
+ return;
141
+ }
142
+
143
+ const compact = args.includes("--compact");
144
+ const exportMode = args.includes("--export");
145
+
146
+ // --export always outputs the default template
147
+ if (exportMode) {
148
+ const content = defaultPrimeContent(false);
149
+ if (json) {
150
+ jsonOut({ success: true, command: "prime", content });
151
+ } else if (!isQuiet()) {
152
+ process.stdout.write(content);
153
+ }
154
+ return;
155
+ }
156
+
157
+ // Try to find .trellis dir for custom PRIME.md
158
+ let content: string | null = null;
159
+ try {
160
+ const trellisDir = join(process.cwd(), ".trellis");
161
+ const customFile = Bun.file(join(trellisDir, PRIME_FILE));
162
+ if (await customFile.exists()) {
163
+ content = await customFile.text();
164
+ }
165
+ } catch {
166
+ // No .trellis dir — that's fine, use default
167
+ }
168
+
169
+ if (!content) {
170
+ content = defaultPrimeContent(compact);
171
+ }
172
+
173
+ if (json) {
174
+ jsonOut({ success: true, command: "prime", content });
175
+ } else if (!isQuiet()) {
176
+ process.stdout.write(content);
177
+ }
178
+ }
179
+
180
+ export function registerPrimeCommand(program: Command): void {
181
+ program
182
+ .command("prime")
183
+ .description("Output workflow context for AI agents")
184
+ .option("--compact", "Output minimal quick-reference")
185
+ .option("--export", "Output default template (ignores custom PRIME.md)")
186
+ .action(async (opts: Record<string, unknown>) => {
187
+ const json: boolean = program.opts().json ?? false;
188
+ const args: string[] = [];
189
+ if (opts.compact) args.push("--compact");
190
+ if (opts.export) args.push("--export");
191
+ await prime(args, json);
192
+ });
193
+ }