@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,365 @@
1
+ import { existsSync, mkdirSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import type { Command } from "commander";
4
+ import { loadConfig } from "../config.ts";
5
+ import { renderFrontmatter } from "../frontmatter.ts";
6
+ import { c, errorOut, fmt, humanOut, jsonOut } from "../output.ts";
7
+ import { resolvePrompt } from "../render.ts";
8
+ import { dedupById, readJsonl } from "../store.ts";
9
+ import type { Config, Prompt } from "../types.ts";
10
+ import { ExitError } from "../types.ts";
11
+
12
+ function formatSections(sections: { name: string; body: string }[]): string {
13
+ return sections.length === 1
14
+ ? sections.map((s) => s.body).join("")
15
+ : sections.map((s) => `## ${s.name}\n\n${s.body}`).join("\n\n");
16
+ }
17
+
18
+ function promptToMarkdown(
19
+ sections: { name: string; body: string }[],
20
+ frontmatter: Record<string, unknown>,
21
+ promptName: string,
22
+ promptDescription?: string,
23
+ ): string {
24
+ const combined: Record<string, unknown> = { name: promptName };
25
+ if (promptDescription) combined.description = promptDescription;
26
+ Object.assign(combined, frontmatter);
27
+
28
+ const fmBlock = renderFrontmatter(combined);
29
+ const sectionsMd = `${formatSections(sections)}\n`;
30
+ return fmBlock ? `${fmBlock}\n${sectionsMd}` : sectionsMd;
31
+ }
32
+
33
+ export function toExportName(name: string): string {
34
+ return name.toUpperCase().replace(/-/g, "_");
35
+ }
36
+
37
+ export function escapeTemplateLiteral(s: string): string {
38
+ return s.replace(/\\[`\\$]|`|\$\{|\\/g, (match) => {
39
+ // Already-escaped sequences: preserve as-is in the template literal
40
+ if (match === "\\`" || match === "\\\\" || match === "\\$") return match;
41
+ // Raw characters that need escaping for template literal safety
42
+ if (match === "`") return "\\`";
43
+ if (match === "${") return "\\${";
44
+ // Lone backslash (not followed by `, \, or $)
45
+ return "\\\\";
46
+ });
47
+ }
48
+
49
+ function promptToTypeScript(
50
+ sections: { name: string; body: string }[],
51
+ promptName: string,
52
+ promptDescription?: string,
53
+ ): string {
54
+ const exportName = toExportName(promptName);
55
+ const body = formatSections(sections);
56
+ const escaped = escapeTemplateLiteral(body);
57
+
58
+ const lines: string[] = [
59
+ "// Auto-generated by Trellis — do not edit manually",
60
+ `// Prompt: ${promptName}`,
61
+ ];
62
+ if (promptDescription) {
63
+ lines.push(`// ${promptDescription}`);
64
+ }
65
+ lines.push("");
66
+ lines.push(`export const ${exportName} = \`${escaped}\`;`);
67
+ lines.push("");
68
+ return lines.join("\n");
69
+ }
70
+
71
+ function renderPrompt(
72
+ filename: string,
73
+ sections: { name: string; body: string }[],
74
+ frontmatter: Record<string, unknown>,
75
+ promptName: string,
76
+ promptDescription?: string,
77
+ ): string {
78
+ if (filename.endsWith(".ts")) {
79
+ return promptToTypeScript(sections, promptName, promptDescription);
80
+ }
81
+ return promptToMarkdown(sections, frontmatter, promptName, promptDescription);
82
+ }
83
+
84
+ export function resolveEmitDir(prompt: Prompt, config: Config): string {
85
+ if (prompt.emitDir) return prompt.emitDir;
86
+ if (config.targets && prompt.tags) {
87
+ for (const tag of prompt.tags) {
88
+ for (const target of Object.values(config.targets)) {
89
+ if (target.tags?.includes(tag)) return target.dir;
90
+ }
91
+ }
92
+ }
93
+ if (config.targets) {
94
+ const defaultTarget = Object.values(config.targets).find((t) => t.default);
95
+ if (defaultTarget) return defaultTarget.dir;
96
+ }
97
+ return "agents";
98
+ }
99
+
100
+ async function emitPrompt(
101
+ prompt: Prompt,
102
+ outDir: string,
103
+ allPrompts: Prompt[],
104
+ force: boolean,
105
+ ): Promise<{ name: string; path: string; version: number; skipped?: boolean }> {
106
+ const pinnedVersion = prompt.pinned;
107
+ const result = resolvePrompt(prompt.name, allPrompts, pinnedVersion);
108
+ const filename = prompt.emitAs ?? `${prompt.name}.md`;
109
+ const outPath = join(outDir, filename);
110
+
111
+ if (!force && existsSync(outPath)) {
112
+ // Check if up to date
113
+ const existing = await Bun.file(outPath).text();
114
+ const content = renderPrompt(
115
+ filename,
116
+ result.sections,
117
+ result.frontmatter,
118
+ prompt.name,
119
+ prompt.description,
120
+ );
121
+ if (existing === content) {
122
+ return { name: prompt.name, path: outPath, version: result.version, skipped: true };
123
+ }
124
+ }
125
+
126
+ mkdirSync(dirname(outPath), { recursive: true });
127
+ await Bun.write(
128
+ outPath,
129
+ renderPrompt(filename, result.sections, result.frontmatter, prompt.name, prompt.description),
130
+ );
131
+
132
+ return { name: prompt.name, path: outPath, version: result.version };
133
+ }
134
+
135
+ export default async function emit(args: string[], json: boolean): Promise<void> {
136
+ const cwd = process.cwd();
137
+ const promptsPath = join(cwd, ".trellis", "prompts.jsonl");
138
+ const config = await loadConfig(cwd);
139
+
140
+ if (args.includes("--help") || args.includes("-h")) {
141
+ humanOut(`Usage: tl emit <name> [options]
142
+ tl emit --all [options]
143
+
144
+ Options:
145
+ --all Emit all active prompts
146
+ --check Check if emitted files are up to date
147
+ --out <path> Custom output path (single prompt)
148
+ --out-dir <path> Custom output directory (--all mode)
149
+ --force Overwrite even if unchanged
150
+ --dry-run Show what would be emitted
151
+ --json Output as JSON`);
152
+ return;
153
+ }
154
+
155
+ const force = args.includes("--force");
156
+ const dryRun = args.includes("--dry-run");
157
+ const checkMode = args.includes("--check");
158
+ const allMode = args.includes("--all") || checkMode;
159
+
160
+ const allRecords = await readJsonl<Prompt>(promptsPath);
161
+ const current = dedupById(allRecords);
162
+
163
+ // Get output directory override (applies to all prompts when set)
164
+ let outDir: string | undefined;
165
+ const outDirIdx = args.indexOf("--out-dir");
166
+ if (outDirIdx !== -1 && args[outDirIdx + 1]) {
167
+ outDir = args[outDirIdx + 1];
168
+ }
169
+
170
+ if (allMode) {
171
+ const activePrompts = current.filter((p) => p.status === "active");
172
+
173
+ if (dryRun) {
174
+ if (json) {
175
+ jsonOut({
176
+ success: true,
177
+ command: "emit",
178
+ dryRun: true,
179
+ files: activePrompts.map((p) => ({
180
+ name: p.name,
181
+ path: join(outDir ?? resolveEmitDir(p, config), p.emitAs ?? `${p.name}.md`),
182
+ version: p.version,
183
+ })),
184
+ });
185
+ } else {
186
+ if (outDir) {
187
+ humanOut(`Would emit ${activePrompts.length} prompts to ${outDir}/`);
188
+ } else {
189
+ humanOut(`Would emit ${activePrompts.length} prompts:`);
190
+ }
191
+ for (const p of activePrompts) {
192
+ const dir = outDir ?? resolveEmitDir(p, config);
193
+ humanOut(` ${p.name} → ${join(dir, p.emitAs ?? `${p.name}.md`)}`);
194
+ }
195
+ }
196
+ return;
197
+ }
198
+
199
+ if (checkMode) {
200
+ const stale: string[] = [];
201
+ for (const p of activePrompts) {
202
+ const filename = p.emitAs ?? `${p.name}.md`;
203
+ const promptOutDir = outDir ?? resolveEmitDir(p, config);
204
+ const outPath = join(promptOutDir, filename);
205
+ const result = resolvePrompt(p.name, allRecords, p.pinned);
206
+ const expected = renderPrompt(
207
+ filename,
208
+ result.sections,
209
+ result.frontmatter,
210
+ p.name,
211
+ p.description,
212
+ );
213
+
214
+ let actual = "";
215
+ try {
216
+ actual = await Bun.file(outPath).text();
217
+ } catch {
218
+ stale.push(p.name);
219
+ continue;
220
+ }
221
+
222
+ if (actual !== expected) {
223
+ stale.push(p.name);
224
+ }
225
+ }
226
+
227
+ if (json) {
228
+ jsonOut({
229
+ success: stale.length === 0,
230
+ command: "emit",
231
+ check: true,
232
+ stale,
233
+ upToDate: stale.length === 0,
234
+ });
235
+ } else {
236
+ if (stale.length === 0) {
237
+ humanOut(fmt.success("All emitted files are up to date"));
238
+ } else {
239
+ humanOut(fmt.error(`${stale.length} stale file(s)`));
240
+ for (const name of stale) {
241
+ humanOut(` - ${name}`);
242
+ }
243
+ }
244
+ }
245
+
246
+ if (stale.length > 0) throw new ExitError(1);
247
+ return;
248
+ }
249
+
250
+ const results = [];
251
+ for (const p of activePrompts) {
252
+ const r = await emitPrompt(p, outDir ?? resolveEmitDir(p, config), allRecords, force);
253
+ results.push(r);
254
+ }
255
+
256
+ if (json) {
257
+ jsonOut({ success: true, command: "emit", files: results });
258
+ } else {
259
+ for (const r of results) {
260
+ const status = r.skipped ? c.dim("(unchanged)") : c.green("✓");
261
+ humanOut(`${status} ${r.name} → ${r.path}`);
262
+ }
263
+ }
264
+ return;
265
+ }
266
+
267
+ // Single prompt emit
268
+ const nameArg = args.filter((a) => !a.startsWith("--"))[0];
269
+ if (!nameArg) {
270
+ if (json) {
271
+ jsonOut({ success: false, command: "emit", error: "Prompt name or --all required" });
272
+ } else {
273
+ errorOut("Usage: tl emit <name> [--out <path>] or tl emit --all");
274
+ }
275
+ throw new ExitError(1);
276
+ }
277
+
278
+ const prompt = current.find((p) => p.name === nameArg);
279
+ if (!prompt) {
280
+ if (json) {
281
+ jsonOut({ success: false, command: "emit", error: `Prompt '${nameArg}' not found` });
282
+ } else {
283
+ errorOut(`Prompt '${nameArg}' not found`);
284
+ }
285
+ throw new ExitError(1);
286
+ }
287
+
288
+ // Custom --out path
289
+ let outPath: string | undefined;
290
+ const outIdx = args.indexOf("--out");
291
+ if (outIdx !== -1 && args[outIdx + 1]) {
292
+ outPath = args[outIdx + 1];
293
+ }
294
+
295
+ const filename = prompt.emitAs ?? `${prompt.name}.md`;
296
+ const singleOutDir = outDir ?? resolveEmitDir(prompt, config);
297
+ const resolvedPath = outPath ?? join(singleOutDir, filename);
298
+
299
+ if (!force && existsSync(resolvedPath)) {
300
+ const result = resolvePrompt(prompt.name, allRecords, prompt.pinned);
301
+ const content = renderPrompt(
302
+ filename,
303
+ result.sections,
304
+ result.frontmatter,
305
+ prompt.name,
306
+ prompt.description,
307
+ );
308
+ const existing = await Bun.file(resolvedPath).text();
309
+ if (existing === content) {
310
+ if (json) {
311
+ jsonOut({
312
+ success: true,
313
+ command: "emit",
314
+ files: [
315
+ { name: prompt.name, path: resolvedPath, version: result.version, skipped: true },
316
+ ],
317
+ });
318
+ } else {
319
+ humanOut(c.dim(`(unchanged) ${prompt.name} → ${resolvedPath}`));
320
+ }
321
+ return;
322
+ }
323
+ }
324
+
325
+ const result = resolvePrompt(prompt.name, allRecords, prompt.pinned);
326
+ mkdirSync(dirname(resolvedPath), { recursive: true });
327
+ await Bun.write(
328
+ resolvedPath,
329
+ renderPrompt(filename, result.sections, result.frontmatter, prompt.name, prompt.description),
330
+ );
331
+
332
+ if (json) {
333
+ jsonOut({
334
+ success: true,
335
+ command: "emit",
336
+ files: [{ name: prompt.name, path: resolvedPath, version: result.version }],
337
+ });
338
+ } else {
339
+ humanOut(fmt.success(`${prompt.name} → ${resolvedPath}`));
340
+ }
341
+ }
342
+
343
+ export function registerEmitCommand(program: Command): void {
344
+ program
345
+ .command("emit [name]")
346
+ .description("Render and write prompt to a file")
347
+ .option("--all", "Emit all active prompts")
348
+ .option("--check", "Check if emitted files are up to date")
349
+ .option("--out <path>", "Custom output path (single prompt)")
350
+ .option("--out-dir <path>", "Custom output directory (--all mode)")
351
+ .option("--force", "Overwrite even if unchanged")
352
+ .option("--dry-run", "Show what would be emitted")
353
+ .action(async (name: string | undefined, opts: Record<string, unknown>) => {
354
+ const json: boolean = program.opts().json ?? false;
355
+ const args: string[] = [];
356
+ if (name) args.push(name);
357
+ if (opts.all) args.push("--all");
358
+ if (opts.check) args.push("--check");
359
+ if (opts.out) args.push("--out", opts.out as string);
360
+ if (opts.outDir) args.push("--out-dir", opts.outDir as string);
361
+ if (opts.force) args.push("--force");
362
+ if (opts.dryRun) args.push("--dry-run");
363
+ await emit(args, json);
364
+ });
365
+ }
@@ -0,0 +1,83 @@
1
+ import { join } from "node:path";
2
+ import type { Command } from "commander";
3
+ import { c, errorOut, humanOut, icons, jsonOut } from "../output.ts";
4
+ import { dedupById, getVersions, readJsonl } from "../store.ts";
5
+ import type { Prompt } from "../types.ts";
6
+ import { ExitError } from "../types.ts";
7
+
8
+ export default async function history(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 history <name> [options]
14
+
15
+ Options:
16
+ --limit <n> Max versions to show (default: 20)
17
+ --json Output as JSON`);
18
+ return;
19
+ }
20
+
21
+ const name = args.filter((a) => !a.startsWith("--"))[0];
22
+ if (!name) {
23
+ if (json) {
24
+ jsonOut({ success: false, command: "history", error: "Prompt name required" });
25
+ } else {
26
+ errorOut("Usage: tl history <name> [--limit <n>]");
27
+ }
28
+ throw new ExitError(1);
29
+ }
30
+
31
+ let limit = 20;
32
+ const limitIdx = args.indexOf("--limit");
33
+ if (limitIdx !== -1 && args[limitIdx + 1]) {
34
+ limit = Number.parseInt(args[limitIdx + 1] ?? "20", 10);
35
+ }
36
+
37
+ const allRecords = await readJsonl<Prompt>(promptsPath);
38
+
39
+ // Find the ID for this prompt name
40
+ const current = dedupById(allRecords);
41
+ const prompt = current.find((p) => p.name === name);
42
+ if (!prompt) {
43
+ if (json) {
44
+ jsonOut({ success: false, command: "history", error: `Prompt '${name}' not found` });
45
+ } else {
46
+ errorOut(`Prompt '${name}' not found`);
47
+ }
48
+ throw new ExitError(1);
49
+ }
50
+
51
+ const versions = getVersions(allRecords, prompt.id).reverse().slice(0, limit);
52
+
53
+ if (json) {
54
+ jsonOut({ success: true, command: "history", name, versions });
55
+ return;
56
+ }
57
+
58
+ humanOut(`${c.bold(name)} — version history (${versions.length} versions)`);
59
+ humanOut("");
60
+
61
+ for (const v of versions) {
62
+ const isCurrent = v.version === prompt.version;
63
+ const marker = isCurrent ? ` ${icons.active} current` : "";
64
+ const pinned = v.pinned !== undefined ? c.yellow(` (pinned @${v.pinned})`) : "";
65
+ humanOut(` ${c.bold(`v${v.version}`)}${marker}${pinned} ${c.dim(v.updatedAt)}`);
66
+ humanOut(` sections: ${v.sections.map((s) => s.name).join(", ") || c.dim("(none)")}`);
67
+ }
68
+ }
69
+
70
+ export function registerHistoryCommand(program: Command): void {
71
+ program
72
+ .command("history")
73
+ .description("Show version timeline for a prompt")
74
+ .argument("<name>", "Prompt name")
75
+ .option("--limit <n>", "Max versions to show (default: 20)")
76
+ .action(async (name: string, options: { limit?: string }) => {
77
+ const json: boolean = program.opts().json ?? false;
78
+ const args = [name];
79
+ if (options.limit) args.push("--limit", options.limit);
80
+ if (json) args.push("--json");
81
+ await history(args, json);
82
+ });
83
+ }
@@ -0,0 +1,198 @@
1
+ import { join } from "node:path";
2
+ import type { Command } from "commander";
3
+ import { loadConfig } from "../config.ts";
4
+ import { extractFrontmatter } from "../frontmatter.ts";
5
+ import { generateId } from "../id.ts";
6
+ import { c, errorOut, fmt, humanOut, jsonOut } from "../output.ts";
7
+ import { acquireLock, appendJsonl, dedupById, readJsonl, releaseLock } from "../store.ts";
8
+ import type { Prompt, Section } from "../types.ts";
9
+ import { ExitError } from "../types.ts";
10
+
11
+ function splitMarkdown(content: string): Section[] {
12
+ const sections: Section[] = [];
13
+ const lines = content.split("\n");
14
+ let currentName = "intro";
15
+ let currentLines: string[] = [];
16
+
17
+ function flush() {
18
+ const body = currentLines.join("\n").trim();
19
+ if (body || currentName !== "intro") {
20
+ sections.push({ name: currentName, body });
21
+ }
22
+ currentLines = [];
23
+ }
24
+
25
+ for (const line of lines) {
26
+ // Match ## headings
27
+ const headingMatch = line.match(/^##\s+(.+)$/);
28
+ if (headingMatch) {
29
+ flush();
30
+ const heading = headingMatch[1] ?? "";
31
+ currentName = heading;
32
+ } else {
33
+ currentLines.push(line);
34
+ }
35
+ }
36
+
37
+ flush();
38
+
39
+ return sections.filter((s) => s.body !== "");
40
+ }
41
+
42
+ export default async function importCmd(args: string[], json: boolean): Promise<void> {
43
+ const cwd = process.cwd();
44
+ const promptsPath = join(cwd, ".trellis", "prompts.jsonl");
45
+
46
+ if (args.includes("--help") || args.includes("-h")) {
47
+ humanOut(`Usage: tl import <path> --name <name> [options]
48
+
49
+ Options:
50
+ --name <name> Prompt name (required)
51
+ --no-split Import as single body section (default: split on ## headings)
52
+ --tag <tag> Add tag (repeatable)
53
+ --json Output as JSON`);
54
+ return;
55
+ }
56
+
57
+ const pathArg = args.filter((a) => !a.startsWith("--"))[0];
58
+ if (!pathArg) {
59
+ if (json) {
60
+ jsonOut({ success: false, command: "import", error: "File path required" });
61
+ } else {
62
+ errorOut("Usage: tl import <path> --name <name> [--no-split] [--tag <tag>]");
63
+ }
64
+ throw new ExitError(1);
65
+ }
66
+
67
+ let name = "";
68
+ let noSplit = false;
69
+ const tags: string[] = [];
70
+
71
+ for (let i = 0; i < args.length; i++) {
72
+ if (args[i] === "--name" && args[i + 1]) {
73
+ name = args[++i] ?? "";
74
+ } else if (args[i] === "--no-split") {
75
+ noSplit = true;
76
+ } else if (args[i] === "--tag" && args[i + 1]) {
77
+ tags.push(args[++i] ?? "");
78
+ }
79
+ }
80
+
81
+ if (!name) {
82
+ if (json) {
83
+ jsonOut({ success: false, command: "import", error: "--name is required" });
84
+ } else {
85
+ errorOut("--name is required");
86
+ }
87
+ throw new ExitError(1);
88
+ }
89
+
90
+ // Read input file
91
+ let content: string;
92
+ try {
93
+ content = await Bun.file(pathArg).text();
94
+ } catch {
95
+ if (json) {
96
+ jsonOut({ success: false, command: "import", error: `Cannot read file: ${pathArg}` });
97
+ } else {
98
+ errorOut(`Cannot read file: ${pathArg}`);
99
+ }
100
+ throw new ExitError(1);
101
+ }
102
+
103
+ // Extract frontmatter before splitting
104
+ const { metadata, body } = extractFrontmatter(content);
105
+
106
+ const sections: Section[] = noSplit ? [{ name: "body", body: body.trim() }] : splitMarkdown(body);
107
+
108
+ const config = await loadConfig(cwd);
109
+
110
+ await acquireLock(promptsPath);
111
+ try {
112
+ const allRecords = await readJsonl<Prompt>(promptsPath);
113
+ const current = dedupById(allRecords);
114
+
115
+ // Check for name collision
116
+ if (current.find((p) => p.name === name && p.status !== "archived")) {
117
+ if (json) {
118
+ jsonOut({
119
+ success: false,
120
+ command: "import",
121
+ error: `Prompt name '${name}' already exists`,
122
+ });
123
+ } else {
124
+ errorOut(`Prompt name '${name}' already exists`);
125
+ }
126
+ throw new ExitError(1);
127
+ }
128
+
129
+ const id = generateId(
130
+ config.project,
131
+ current.map((p) => p.id),
132
+ );
133
+ const now = new Date().toISOString();
134
+
135
+ const prompt: Prompt = {
136
+ id,
137
+ name,
138
+ version: 1,
139
+ sections,
140
+ status: "active",
141
+ createdAt: now,
142
+ updatedAt: now,
143
+ };
144
+
145
+ if (tags.length > 0) prompt.tags = tags;
146
+
147
+ // Map frontmatter description to prompt description
148
+ if (metadata.description && typeof metadata.description === "string") {
149
+ prompt.description = metadata.description;
150
+ }
151
+
152
+ // Store remaining frontmatter (excluding name/description which are Prompt fields)
153
+ const { name: _name, description: _desc, ...restFrontmatter } = metadata;
154
+ if (Object.keys(restFrontmatter).length > 0) {
155
+ prompt.frontmatter = restFrontmatter;
156
+ }
157
+
158
+ await appendJsonl(promptsPath, prompt);
159
+
160
+ if (json) {
161
+ jsonOut({
162
+ success: true,
163
+ command: "import",
164
+ id,
165
+ name,
166
+ sections: sections.length,
167
+ });
168
+ } else {
169
+ humanOut(
170
+ `${fmt.success("Imported")} ${c.bold(name)} ${fmt.id(id)} with ${sections.length} section(s)`,
171
+ );
172
+ }
173
+ } finally {
174
+ releaseLock(promptsPath);
175
+ }
176
+ }
177
+
178
+ export function registerImportCommand(program: Command): void {
179
+ program
180
+ .command("import")
181
+ .description("Import an existing .md file as a prompt")
182
+ .argument("<path>", "Path to the markdown file")
183
+ .requiredOption("--name <name>", "Prompt name")
184
+ .option("--no-split", "Import as single body section (default: split on ## headings)")
185
+ .option(
186
+ "--tag <tag>",
187
+ "Add tag (repeatable)",
188
+ (v: string, a: string[]) => a.concat([v]),
189
+ [] as string[],
190
+ )
191
+ .action(async (filePath: string, opts: Record<string, unknown>) => {
192
+ const json: boolean = program.opts().json ?? false;
193
+ const args: string[] = [filePath, "--name", opts.name as string];
194
+ if (!opts.split) args.push("--no-split");
195
+ for (const tag of opts.tag as string[]) args.push("--tag", tag);
196
+ await importCmd(args, json);
197
+ });
198
+ }