@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,79 @@
1
+ import type { Command } from "commander";
2
+ import { printError } from "../utils/palette.ts";
3
+
4
+ const SUPPORTED_SHELLS = ["bash", "zsh", "fish"] as const;
5
+ type Shell = (typeof SUPPORTED_SHELLS)[number];
6
+
7
+ function getVisibleCommands(program: Command): string[] {
8
+ const helper = program.createHelp();
9
+ return helper.visibleCommands(program).map((cmd) => cmd.name());
10
+ }
11
+
12
+ function getGlobalOptions(program: Command): string[] {
13
+ return program.options.map((opt) => opt.long).filter(Boolean) as string[];
14
+ }
15
+
16
+ function generateBash(commands: string[], globalOptions: string[]): string {
17
+ const allWords = [...commands, ...globalOptions].join(" ");
18
+ return `# loam bash completions
19
+ # Add to ~/.bashrc: eval "$(loam completions bash)"
20
+ _loam() {
21
+ local cur="\${COMP_WORDS[COMP_CWORD]}"
22
+ COMPREPLY=( $(compgen -W "${allWords}" -- "$cur") )
23
+ }
24
+ complete -F _loam loam
25
+ complete -F _loam lm
26
+ `;
27
+ }
28
+
29
+ function generateZsh(commands: string[], globalOptions: string[]): string {
30
+ const allWords = [...commands, ...globalOptions].join(" ");
31
+ return `# loam zsh completions
32
+ # Add to ~/.zshrc: eval "$(loam completions zsh)"
33
+ _loam() {
34
+ local -a words
35
+ words=(${allWords})
36
+ _describe 'loam commands' words
37
+ }
38
+ compdef _loam loam
39
+ compdef _loam lm
40
+ `;
41
+ }
42
+
43
+ function generateFish(commands: string[], _globalOptions: string[]): string {
44
+ const lines = ["# loam fish completions", "# Save to ~/.config/fish/completions/loam.fish", ""];
45
+ for (const cmd of commands) {
46
+ lines.push(`complete -c loam -n '__fish_use_subcommand' -a '${cmd}' -d '${cmd} command'`);
47
+ lines.push(`complete -c lm -n '__fish_use_subcommand' -a '${cmd}' -d '${cmd} command'`);
48
+ }
49
+ return `${lines.join("\n")}\n`;
50
+ }
51
+
52
+ export function registerCompletionsCommand(program: Command): void {
53
+ program
54
+ .command("completions <shell>")
55
+ .description("Output shell completion script (bash, zsh, fish)")
56
+ .action((shell: string) => {
57
+ const s = shell.toLowerCase();
58
+ if (!SUPPORTED_SHELLS.includes(s as Shell)) {
59
+ printError(`Unsupported shell: "${shell}". Supported: ${SUPPORTED_SHELLS.join(", ")}`);
60
+ process.exitCode = 1;
61
+ return;
62
+ }
63
+
64
+ const commands = getVisibleCommands(program);
65
+ const globalOptions = getGlobalOptions(program);
66
+
67
+ switch (s as Shell) {
68
+ case "bash":
69
+ process.stdout.write(generateBash(commands, globalOptions));
70
+ break;
71
+ case "zsh":
72
+ process.stdout.write(generateZsh(commands, globalOptions));
73
+ break;
74
+ case "fish":
75
+ process.stdout.write(generateFish(commands, globalOptions));
76
+ break;
77
+ }
78
+ });
79
+ }
@@ -0,0 +1,381 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { existsSync } from "node:fs";
3
+ import { rename, unlink, writeFile } from "node:fs/promises";
4
+ import Ajv, { type ErrorObject } from "ajv";
5
+ import type { Command } from "commander";
6
+ import yaml from "js-yaml";
7
+ import { configSchema } from "../schemas/config-schema.ts";
8
+ import { getConfigPath, readConfig } from "../utils/config.ts";
9
+ import { withFileLock } from "../utils/lock.ts";
10
+
11
+ export function registerConfigCommand(program: Command): void {
12
+ const config = program.command("config").description("Read and write .loam/loam.config.yaml");
13
+
14
+ config
15
+ .command("schema")
16
+ .description("Emit LoamConfig JSON Schema for warren and other config-UI consumers")
17
+ .action(() => {
18
+ process.stdout.write(`${JSON.stringify(configSchema, null, 2)}\n`);
19
+ });
20
+
21
+ config
22
+ .command("show")
23
+ .description(
24
+ "Emit the effective LoamConfig as JSON. Pass --path to read a single knob; falls back to the schema default when the knob is unset.",
25
+ )
26
+ .option(
27
+ "--path <path>",
28
+ "Dot-notation path to a single knob (e.g. governance.max_entries, search.boost_factor)",
29
+ )
30
+ .action(async (opts: { path?: string }) => {
31
+ let cfg: unknown;
32
+ try {
33
+ cfg = await readConfig();
34
+ } catch (err) {
35
+ process.stderr.write(`${(err as Error).message}\n`);
36
+ process.exitCode = 1;
37
+ return;
38
+ }
39
+ if (opts.path === undefined) {
40
+ process.stdout.write(`${JSON.stringify(cfg, null, 2)}\n`);
41
+ return;
42
+ }
43
+ const segments = opts.path.split(".").filter((s) => s.length > 0);
44
+ if (segments.length === 0) {
45
+ process.stderr.write("--path must not be empty.\n");
46
+ process.exitCode = 1;
47
+ return;
48
+ }
49
+ const fromConfig = walkConfig(cfg, segments);
50
+ const value = fromConfig !== undefined ? fromConfig : walkSchemaDefault(segments);
51
+ if (value === undefined) {
52
+ process.stderr.write(
53
+ `Path '${opts.path}' not found in config and has no schema default.\n`,
54
+ );
55
+ process.exitCode = 1;
56
+ return;
57
+ }
58
+ process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
59
+ });
60
+
61
+ config
62
+ .command("set")
63
+ .description(
64
+ "Set a config knob via dot-notation path. <value> is YAML-parsed (so booleans, numbers, lists, and objects all work uniformly). The full resulting config is validated against the schema before atomic write under a file lock; invalid values are rejected with a schema-referenced error. Last-writer-wins semantics under concurrent writes — re-read via `lm config show` after every write.",
65
+ )
66
+ .argument(
67
+ "<path>",
68
+ "Dot-notation path (e.g. governance.max_entries, search.boost_factor, domains.warren.allowed_types)",
69
+ )
70
+ .argument("<value>", "YAML-parsed value to set at <path>")
71
+ .action(async (path: string, value: string) => {
72
+ try {
73
+ await runConfigSet(path, value);
74
+ } catch (err) {
75
+ process.stderr.write(`${(err as Error).message}\n`);
76
+ process.exitCode = 1;
77
+ }
78
+ });
79
+
80
+ config
81
+ .command("unset")
82
+ .description(
83
+ "Remove a config knob via dot-notation path so subsequent reads fall back to the schema default. Empty parent objects along the unset path are pruned when the schema allows the parent to be omitted; required-field removals are rejected with a schema-referenced error (use `lm config set` to override). Idempotent — unsetting a never-set path is a silent no-op. Atomic write under a file lock.",
84
+ )
85
+ .argument(
86
+ "<path>",
87
+ "Dot-notation path (e.g. search.boost_factor, domains.warren, hooks.pre-record)",
88
+ )
89
+ .action(async (path: string) => {
90
+ try {
91
+ await runConfigUnset(path);
92
+ } catch (err) {
93
+ process.stderr.write(`${(err as Error).message}\n`);
94
+ process.exitCode = 1;
95
+ }
96
+ });
97
+ }
98
+
99
+ async function runConfigSet(rawPath: string, rawValue: string): Promise<void> {
100
+ const segments = rawPath.split(".").filter((s) => s.length > 0);
101
+ if (segments.length === 0) {
102
+ throw new Error("<path> must not be empty.");
103
+ }
104
+
105
+ const configPath = getConfigPath();
106
+ if (!existsSync(configPath)) {
107
+ throw new Error(
108
+ "No .loam/ directory found. Run `loam init` to set up this project before `lm config set`.",
109
+ );
110
+ }
111
+
112
+ // Reject paths that descend through a closed-shape boundary into an unknown
113
+ // key. AJV catches type/range mismatches at validate time; this gives a
114
+ // targeted error before write so `lm config set governance.typo 5` doesn't
115
+ // silently turn into "valid value at an unknown leaf".
116
+ validatePathInSchema(segments);
117
+
118
+ let parsedValue: unknown;
119
+ try {
120
+ parsedValue = yaml.load(rawValue);
121
+ } catch (err) {
122
+ throw new Error(`Invalid YAML for <value>: ${(err as Error).message}`);
123
+ }
124
+
125
+ await withFileLock(configPath, async () => {
126
+ const cfg = (await readConfig()) as unknown as Record<string, unknown>;
127
+ setAtPath(cfg, segments, parsedValue);
128
+
129
+ const ajv = new Ajv({ allErrors: true, strict: false });
130
+ const validate = ajv.compile(configSchema);
131
+ if (!validate(cfg)) {
132
+ const errs = validate.errors ?? [];
133
+ const lines = errs.map(formatAjvError);
134
+ throw new Error(`Invalid config after set:\n${lines.join("\n")}`);
135
+ }
136
+
137
+ const dumped = yaml.dump(cfg, { lineWidth: -1 });
138
+ await writeFileAtomic(configPath, dumped);
139
+ });
140
+ }
141
+
142
+ async function runConfigUnset(rawPath: string): Promise<void> {
143
+ const segments = rawPath.split(".").filter((s) => s.length > 0);
144
+ if (segments.length === 0) {
145
+ throw new Error("<path> must not be empty.");
146
+ }
147
+
148
+ const configPath = getConfigPath();
149
+ if (!existsSync(configPath)) {
150
+ throw new Error(
151
+ "No .loam/ directory found. Run `loam init` to set up this project before `lm config unset`.",
152
+ );
153
+ }
154
+
155
+ // Same closed-shape gate as `lm config set`: catches typos like
156
+ // `governance.typo` before we touch the file.
157
+ validatePathInSchema(segments);
158
+
159
+ await withFileLock(configPath, async () => {
160
+ const cfg = (await readConfig()) as unknown as Record<string, unknown>;
161
+ const changed = unsetAtPath(cfg, segments);
162
+ if (!changed) {
163
+ // Idempotent: the knob wasn't set, nothing to write.
164
+ return;
165
+ }
166
+
167
+ const ajv = new Ajv({ allErrors: true, strict: false });
168
+ const validate = ajv.compile(configSchema);
169
+ if (!validate(cfg)) {
170
+ const errs = validate.errors ?? [];
171
+ const lines = errs.map(formatAjvError);
172
+ throw new Error(`Invalid config after unset:\n${lines.join("\n")}`);
173
+ }
174
+
175
+ const dumped = yaml.dump(cfg, { lineWidth: -1 });
176
+ await writeFileAtomic(configPath, dumped);
177
+ });
178
+ }
179
+
180
+ function validatePathInSchema(segments: string[]): void {
181
+ let cur: unknown = configSchema;
182
+ for (let i = 0; i < segments.length; i++) {
183
+ const seg = segments[i] as string;
184
+ if (!cur || typeof cur !== "object") {
185
+ throw new Error(
186
+ `Path '${segments.slice(0, i + 1).join(".")}' descends past a leaf in the schema.`,
187
+ );
188
+ }
189
+ const node = cur as Record<string, unknown>;
190
+ const props = node.properties as Record<string, unknown> | undefined;
191
+ if (props && Object.hasOwn(props, seg)) {
192
+ cur = props[seg];
193
+ continue;
194
+ }
195
+ const additional = node.additionalProperties;
196
+ if (additional && typeof additional === "object") {
197
+ cur = additional;
198
+ continue;
199
+ }
200
+ const known = props ? Object.keys(props).join(", ") : "(none)";
201
+ const parent = segments.slice(0, i).join(".") || "<root>";
202
+ throw new Error(
203
+ `Unknown config path: '${segments.slice(0, i + 1).join(".")}' is not a known knob. Known keys at '${parent}': ${known}.`,
204
+ );
205
+ }
206
+ }
207
+
208
+ // Returns true if the leaf was actually present and removed. Prunes empty
209
+ // ancestor objects walking up the unset path, but stops at any ancestor whose
210
+ // key is in its parent schema node's `required` list — those must remain in
211
+ // the on-disk shape (validation catches the case where the leaf itself was
212
+ // required).
213
+ function unsetAtPath(cfg: Record<string, unknown>, segments: string[]): boolean {
214
+ const ancestors: Record<string, unknown>[] = [cfg];
215
+ for (let i = 0; i < segments.length - 1; i++) {
216
+ const seg = segments[i] as string;
217
+ const parent = ancestors[ancestors.length - 1] as Record<string, unknown>;
218
+ const next = parent[seg];
219
+ if (next === null || typeof next !== "object" || Array.isArray(next)) {
220
+ return false;
221
+ }
222
+ ancestors.push(next as Record<string, unknown>);
223
+ }
224
+ const leafKey = segments[segments.length - 1] as string;
225
+ const leafParent = ancestors[ancestors.length - 1] as Record<string, unknown>;
226
+ if (!Object.hasOwn(leafParent, leafKey)) {
227
+ return false;
228
+ }
229
+ delete leafParent[leafKey];
230
+
231
+ for (let i = ancestors.length - 1; i >= 1; i--) {
232
+ const node = ancestors[i] as Record<string, unknown>;
233
+ if (Object.keys(node).length > 0) break;
234
+ const parent = ancestors[i - 1] as Record<string, unknown>;
235
+ const keyInParent = segments[i - 1] as string;
236
+ if (isRequiredInSchema(segments.slice(0, i - 1), keyInParent)) break;
237
+ delete parent[keyInParent];
238
+ }
239
+ return true;
240
+ }
241
+
242
+ function isRequiredInSchema(parentSegments: string[], key: string): boolean {
243
+ let cur: unknown = configSchema;
244
+ for (const seg of parentSegments) {
245
+ if (!cur || typeof cur !== "object") return false;
246
+ const node = cur as Record<string, unknown>;
247
+ const props = node.properties as Record<string, unknown> | undefined;
248
+ if (props && Object.hasOwn(props, seg)) {
249
+ cur = props[seg];
250
+ continue;
251
+ }
252
+ const additional = node.additionalProperties;
253
+ if (additional && typeof additional === "object") {
254
+ cur = additional;
255
+ continue;
256
+ }
257
+ return false;
258
+ }
259
+ if (!cur || typeof cur !== "object") return false;
260
+ const required = (cur as { required?: unknown }).required;
261
+ return Array.isArray(required) && required.includes(key);
262
+ }
263
+
264
+ function setAtPath(obj: Record<string, unknown>, segments: string[], value: unknown): void {
265
+ let cur = obj;
266
+ for (let i = 0; i < segments.length - 1; i++) {
267
+ const seg = segments[i] as string;
268
+ const next = cur[seg];
269
+ if (next === null || typeof next !== "object" || Array.isArray(next)) {
270
+ cur[seg] = {};
271
+ }
272
+ cur = cur[seg] as Record<string, unknown>;
273
+ }
274
+ const leaf = segments[segments.length - 1] as string;
275
+ cur[leaf] = value;
276
+ }
277
+
278
+ function formatAjvError(err: ErrorObject): string {
279
+ const path = err.instancePath || "<root>";
280
+ const meta = lookupSchemaMeta(err.instancePath);
281
+ const tail = meta?.title ? ` (${meta.title})` : "";
282
+ return ` - ${path}: ${err.message ?? "(unknown)"}${tail}`;
283
+ }
284
+
285
+ function lookupSchemaMeta(
286
+ instancePath: string,
287
+ ): { title?: string; description?: string } | undefined {
288
+ if (!instancePath) return undefined;
289
+ const segs = instancePath
290
+ .split("/")
291
+ .slice(1)
292
+ .filter((s) => s.length > 0);
293
+ let cur: unknown = configSchema;
294
+ for (const seg of segs) {
295
+ if (!cur || typeof cur !== "object") return undefined;
296
+ const node = cur as Record<string, unknown>;
297
+ const props = node.properties as Record<string, unknown> | undefined;
298
+ if (props && Object.hasOwn(props, seg)) {
299
+ cur = props[seg];
300
+ continue;
301
+ }
302
+ const additional = node.additionalProperties;
303
+ if (additional && typeof additional === "object") {
304
+ cur = additional;
305
+ continue;
306
+ }
307
+ return undefined;
308
+ }
309
+ if (!cur || typeof cur !== "object") return undefined;
310
+ const node = cur as Record<string, unknown>;
311
+ return {
312
+ title: node.title as string | undefined,
313
+ description: node.description as string | undefined,
314
+ };
315
+ }
316
+
317
+ async function writeFileAtomic(filePath: string, content: string): Promise<void> {
318
+ const tmpPath = `${filePath}.tmp.${randomBytes(8).toString("hex")}`;
319
+ await writeFile(tmpPath, content, "utf-8");
320
+ try {
321
+ await rename(tmpPath, filePath);
322
+ } catch (err) {
323
+ try {
324
+ await unlink(tmpPath);
325
+ } catch {
326
+ // best-effort cleanup
327
+ }
328
+ throw err;
329
+ }
330
+ }
331
+
332
+ function walkConfig(value: unknown, segments: string[]): unknown {
333
+ let cur: unknown = value;
334
+ for (const seg of segments) {
335
+ if (cur === null || typeof cur !== "object") return undefined;
336
+ cur = (cur as Record<string, unknown>)[seg];
337
+ if (cur === undefined) return undefined;
338
+ }
339
+ return cur;
340
+ }
341
+
342
+ // Walk the JSON Schema down `segments` and synthesize a default for the
343
+ // resulting node. Leaves return their declared `default`; non-leaf nodes
344
+ // recurse into `properties` and collect child defaults so `--path search`
345
+ // (after `lm config unset search`) yields `{ boost_factor: 0.1 }` rather than
346
+ // "not found". Open maps (additionalProperties as an object) are followed when
347
+ // no matching `properties` entry exists, supporting paths like
348
+ // `domains.<name>.allowed_types`.
349
+ function walkSchemaDefault(segments: string[]): unknown {
350
+ let cur: unknown = configSchema;
351
+ for (const seg of segments) {
352
+ if (!cur || typeof cur !== "object") return undefined;
353
+ const node = cur as Record<string, unknown>;
354
+ const props = node.properties as Record<string, unknown> | undefined;
355
+ if (props && Object.hasOwn(props, seg)) {
356
+ cur = props[seg];
357
+ continue;
358
+ }
359
+ const additional = node.additionalProperties;
360
+ if (additional && typeof additional === "object") {
361
+ cur = additional;
362
+ continue;
363
+ }
364
+ return undefined;
365
+ }
366
+ return collectSchemaDefaults(cur);
367
+ }
368
+
369
+ function collectSchemaDefaults(node: unknown): unknown {
370
+ if (!node || typeof node !== "object") return undefined;
371
+ const n = node as Record<string, unknown>;
372
+ if ("default" in n) return n.default;
373
+ const props = n.properties as Record<string, unknown> | undefined;
374
+ if (!props) return undefined;
375
+ const result: Record<string, unknown> = {};
376
+ for (const [k, v] of Object.entries(props)) {
377
+ const sub = collectSchemaDefaults(v);
378
+ if (sub !== undefined) result[k] = sub;
379
+ }
380
+ return Object.keys(result).length > 0 ? result : undefined;
381
+ }
@@ -0,0 +1,121 @@
1
+ import { createInterface } from "node:readline";
2
+ import chalk from "chalk";
3
+ import type { Command } from "commander";
4
+ import { getExpertisePath, readConfig, removeDomain } from "../utils/config.ts";
5
+ import { readExpertiseFile } from "../utils/expertise.ts";
6
+ import { outputJson, outputJsonError } from "../utils/json-output.ts";
7
+ import { withFileLock } from "../utils/lock.ts";
8
+ import { accent, brand, isQuiet } from "../utils/palette.ts";
9
+
10
+ async function confirmAction(prompt: string): Promise<boolean> {
11
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
12
+ return new Promise((resolve) => {
13
+ rl.question(`${prompt} (y/N): `, (answer) => {
14
+ rl.close();
15
+ resolve(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
16
+ });
17
+ });
18
+ }
19
+
20
+ export function registerDeleteDomainCommand(program: Command): void {
21
+ program
22
+ .command("delete-domain")
23
+ .argument("<domain>", "expertise domain to delete")
24
+ .description("Delete an expertise domain and its expertise file")
25
+ .option("--yes", "skip confirmation prompt")
26
+ .option("--dry-run", "preview what would be deleted without making changes", false)
27
+ .action(async (domain: string, options: { yes?: boolean; dryRun: boolean }) => {
28
+ const jsonMode = program.opts().json === true;
29
+
30
+ try {
31
+ const config = await readConfig();
32
+
33
+ if (!(domain in config.domains)) {
34
+ if (jsonMode) {
35
+ outputJsonError(
36
+ "delete-domain",
37
+ `Domain "${domain}" not found in config. Available domains: ${Object.keys(config.domains).join(", ") || "(none)"}`,
38
+ );
39
+ } else {
40
+ console.error(chalk.red(`Error: domain "${domain}" not found in config.`));
41
+ console.error(
42
+ chalk.red(
43
+ `Hint: Run \`loam add ${domain}\` to create it, or check \`loam status\` for existing domains.`,
44
+ ),
45
+ );
46
+ }
47
+ process.exitCode = 1;
48
+ return;
49
+ }
50
+
51
+ const filePath = getExpertisePath(domain);
52
+ const records = await readExpertiseFile(filePath);
53
+ const recordCount = records.length;
54
+
55
+ if (options.dryRun) {
56
+ if (jsonMode) {
57
+ outputJson({
58
+ success: true,
59
+ command: "delete-domain",
60
+ domain,
61
+ dryRun: true,
62
+ recordCount,
63
+ });
64
+ } else {
65
+ if (!isQuiet()) {
66
+ console.log(
67
+ `${chalk.yellow("[DRY RUN]")} Would delete domain ${accent(domain)} (${recordCount} record${recordCount === 1 ? "" : "s"}) and its expertise file.`,
68
+ );
69
+ }
70
+ }
71
+ return;
72
+ }
73
+
74
+ // Confirmation (skip in JSON mode or when --yes is passed)
75
+ if (!jsonMode && !options.yes) {
76
+ const confirmed = await confirmAction(
77
+ `This will delete domain "${domain}" (${recordCount} record${recordCount === 1 ? "" : "s"}) and its expertise file. Continue?`,
78
+ );
79
+ if (!confirmed) {
80
+ console.log(chalk.yellow("Cancelled."));
81
+ return;
82
+ }
83
+ }
84
+
85
+ await withFileLock(filePath, async () => {
86
+ await removeDomain(domain, process.cwd());
87
+ });
88
+
89
+ if (jsonMode) {
90
+ outputJson({
91
+ success: true,
92
+ command: "delete-domain",
93
+ domain,
94
+ deletedFile: true,
95
+ recordCount,
96
+ });
97
+ } else {
98
+ if (!isQuiet()) {
99
+ console.log(
100
+ `${brand("✓")} ${brand("Removed domain")} ${accent(domain)} and deleted expertise file.`,
101
+ );
102
+ }
103
+ }
104
+ } catch (err) {
105
+ if ((err as NodeJS.ErrnoException).code === "ENOENT") {
106
+ if (jsonMode) {
107
+ outputJsonError("delete-domain", "No .loam/ directory found. Run `loam init` first.");
108
+ } else {
109
+ console.error(chalk.red("Error: No .loam/ directory found. Run `loam init` first."));
110
+ }
111
+ } else {
112
+ if (jsonMode) {
113
+ outputJsonError("delete-domain", (err as Error).message);
114
+ } else {
115
+ console.error(chalk.red(`Error: ${(err as Error).message}`));
116
+ }
117
+ }
118
+ process.exitCode = 1;
119
+ }
120
+ });
121
+ }