akm-cli 0.9.0-rc.8 → 0.9.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 (450) hide show
  1. package/CHANGELOG.md +1063 -44
  2. package/README.md +51 -25
  3. package/SECURITY.md +14 -1
  4. package/STABILITY.md +497 -0
  5. package/dist/akm +148 -35
  6. package/dist/{akm-migrate-storage → akm-migrate} +6 -9
  7. package/dist/assets/hints/cli-hints-full.md +223 -95
  8. package/dist/assets/hints/cli-hints-short.md +85 -22
  9. package/dist/assets/improve-strategies/default.json +1 -1
  10. package/dist/assets/improve-strategies/reflect-distill.json +1 -1
  11. package/dist/assets/prompts/memory-infer-user.md +2 -3
  12. package/dist/assets/stash-skeleton/README.md +6 -5
  13. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +2 -0
  14. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +2 -0
  15. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +2 -0
  16. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +2 -0
  17. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +2 -0
  18. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +2 -0
  19. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +2 -0
  20. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +2 -0
  21. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +2 -0
  22. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +2 -0
  23. package/dist/assets/stash-skeleton/facts/conventions/domains.md +2 -0
  24. package/dist/assets/stash-skeleton/facts/conventions/organization.md +20 -9
  25. package/dist/assets/tasks/core/extract.yml +1 -1
  26. package/dist/assets/tasks/core/version-check.yml +1 -1
  27. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +5 -0
  28. package/dist/assets/tasks/improve/akm-improve-catchup.yml +8 -0
  29. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +5 -0
  30. package/dist/assets/tasks/improve/akm-improve-frequent.yml +5 -0
  31. package/dist/assets/tasks/improve/akm-improve-nightly.yml +5 -0
  32. package/dist/assets/templates/html/health.html +1 -3
  33. package/dist/assets/workflows/workflow-template.md +32 -15
  34. package/dist/cli/invocation.js +40 -15
  35. package/dist/cli/parse-args.js +0 -22
  36. package/dist/cli/retired-commands.js +121 -0
  37. package/dist/cli/shared.js +154 -22
  38. package/dist/cli/unknown-flags.js +236 -0
  39. package/dist/cli-node.mjs +2 -1
  40. package/dist/cli.js +696 -258
  41. package/dist/commands/agent/agent-dispatch.js +14 -3
  42. package/dist/commands/agent/contribute-cli.js +73 -88
  43. package/dist/commands/completions.js +79 -22
  44. package/dist/commands/config-cli.js +17 -150
  45. package/dist/commands/env/env-cli.js +59 -143
  46. package/dist/commands/env/env.js +12 -163
  47. package/dist/commands/env/marker-path.js +6 -0
  48. package/dist/commands/env/secret-cli.js +36 -66
  49. package/dist/commands/env/secret.js +24 -57
  50. package/dist/commands/feedback-cli.js +141 -87
  51. package/dist/commands/health/accept-rate.js +58 -0
  52. package/dist/commands/health/advisories.js +3 -4
  53. package/dist/commands/health/checks.js +85 -23
  54. package/dist/commands/health/html-report.js +7 -10
  55. package/dist/commands/health/improve-metrics.js +25 -83
  56. package/dist/commands/health/md-report.js +5 -9
  57. package/dist/commands/health/metrics.js +62 -20
  58. package/dist/commands/health/renderers.js +47 -0
  59. package/dist/commands/health/report-view-model.js +4 -5
  60. package/dist/commands/health/stash-exposure.js +1 -1
  61. package/dist/commands/health/surfaces.js +3 -48
  62. package/dist/commands/health/task-runs.js +3 -67
  63. package/dist/commands/health/types-improve.js +7 -0
  64. package/dist/commands/health.js +99 -28
  65. package/dist/commands/improve/anti-collapse.js +2 -2
  66. package/dist/commands/improve/autonomy-gate.js +68 -0
  67. package/dist/commands/improve/collapse-detector.js +41 -40
  68. package/dist/commands/improve/consolidate/eligibility.js +1 -23
  69. package/dist/commands/improve/consolidate/merge.js +4 -0
  70. package/dist/commands/improve/consolidate.js +140 -1000
  71. package/dist/commands/improve/distill/promote-memory.js +12 -12
  72. package/dist/commands/improve/distill/quality-gate.js +6 -6
  73. package/dist/commands/improve/distill.js +58 -69
  74. package/dist/commands/improve/eligibility.js +105 -57
  75. package/dist/commands/improve/extract-cli.js +14 -133
  76. package/dist/commands/improve/improve-cli.js +98 -114
  77. package/dist/commands/improve/improve-result-file.js +1 -28
  78. package/dist/commands/improve/improve-strategies.js +8 -5
  79. package/dist/commands/improve/improve.js +128 -91
  80. package/dist/commands/improve/loop-stages.js +182 -20
  81. package/dist/commands/improve/memory/derived-ref.js +45 -43
  82. package/dist/commands/improve/memory/memory-belief.js +1 -1
  83. package/dist/commands/improve/memory/memory-contradiction-detect.js +4 -12
  84. package/dist/commands/improve/memory/memory-improve.js +6 -5
  85. package/dist/commands/improve/outcome-loop.js +22 -65
  86. package/dist/commands/improve/preparation.js +114 -123
  87. package/dist/commands/improve/proactive-maintenance.js +2 -5
  88. package/dist/commands/improve/reflect.js +56 -160
  89. package/dist/commands/improve/salience.js +11 -122
  90. package/dist/commands/improve/source-identity.js +10 -38
  91. package/dist/commands/lint/base-linter.js +20 -124
  92. package/dist/commands/lint/env-key-rules.js +31 -47
  93. package/dist/commands/lint/index.js +249 -43
  94. package/dist/commands/{events.js → log.js} +33 -38
  95. package/dist/commands/migrate-cli.js +92 -12
  96. package/dist/commands/migration-tool.js +46 -0
  97. package/dist/commands/observability-cli.js +70 -209
  98. package/dist/commands/proposal/drain.js +101 -29
  99. package/dist/commands/proposal/proposal-cli.js +76 -48
  100. package/dist/commands/proposal/proposal.js +54 -18
  101. package/dist/commands/proposal/propose-cli.js +88 -0
  102. package/dist/commands/proposal/propose.js +23 -15
  103. package/dist/commands/proposal/repository.js +701 -278
  104. package/dist/commands/proposal/validators/proposal-quality-validators.js +2 -8
  105. package/dist/commands/proposal/validators/proposal-validators.js +55 -7
  106. package/dist/commands/proposal/validators/proposals.js +4 -7
  107. package/dist/commands/read/curate.js +34 -53
  108. package/dist/commands/read/knowledge.js +150 -95
  109. package/dist/commands/read/registry-search.js +2 -2
  110. package/dist/commands/read/remember-cli.js +42 -15
  111. package/dist/commands/read/search-cli.js +180 -78
  112. package/dist/commands/read/search.js +58 -43
  113. package/dist/commands/read/show.js +197 -141
  114. package/dist/commands/registry-cli.js +12 -51
  115. package/dist/commands/remember.js +14 -57
  116. package/dist/commands/sources/add-cli.js +100 -31
  117. package/dist/commands/sources/bundle-cli.js +166 -0
  118. package/dist/commands/sources/bundle-config-ops.js +7 -2
  119. package/dist/commands/sources/info.js +18 -5
  120. package/dist/commands/sources/init.js +12 -12
  121. package/dist/commands/sources/installed-stashes.js +382 -98
  122. package/dist/commands/sources/schema-repair.js +3 -2
  123. package/dist/commands/sources/self-update.js +131 -38
  124. package/dist/commands/sources/source-add.js +72 -17
  125. package/dist/commands/sources/source-clone.js +129 -45
  126. package/dist/commands/sources/source-manage.js +43 -23
  127. package/dist/commands/sources/sources-cli.js +57 -208
  128. package/dist/commands/sources/stash-cli.js +46 -53
  129. package/dist/commands/tasks/tasks-cli.js +91 -97
  130. package/dist/commands/tasks/tasks.js +276 -421
  131. package/dist/commands/workflow-cli.js +175 -450
  132. package/dist/core/adapter/adapters/akm-adapter.js +47 -28
  133. package/dist/core/adapter/adapters/akm-lint.js +42 -27
  134. package/dist/core/adapter/adapters/akm-metadata.js +15 -44
  135. package/dist/core/adapter/adapters/akm-task-adapter.js +15 -13
  136. package/dist/core/adapter/adapters/akm-workflow-adapter.js +55 -71
  137. package/dist/core/adapter/adapters/dotenv-adapter.js +1 -1
  138. package/dist/core/adapter/adapters/generic-files-adapter.js +2 -0
  139. package/dist/core/adapter/adapters/index.js +6 -6
  140. package/dist/core/adapter/adapters/llm-wiki-adapter.js +14 -8
  141. package/dist/core/adapter/adapters/okf-adapter.js +187 -19
  142. package/dist/core/adapter/adapters/shared.js +3 -19
  143. package/dist/core/adapter/adapters/tool-dir-shared.js +8 -3
  144. package/dist/core/adapter/adapters/website-snapshot-adapter.js +1 -0
  145. package/dist/core/adapter/detect-adapter.js +17 -0
  146. package/dist/core/adapter/recognize-match.js +6 -4
  147. package/dist/core/adapter/validate-context.js +214 -0
  148. package/dist/core/asset/akm-markdown.js +63 -0
  149. package/dist/core/asset/asset-placement.js +20 -6
  150. package/dist/core/asset/asset-ref.js +11 -9
  151. package/dist/core/asset/frontmatter-lint.js +30 -0
  152. package/dist/core/asset/frontmatter.js +37 -9
  153. package/dist/core/asset/markdown.js +40 -51
  154. package/dist/core/asset/resolve-ref.js +89 -18
  155. package/dist/core/asset/stash-meta.js +1 -1
  156. package/dist/core/bundle-id.js +51 -0
  157. package/dist/core/common.js +152 -38
  158. package/dist/core/config/config-io.js +12 -1
  159. package/dist/core/config/config-schema.js +35 -8
  160. package/dist/core/config/config-sources.js +55 -11
  161. package/dist/core/config/config-walker.js +25 -9
  162. package/dist/core/config/config.js +9 -48
  163. package/dist/core/config/experimental.js +21 -0
  164. package/dist/core/config/schema/embedding.js +5 -1
  165. package/dist/core/config/schema/experimental.js +30 -0
  166. package/dist/core/config/schema/improve-processes.js +0 -6
  167. package/dist/core/config/schema/improve.js +21 -3
  168. package/dist/core/config/schema/index-config.js +8 -15
  169. package/dist/core/config/schema/output.js +4 -1
  170. package/dist/core/config/schema/setup.js +9 -18
  171. package/dist/core/config/schema/sources-bundles.js +49 -33
  172. package/dist/core/config/schema/workflow.js +3 -3
  173. package/dist/core/env-secret-ref.js +76 -46
  174. package/dist/core/errors.js +18 -12
  175. package/dist/core/events.js +46 -128
  176. package/dist/core/file-change.js +6 -5
  177. package/dist/core/fs-txn.js +83 -7
  178. package/dist/core/git-message.js +2 -2
  179. package/dist/core/improve-result.js +1 -100
  180. package/dist/core/lesson-lint.js +1 -17
  181. package/dist/core/logs-db.js +2 -1
  182. package/dist/core/migration-operation.js +16 -0
  183. package/dist/core/mutation-target.js +78 -0
  184. package/dist/core/parse.js +4 -1
  185. package/dist/core/paths.js +17 -20
  186. package/dist/core/recognition-util.js +12 -14
  187. package/dist/core/redaction.js +34 -0
  188. package/dist/core/standards/resolve-standards-context.js +2 -14
  189. package/dist/core/standards/resolve-stash-standards.js +2 -2
  190. package/dist/core/standards/resolve-type-conventions.js +2 -2
  191. package/dist/core/state/migrations.js +41 -18
  192. package/dist/core/state-db.js +5 -14
  193. package/dist/core/structured.js +1 -1
  194. package/dist/core/subprocess.js +6 -4
  195. package/dist/core/text-truncation.js +9 -5
  196. package/dist/core/type-presentation.js +3 -3
  197. package/dist/core/warn.js +0 -3
  198. package/dist/core/write-source.js +771 -95
  199. package/dist/indexer/bundle-identity-guard.js +3 -2
  200. package/dist/indexer/db/graph-db.js +0 -24
  201. package/dist/indexer/ensure-index.js +1 -0
  202. package/dist/indexer/graph/graph-boost.js +9 -34
  203. package/dist/indexer/graph/graph-extraction.js +8 -5
  204. package/dist/indexer/index-writer-lock.js +53 -17
  205. package/dist/indexer/index-written-assets.js +16 -22
  206. package/dist/indexer/indexer.js +497 -239
  207. package/dist/indexer/installations.js +14 -96
  208. package/dist/indexer/passes/dir-staleness.js +16 -9
  209. package/dist/indexer/passes/memory-inference.js +11 -9
  210. package/dist/indexer/passes/metadata.js +113 -47
  211. package/dist/indexer/scan/doc-to-entry.js +38 -1
  212. package/dist/indexer/scan/drain-dir.js +13 -23
  213. package/dist/indexer/search/db-search.js +99 -54
  214. package/dist/indexer/search/fts-query.js +47 -24
  215. package/dist/indexer/search/ranking-contributors.js +42 -20
  216. package/dist/indexer/search/ranking.js +18 -99
  217. package/dist/indexer/search/search-fields.js +7 -2
  218. package/dist/indexer/search/search-source.js +82 -93
  219. package/dist/indexer/usage/usage-events.js +0 -89
  220. package/dist/indexer/walk/file-context.js +2 -1
  221. package/dist/indexer/walk/matchers.js +30 -43
  222. package/dist/indexer/walk/path-resolver.js +7 -2
  223. package/dist/indexer/walk/walker.js +38 -12
  224. package/dist/integrations/agent/builders.js +0 -6
  225. package/dist/integrations/agent/config.js +2 -2
  226. package/dist/integrations/agent/detect.js +49 -19
  227. package/dist/integrations/agent/engine-fallback.js +76 -0
  228. package/dist/integrations/agent/profiles.js +14 -0
  229. package/dist/integrations/agent/prompts.js +12 -8
  230. package/dist/integrations/agent/runner-dispatch.js +4 -2
  231. package/dist/integrations/agent/runner.js +0 -1
  232. package/dist/integrations/agent/spawn.js +5 -6
  233. package/dist/integrations/github.js +1 -1
  234. package/dist/integrations/harnesses/aider/agent-builder.js +6 -4
  235. package/dist/integrations/harnesses/amazonq/agent-builder.js +7 -4
  236. package/dist/integrations/harnesses/claude/session-log.js +0 -10
  237. package/dist/integrations/harnesses/codex/agent-builder.js +5 -2
  238. package/dist/integrations/harnesses/copilot/agent-builder.js +5 -3
  239. package/dist/integrations/harnesses/gemini/agent-builder.js +5 -3
  240. package/dist/integrations/harnesses/index.js +3 -7
  241. package/dist/integrations/harnesses/opencode/agent-builder.js +21 -2
  242. package/dist/integrations/harnesses/opencode/session-log.js +0 -15
  243. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +13 -4
  244. package/dist/integrations/harnesses/openhands/agent-builder.js +9 -6
  245. package/dist/integrations/harnesses/pi/agent-builder.js +6 -4
  246. package/dist/integrations/lockfile.js +101 -6
  247. package/dist/integrations/session-logs/index.js +3 -28
  248. package/dist/llm/client.js +136 -100
  249. package/dist/llm/embedders/remote.js +13 -5
  250. package/dist/llm/feature-gate.js +4 -12
  251. package/dist/llm/graph-extract.js +5 -11
  252. package/dist/llm/memory-infer.js +144 -1
  253. package/dist/llm/metadata-enhance.js +5 -7
  254. package/dist/llm/structured-call.js +1 -1
  255. package/dist/llm/usage-persist.js +26 -5
  256. package/dist/llm/usage-telemetry.js +25 -2
  257. package/dist/output/cli-hints.js +1 -2
  258. package/dist/output/context.js +22 -7
  259. package/dist/output/format-exempt.js +80 -0
  260. package/dist/output/generic-render.js +259 -0
  261. package/dist/output/render-registry.js +57 -0
  262. package/dist/output/renderers.js +14 -36
  263. package/dist/output/shapes/curate.js +10 -1
  264. package/dist/output/shapes/events.js +12 -7
  265. package/dist/output/shapes/helpers.js +56 -83
  266. package/dist/output/shapes/migrate.js +8 -0
  267. package/dist/output/shapes/passthrough.js +7 -41
  268. package/dist/output/shapes/proposal/producer.js +15 -7
  269. package/dist/output/shapes.js +2 -9
  270. package/dist/output/text/{init.js → bundle-create.js} +3 -1
  271. package/dist/output/text/bundle-show.js +7 -0
  272. package/dist/output/text/command-format.js +164 -96
  273. package/dist/output/text/env.js +1 -3
  274. package/dist/output/text/events.js +8 -7
  275. package/dist/output/text/health-format.js +103 -0
  276. package/dist/output/text/health.js +7 -0
  277. package/dist/output/text/helpers.js +10 -8
  278. package/dist/output/text/lint-format.js +43 -0
  279. package/dist/output/text/{save.js → lint.js} +2 -2
  280. package/dist/output/text/migrate.js +88 -0
  281. package/dist/output/text/proposal/producer.js +4 -2
  282. package/dist/output/text/proposal-format.js +44 -72
  283. package/dist/output/text/registry-commands.js +1 -2
  284. package/dist/output/text/show-directives.js +15 -7
  285. package/dist/output/text/status-list.js +32 -0
  286. package/dist/output/text/sync.js +5 -0
  287. package/dist/output/text/workflow-format.js +24 -203
  288. package/dist/output/text/workflow.js +1 -7
  289. package/dist/output/text.js +16 -17
  290. package/dist/registry/factory.js +4 -6
  291. package/dist/registry/origin-resolve.js +16 -27
  292. package/dist/registry/providers/skills-sh.js +3 -3
  293. package/dist/registry/providers/static-index.js +13 -23
  294. package/dist/registry/resolve.js +42 -7
  295. package/dist/registry/semver.js +34 -84
  296. package/dist/runtime.js +2 -23
  297. package/dist/scripts/akm-migrate-node.js +60290 -0
  298. package/dist/scripts/akm-migrate.js +59628 -0
  299. package/dist/setup/detect.js +42 -15
  300. package/dist/setup/registry-stash-loader.js +2 -2
  301. package/dist/setup/setup.js +236 -136
  302. package/dist/setup/steps/connection.js +7 -9
  303. package/dist/setup/steps/platforms.js +9 -9
  304. package/dist/setup/steps/semantic.js +15 -3
  305. package/dist/setup/steps/sources.js +12 -13
  306. package/dist/setup/steps/stashdir.js +2 -3
  307. package/dist/setup/steps/tasks.js +237 -120
  308. package/dist/sources/freshness.js +1 -1
  309. package/dist/sources/provider-factory.js +11 -17
  310. package/dist/sources/providers/filesystem.js +2 -3
  311. package/dist/sources/providers/git-install.js +278 -34
  312. package/dist/sources/providers/git-provider.js +25 -23
  313. package/dist/sources/providers/git-stash.js +395 -106
  314. package/dist/sources/providers/git.js +2 -2
  315. package/dist/sources/providers/npm.js +16 -19
  316. package/dist/sources/providers/provider-utils.js +7 -4
  317. package/dist/sources/providers/sync-from-ref.js +3 -9
  318. package/dist/sources/providers/website.js +6 -1
  319. package/dist/sources/resolve.js +6 -5
  320. package/dist/sources/snapshot-fetchers/bluesky.js +146 -0
  321. package/dist/sources/snapshot-fetchers/content-extract.js +566 -0
  322. package/dist/sources/snapshot-fetchers/fetcher-util.js +41 -0
  323. package/dist/sources/snapshot-fetchers/github.js +100 -0
  324. package/dist/sources/snapshot-fetchers/host-guard.js +291 -0
  325. package/dist/sources/snapshot-fetchers/registry.js +17 -1
  326. package/dist/sources/snapshot-fetchers/robots.js +348 -0
  327. package/dist/sources/snapshot-fetchers/rss.js +282 -0
  328. package/dist/sources/snapshot-fetchers/secret-seam.js +42 -0
  329. package/dist/sources/snapshot-fetchers/website-ingest.js +566 -268
  330. package/dist/sources/snapshot-fetchers/x.js +910 -0
  331. package/dist/storage/database.js +7 -0
  332. package/dist/storage/engines/sqlite-migrations.js +23 -111
  333. package/dist/storage/managed-db.js +2 -2
  334. package/dist/storage/repositories/canaries-repository.js +1 -1
  335. package/dist/storage/repositories/events-repository.js +27 -11
  336. package/dist/storage/repositories/improve-runs-repository.js +6 -12
  337. package/dist/storage/repositories/index-connection.js +17 -6
  338. package/dist/storage/repositories/index-entries-repository.js +151 -240
  339. package/dist/storage/repositories/index-entry-mapper.js +15 -11
  340. package/dist/storage/repositories/index-fts-repository.js +5 -2
  341. package/dist/storage/repositories/index-llm-cache-repository.js +0 -1
  342. package/dist/storage/repositories/index-meta-repository.js +2 -3
  343. package/dist/storage/repositories/index-schema.js +10 -25
  344. package/dist/storage/repositories/index-utility-repository.js +15 -28
  345. package/dist/storage/repositories/index-vec-repository.js +6 -1
  346. package/dist/storage/repositories/outcome-repository.js +119 -0
  347. package/dist/storage/repositories/proposals-repository.js +296 -59
  348. package/dist/storage/repositories/registry-cache.js +19 -0
  349. package/dist/storage/repositories/salience-repository.js +172 -0
  350. package/dist/storage/repositories/task-history-repository.js +15 -13
  351. package/dist/storage/repositories/workflow-runs-repository.js +52 -40
  352. package/dist/tasks/backends/cron.js +105 -15
  353. package/dist/tasks/backends/index.js +1 -1
  354. package/dist/tasks/backends/launchd.js +85 -38
  355. package/dist/tasks/backends/schtasks.js +135 -15
  356. package/dist/tasks/embedded.js +56 -40
  357. package/dist/tasks/parser.js +7 -157
  358. package/dist/tasks/resolve-akm-bin.js +137 -59
  359. package/dist/tasks/runner.js +79 -42
  360. package/dist/tasks/scheduler-invocation.js +220 -10
  361. package/dist/tasks/schema.js +24 -1
  362. package/dist/tasks/task-id.js +1 -3
  363. package/dist/tasks/validator.js +20 -6
  364. package/dist/workflows/authoring/authoring.js +94 -143
  365. package/dist/workflows/authoring/scope-key.js +1 -1
  366. package/dist/workflows/exec/frozen-judge.js +28 -2
  367. package/dist/workflows/exec/native-executor.js +77 -57
  368. package/dist/workflows/exec/param-secrets.js +9 -9
  369. package/dist/workflows/exec/run-workflow.js +133 -79
  370. package/dist/workflows/exec/step-work.js +219 -346
  371. package/dist/{migrate-storage-node.mjs → workflows/exec/unit-dispatch.js} +1 -5
  372. package/dist/workflows/ir/compile.js +141 -270
  373. package/dist/workflows/ir/freeze.js +40 -30
  374. package/dist/workflows/ir/params.js +135 -11
  375. package/dist/workflows/ir/plan-hash.js +1 -1
  376. package/dist/workflows/ir/schema.js +25 -26
  377. package/dist/workflows/parser.js +872 -307
  378. package/dist/workflows/program/expressions.js +20 -208
  379. package/dist/workflows/program/schema.js +7 -10
  380. package/dist/workflows/renderer.js +95 -68
  381. package/dist/workflows/resource-limits.js +2 -0
  382. package/dist/workflows/runtime/checkin.js +3 -3
  383. package/dist/workflows/runtime/plan-classifier.js +16 -75
  384. package/dist/workflows/runtime/runs.js +186 -127
  385. package/dist/workflows/runtime/unit-checkin.js +1 -1
  386. package/dist/workflows/runtime/unit-phases.js +2 -2
  387. package/dist/workflows/runtime/workflow-asset-loader.js +232 -83
  388. package/dist/workflows/schema.js +1 -11
  389. package/dist/workflows/validate-summary.js +30 -36
  390. package/dist/workflows/validator.js +21 -62
  391. package/docs/README.md +68 -0
  392. package/docs/migration/README.md +8 -0
  393. package/docs/migration/release-notes/0.7.0.md +11 -11
  394. package/docs/migration/release-notes/0.9.0.md +208 -27
  395. package/docs/migration/v0.7-to-v0.8.md +46 -47
  396. package/docs/migration/v0.8-to-v0.9.md +564 -208
  397. package/docs/migration/v0.9.0-troubleshooting.md +561 -0
  398. package/docs/reference/README.md +12 -0
  399. package/docs/reference/cli.md +2253 -0
  400. package/docs/reference/configuration.md +358 -0
  401. package/docs/reference/data-and-telemetry.md +105 -42
  402. package/docs/reference/workflows.md +647 -0
  403. package/package.json +22 -11
  404. package/schemas/akm-asset-envelope.json +93 -0
  405. package/schemas/akm-config.json +81 -128
  406. package/schemas/akm-workflow.json +74 -73
  407. package/dist/assets/tasks/core/backup.yml +0 -5
  408. package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
  409. package/dist/cli/config-migrate.js +0 -1806
  410. package/dist/cli/config-validate.js +0 -41
  411. package/dist/commands/backup-cli.js +0 -56
  412. package/dist/commands/bundle/bundle-cli.js +0 -68
  413. package/dist/commands/bundle/bundle.js +0 -219
  414. package/dist/commands/graph/graph-cli.js +0 -124
  415. package/dist/commands/graph/graph.js +0 -489
  416. package/dist/commands/improve/extract-watch.js +0 -140
  417. package/dist/commands/mv-cli.js +0 -1221
  418. package/dist/commands/sources/history.js +0 -201
  419. package/dist/commands/tasks/default-tasks.js +0 -186
  420. package/dist/core/migration-backup.js +0 -1234
  421. package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -95
  422. package/dist/llm/memory-infer-impl.js +0 -138
  423. package/dist/migrate/legacy/config-source-migration.js +0 -223
  424. package/dist/migrate/legacy/content-migration.js +0 -305
  425. package/dist/migrate/legacy/legacy-layout.js +0 -779
  426. package/dist/migrate/legacy/legacy-paths.js +0 -25
  427. package/dist/migrate/legacy/legacy-stash-json.js +0 -72
  428. package/dist/migrate/legacy/proposal-fs-import.js +0 -168
  429. package/dist/migrate/legacy/task-target-ref-migration.js +0 -272
  430. package/dist/migrate/legacy/three-db-cutover.js +0 -841
  431. package/dist/migrate/legacy/workflow-migrations-bodies.js +0 -52
  432. package/dist/migrate/legacy/workflow-migrations-frozen.js +0 -21
  433. package/dist/migrate/legacy-ref-grammar.js +0 -214
  434. package/dist/output/shapes/distill.js +0 -14
  435. package/dist/output/shapes/history.js +0 -11
  436. package/dist/output/text/distill.js +0 -6
  437. package/dist/output/text/enable-disable.js +0 -8
  438. package/dist/output/text/history.js +0 -6
  439. package/dist/registry/build-index.js +0 -382
  440. package/dist/schemas/akm-config.json +0 -4704
  441. package/dist/schemas/akm-task.json +0 -87
  442. package/dist/schemas/akm-workflow.json +0 -372
  443. package/dist/scripts/migrate-storage.js +0 -3816
  444. package/dist/workflows/authoring/workflow-program-template.yaml +0 -31
  445. package/dist/workflows/cli.js +0 -53
  446. package/dist/workflows/exec/brief.js +0 -481
  447. package/dist/workflows/exec/report.js +0 -1460
  448. package/dist/workflows/exec/watch.js +0 -116
  449. package/dist/workflows/program/parser.js +0 -813
  450. package/dist/workflows/program/project.js +0 -104
@@ -11,14 +11,11 @@
11
11
  * `configVersion` is controlled by the config lifecycle. All execution
12
12
  * settings use their canonical engine/strategy paths; retired aliases are not
13
13
  * rewritten at this boundary.
14
- * - `parseConfigValue` returns a Partial<AkmConfig> so it can be merged with
15
- * the runtime config object via `mergeConfigValue`.
16
14
  */
17
15
  import { defineGroupCommand, defineJsonCommand, output } from "../cli/shared.js";
18
16
  import { resolveStashDir } from "../core/common.js";
19
17
  import { DEFAULT_CONFIG, loadConfig, mutateConfig } from "../core/config/config.js";
20
18
  import { configGet, configSet, configUnset, unknownKeyHint } from "../core/config/config-walker.js";
21
- import { UsageError } from "../core/errors.js";
22
19
  import { getCacheDir, getConfigPath, getDbPath, getDefaultStashDir } from "../core/paths.js";
23
20
  // ── Public API ──────────────────────────────────────────────────────────────
24
21
  export function getConfigValue(config, key) {
@@ -30,25 +27,6 @@ export function setConfigValue(config, key, rawValue) {
30
27
  export function unsetConfigValue(config, key) {
31
28
  return configUnset(config, key);
32
29
  }
33
- /**
34
- * Compatibility shim: returns a `Partial<AkmConfig>` containing just the
35
- * change. Older code merged this onto the live config — new code should call
36
- * `setConfigValue` directly (which returns the full merged config).
37
- */
38
- export function parseConfigValue(key, value) {
39
- // Use a "marker" base so we can detect which top-level fields actually got
40
- // touched by the set call. Anything still equal to the marker is untouched.
41
- const SENTINEL = Symbol("untouched");
42
- const base = { semanticSearchMode: SENTINEL };
43
- const next = setConfigValue(base, key, value);
44
- const patch = {};
45
- for (const k of Object.keys(next)) {
46
- if (next[k] !== SENTINEL) {
47
- patch[k] = next[k];
48
- }
49
- }
50
- return patch;
51
- }
52
30
  export function listConfig(config) {
53
31
  // 0.9.0 (spec §10.1): sources live in `bundles` (spread from config); the
54
32
  // retired top-level `sources[]` array is no longer surfaced.
@@ -71,63 +49,28 @@ function redactConfigValue(value) {
71
49
  export { unknownKeyHint };
72
50
  // ── `akm config` command surface ────────────────────────────────────────────
73
51
  // Extracted verbatim from src/cli.ts (WS6). The `main.subCommands.config` key
74
- // and every config subcommand's args/output shape are byte-identical. The
75
- // `skills.sh` toggle helpers and the `CONFIG_SUBCOMMAND_SET` routing constant
76
- // are used ONLY by this command, so they move with the cluster. Leaf handlers
77
- // whose body is a plain `runWithJsonErrors(() => { … })` are migrated to
78
- // `defineJsonCommand`, which emits the same JSON envelope (stdout/stderr/
79
- // exit-code) as the inline form.
80
- const SKILLS_SH_NAME = "skills.sh";
81
- const SKILLS_SH_URL = "https://skills.sh";
82
- const SKILLS_SH_PROVIDER = "skills-sh";
83
- function normalizeToggleTarget(target) {
84
- const normalized = target.trim().toLowerCase();
85
- if (normalized === "skills.sh" || normalized === "skills-sh")
86
- return "skills.sh";
87
- throw new UsageError(`Unsupported target "${target}". Supported targets: skills.sh`);
88
- }
89
- function toggleSkillsShRegistry(enabled) {
90
- let changed = false;
91
- mutateConfig((config) => {
92
- const registries = (config.registries ?? DEFAULT_CONFIG.registries ?? []).map((registry) => ({ ...registry }));
93
- const idx = registries.findIndex((registry) => registry.provider === SKILLS_SH_PROVIDER || registry.name === SKILLS_SH_NAME || registry.url === SKILLS_SH_URL);
94
- if (idx >= 0) {
95
- const target = registries[idx];
96
- const wasEnabled = target.enabled !== false;
97
- target.enabled = enabled;
98
- changed = wasEnabled !== enabled;
99
- return { ...config, registries };
100
- }
101
- registries.push({ url: SKILLS_SH_URL, name: SKILLS_SH_NAME, provider: SKILLS_SH_PROVIDER, enabled });
102
- changed = true;
103
- return { ...config, registries };
104
- });
105
- return { changed, component: SKILLS_SH_NAME, enabled };
106
- }
107
- function toggleComponent(targetRaw, enabled) {
108
- const target = normalizeToggleTarget(targetRaw);
109
- if (target === "skills.sh")
110
- return toggleSkillsShRegistry(enabled);
111
- // normalizeToggleTarget throws for any unsupported target; this is unreachable.
112
- throw new UsageError(`Unsupported target "${targetRaw}". Supported targets: skills.sh`);
113
- }
52
+ // and every config subcommand's args/output shape are byte-identical. Leaf
53
+ // handlers whose body is a plain `runWithJsonErrors(() => { … })` are
54
+ // migrated to `defineJsonCommand`, which emits the same JSON envelope
55
+ // (stdout/stderr/exit-code) as the inline form.
56
+ //
57
+ // `akm config enable|disable` (a hardcoded alias for toggling the skills.sh
58
+ // registry) was removed in 0.9.0 (C4). Use `akm registry add|remove`, the
59
+ // general mechanism, instead.
114
60
  export const configCommand = defineGroupCommand({
115
61
  meta: { name: "config", description: "Show and manage configuration" },
116
- args: {
117
- list: { type: "boolean", description: "List current configuration", default: false },
118
- },
119
62
  subCommands: {
120
63
  path: defineJsonCommand({
121
- meta: { name: "path", description: "Show paths to config, stash, cache, and index" },
64
+ meta: { name: "path", description: "Show paths to config, bundle, cache, and index" },
122
65
  args: {
123
- all: { type: "boolean", description: "Show all paths (config, stash, cache, index)", default: false },
66
+ all: { type: "boolean", description: "Show all paths (config, bundle, cache, index)", default: false },
124
67
  },
125
68
  run({ args }) {
126
69
  const configPath = getConfigPath();
127
70
  if (args.all) {
128
71
  let stashDir;
129
72
  try {
130
- stashDir = resolveStashDir({ readOnly: true });
73
+ stashDir = resolveStashDir();
131
74
  }
132
75
  catch {
133
76
  stashDir = `${getDefaultStashDir()} (not initialized)`;
@@ -135,7 +78,7 @@ export const configCommand = defineGroupCommand({
135
78
  const cacheDir = getCacheDir();
136
79
  const result = {
137
80
  config: configPath,
138
- stash: stashDir,
81
+ bundle: stashDir,
139
82
  cache: cacheDir,
140
83
  index: getDbPath(),
141
84
  };
@@ -152,16 +95,10 @@ export const configCommand = defineGroupCommand({
152
95
  output("config", listConfig(loadConfig()));
153
96
  },
154
97
  }),
155
- show: defineJsonCommand({
156
- meta: { name: "show", description: "Alias for `akm config list` — list current configuration" },
157
- run() {
158
- output("config", listConfig(loadConfig()));
159
- },
160
- }),
161
98
  get: defineJsonCommand({
162
99
  meta: { name: "get", description: "Get a configuration value by key" },
163
100
  args: {
164
- key: { type: "positional", required: true, description: "Config key (for example: embedding, stashDir)" },
101
+ key: { type: "positional", required: true, description: "Config key (for example: embedding, defaultBundle)" },
165
102
  },
166
103
  run({ args }) {
167
104
  output("config", getConfigValue(loadConfig(), args.key));
@@ -184,22 +121,8 @@ export const configCommand = defineGroupCommand({
184
121
  description: "Suppress the post-write config dump on stdout. Use from hooks and CI scripts; the write still happens and errors still print.",
185
122
  default: false,
186
123
  },
187
- // #463: explicit layer flag for forward-compat. User layer is the only
188
- // settable layer today; the flag exists so plugin authors can encode
189
- // intent and the surface stays stable if project-layer writes return.
190
- layer: {
191
- type: "string",
192
- description: "Config layer to write to. Currently only `user` is supported.",
193
- default: "user",
194
- },
195
124
  },
196
125
  run({ args }) {
197
- if (args.layer && args.layer !== "user") {
198
- throw new UsageError(`Unsupported --layer "${args.layer}". Only "user" is settable in 0.9.0.`, "INVALID_FLAG_VALUE");
199
- }
200
- // Use loadConfig (not loadUserConfig) so the project-config
201
- // deprecation warning fires consistently with `akm config get`
202
- // (#457). Effective merged shape is identical post-0.8.0.
203
126
  const updated = mutateConfig((current) => setConfigValue(current, args.key, args.value)).config;
204
127
  if (!args.silent) {
205
128
  output("config", listConfig(updated));
@@ -215,16 +138,8 @@ export const configCommand = defineGroupCommand({
215
138
  description: "Suppress the post-write config dump on stdout.",
216
139
  default: false,
217
140
  },
218
- layer: {
219
- type: "string",
220
- description: "Config layer to write to. Currently only `user` is supported.",
221
- default: "user",
222
- },
223
141
  },
224
142
  run({ args }) {
225
- if (args.layer && args.layer !== "user") {
226
- throw new UsageError(`Unsupported --layer "${args.layer}". Only "user" is settable in 0.9.0.`, "INVALID_FLAG_VALUE");
227
- }
228
143
  const result = mutateConfig((current) => unsetConfigValue(current, args.key), { absentNoop: true });
229
144
  const updated = result.config;
230
145
  if (!args.silent) {
@@ -232,60 +147,12 @@ export const configCommand = defineGroupCommand({
232
147
  }
233
148
  },
234
149
  }),
235
- validate: defineJsonCommand({
236
- meta: {
237
- name: "validate",
238
- description: "Validate the on-disk config file against the schema. Exits non-zero on errors.",
239
- },
240
- async run() {
241
- const { runConfigValidate } = await import("../cli/config-validate.js");
242
- await runConfigValidate();
243
- },
244
- }),
245
- migrate: defineJsonCommand({
246
- meta: {
247
- name: "migrate",
248
- description: "Compatibility alias for the canonical top-level migrate status/apply coordinator.",
249
- },
250
- args: {
251
- config: {
252
- type: "string",
253
- description: "Path to an operator-prepared current-version config to validate and install",
254
- },
255
- dryRun: { type: "boolean", default: false, description: "Validate and report without changing files" },
256
- },
257
- async run({ args }) {
258
- const { runConfigMigrate } = await import("../cli/config-migrate.js");
259
- await runConfigMigrate({ preparedConfigPath: args.config, dryRun: args.dryRun });
260
- },
261
- }),
262
- enable: defineJsonCommand({
263
- meta: { name: "enable", description: "Enable an optional component (skills.sh)" },
264
- args: {
265
- target: { type: "positional", description: "Component to enable (skills.sh)", required: true },
266
- },
267
- run({ args }) {
268
- const result = toggleComponent(args.target, true);
269
- output("enable", result);
270
- },
271
- }),
272
- disable: defineJsonCommand({
273
- meta: { name: "disable", description: "Disable an optional component (skills.sh)" },
274
- args: {
275
- target: { type: "positional", description: "Component to disable (skills.sh)", required: true },
276
- },
277
- run({ args }) {
278
- const result = toggleComponent(args.target, false);
279
- output("disable", result);
280
- },
281
- }),
282
150
  },
283
151
  // The bare `akm config` invocation (and `akm config --list`) dumps the
284
152
  // current config. defineGroupCommand short-circuits this body when a
285
153
  // registered subcommand ran, so the routing set stays derived from the
286
- // subCommands map and can never desync (previously validate/migrate were
287
- // missing from a hand-maintained set, causing a spurious second dump).
288
- defaultRun() {
289
- output("config", listConfig(loadConfig()));
290
- },
154
+ // subCommands map and can never desync.
155
+ // No `defaultRun`: bare `akm config` is a usage error (exit 2), the canonical
156
+ // bare-group behavior — owner ruling 12. Run `akm config list` for what the
157
+ // bare form used to print.
291
158
  });
@@ -25,16 +25,16 @@ import { defineGroupCommand, defineJsonCommand, output } from "../../cli/shared.
25
25
  import { deriveCanonicalAssetName } from "../../core/asset/asset-placement.js";
26
26
  import { writeFileAtomic } from "../../core/common.js";
27
27
  import { loadConfig } from "../../core/config/config.js";
28
- import { commitEnvSecretWrite, makeEnvRef, resolveEnvPath, resolveEnvWriteTarget, writeTargetDisplaySource, } from "../../core/env-secret-ref.js";
28
+ import { makeEnvRef, resolveEnvPath, resolveEnvWriteTarget, withEnvSecretWrite } from "../../core/env-secret-ref.js";
29
29
  import { ConfigError, NotFoundError, UsageError } from "../../core/errors.js";
30
- import { isQuiet } from "../../core/warn.js";
31
30
  import { resolveSourceEntries } from "../../indexer/search/search-source.js";
32
31
  import { readStdin } from "../../runtime.js";
33
32
  import { buildChildEnv } from "./child-env.js";
33
+ import { sensitiveMarkerPath } from "./marker-path.js";
34
34
  /**
35
35
  * Walk each stash's env files and return one entry per `.env` file, using the
36
36
  * env asset spec's canonical-name logic (e.g. `env/team/prod.env` →
37
- * `env:team/prod`, `env/team/.env` → `env:team/default`).
37
+ * `env/team/prod`, `env/team/.env` → `env/team/default`).
38
38
  */
39
39
  function listEnvsRecursive(listKeysFn) {
40
40
  const result = [];
@@ -57,7 +57,7 @@ function listEnvsRecursive(listKeysFn) {
57
57
  if (!canonical)
58
58
  continue;
59
59
  // Skip sensitive envs: a sibling .sensitive marker file suppresses listing.
60
- const markerPath = full.replace(/\.env$/, ".sensitive");
60
+ const markerPath = sensitiveMarkerPath(full, "env");
61
61
  if (fs.existsSync(markerPath))
62
62
  continue;
63
63
  const { keys } = listKeysFn(full);
@@ -69,7 +69,7 @@ function listEnvsRecursive(listKeysFn) {
69
69
  return result;
70
70
  }
71
71
  const envListCommand = defineJsonCommand({
72
- meta: { name: "list", description: "List all env files across all stashes with their key names (no values)" },
72
+ meta: { name: "list", description: "List all env files across all bundles with their key names (no values)" },
73
73
  async run() {
74
74
  const { listKeys } = await import("./env.js");
75
75
  output("env-list", { envs: listEnvsRecursive(listKeys) });
@@ -99,28 +99,27 @@ const envCreateCommand = defineJsonCommand({
99
99
  },
100
100
  target: {
101
101
  type: "string",
102
- description: "Override the write destination. Accepts a source name from your config; falls back to defaultWriteTarget then the working stash.",
102
+ description: "Override the write destination. Accepts a source name from your config; falls back to defaultWriteTarget then the working bundle.",
103
103
  },
104
104
  },
105
105
  async run({ args }) {
106
106
  const { createEnv, writeEnv } = await import("./env.js");
107
107
  // `create` always targets env/, never the frozen vaults/ copy. `--path` is
108
108
  // the subdirectory; `--target` selects the writable destination source.
109
- const { name, absPath, target } = resolveEnvWriteTarget(args.name, args.target, {
109
+ const { name, absPath, target, ref } = resolveEnvWriteTarget(args.name, args.target, {
110
110
  subPath: getStringArg(args, "path"),
111
111
  });
112
- const displaySource = writeTargetDisplaySource(target);
113
112
  const fromFile = args["from-file"];
114
113
  const fromStdin = args["from-stdin"] === true;
115
114
  if (fromFile !== undefined && fromStdin) {
116
115
  throw new UsageError("Pass only one of --from-file or --from-stdin.", "INVALID_FLAG_VALUE");
117
116
  }
117
+ let content;
118
118
  if (fromFile !== undefined || fromStdin) {
119
119
  // Ingest path: never silently clobber an existing env file.
120
120
  if (fs.existsSync(absPath)) {
121
- throw new UsageError(`Env "${makeEnvRef(name, displaySource)}" already exists. Remove it first (\`akm env remove\`) or edit the file directly.`, "RESOURCE_ALREADY_EXISTS");
121
+ throw new UsageError(`Env "${ref}" already exists. Remove it first (\`akm env remove\`) or edit the file directly.`, "RESOURCE_ALREADY_EXISTS");
122
122
  }
123
- let content;
124
123
  if (fromFile !== undefined) {
125
124
  if (!fs.existsSync(fromFile)) {
126
125
  throw new NotFoundError(`Source file not found: ${fromFile}`, "FILE_NOT_FOUND");
@@ -132,21 +131,24 @@ const envCreateCommand = defineJsonCommand({
132
131
  const buf = await readStdin(MAX_ENV_BYTES, () => new UsageError("Env file exceeds 1 MB limit.", "INVALID_FLAG_VALUE"));
133
132
  content = buf.toString("utf8");
134
133
  }
135
- writeEnv(absPath, content);
136
- }
137
- else {
138
- createEnv(absPath);
139
134
  }
140
135
  const written = [absPath];
136
+ const markerPath = sensitiveMarkerPath(absPath, "env");
141
137
  if (args.sensitive) {
142
- const markerPath = absPath.replace(/\.env$/, ".sensitive");
143
- if (!fs.existsSync(markerPath)) {
144
- fs.writeFileSync(markerPath, "", { mode: 0o600 });
145
- }
146
138
  written.push(markerPath);
147
139
  }
148
- commitEnvSecretWrite(target, { type: "env", name }, "Update", written);
149
- output("env-create", { ref: makeEnvRef(name, displaySource) });
140
+ withEnvSecretWrite(target, { type: "env", name }, "Update", written, () => {
141
+ if (content !== undefined)
142
+ writeEnv(absPath, content);
143
+ else
144
+ createEnv(absPath);
145
+ if (args.sensitive) {
146
+ if (!fs.existsSync(markerPath)) {
147
+ writeFileAtomic(markerPath, "", 0o600);
148
+ }
149
+ }
150
+ });
151
+ output("env-create", { ref });
150
152
  },
151
153
  });
152
154
  const envPathCommand = defineJsonCommand({
@@ -170,6 +172,22 @@ const envPathCommand = defineJsonCommand({
170
172
  process.stderr.write(`warning: this is the raw file path. Do NOT \`source\` it (shell substitutions in the file would execute).\n` +
171
173
  ` To inject values run: akm env run ${args.ref} -- <command>\n`);
172
174
  }
175
+ // F3/B3: this stdout write IS the payload — a bare absolute path for
176
+ // shell substitution (`$(akm env path <ref>)`, Docker `_FILE` /
177
+ // `--env-file`) — not a field inside a result envelope. `env path` is
178
+ // now declared format-exempt (src/output/format-exempt.ts, same
179
+ // classification as `env run`/`secret run`/`help`), so `--format`
180
+ // WARNS rather than doing anything to this write (src/cli.ts's startup
181
+ // `isFormatExemptCommand` check). An earlier version of this fix routed
182
+ // this through `output()` with a `{ path }` envelope so `--format`
183
+ // "worked" — but the CLI's process-wide default format is `json`, so a
184
+ // bare `akm env path <ref>` (exactly how `$(akm env path foo)` is
185
+ // written, with no explicit `--format`) started emitting
186
+ // `{"path":"..."}` instead of the raw path, breaking every existing
187
+ // shell substitution silently. Declaring the exemption is the correct
188
+ // fix: it keeps this byte-identical to history and makes the
189
+ // already-broken combination (`--format` + this command) loud instead
190
+ // of silent, per STABILITY.md's promise for exempt commands.
173
191
  process.stdout.write(`${absPath}\n`);
174
192
  },
175
193
  });
@@ -225,7 +243,7 @@ async function runEnvInjected(target, opts) {
225
243
  }
226
244
  if (baseExists) {
227
245
  throw new UsageError(`'akm env run' injects the whole file; the single-key '<ref>/${maybeKey}' form was removed.\n` +
228
- ` For one value use a secret: \`akm secret run secret:${maybeKey} ${maybeKey} -- <command>\`.`, "INVALID_FLAG_VALUE");
246
+ ` For one value use a secret: \`akm secret run secrets/${maybeKey} ${maybeKey} -- <command>\`.`, "INVALID_FLAG_VALUE");
229
247
  }
230
248
  }
231
249
  }
@@ -263,7 +281,15 @@ async function runEnvInjected(target, opts) {
263
281
  }
264
282
  throw err;
265
283
  }
266
- process.exit(result.status ?? 0);
284
+ // R-067: was `process.exit(result.status ?? 0)`, which terminates the
285
+ // process synchronously and skips the `finally { await
286
+ // disposeDispatchResources(); }` block in src/cli.ts's `runCommand` — even
287
+ // on the success path, since this call was unconditional. Setting
288
+ // `process.exitCode` and returning lets cleanup run while still exiting
289
+ // with the child's exact status once the event loop drains, matching the
290
+ // pattern `emitJsonError` (src/cli/shared.ts) already established.
291
+ process.exitCode = result.status ?? 0;
292
+ return;
267
293
  }
268
294
  /** Parse a comma/space-separated key list flag into a trimmed, non-empty array. */
269
295
  function parseKeyListFlag(raw) {
@@ -280,7 +306,7 @@ const envRunCommand = defineJsonCommand({
280
306
  name: "run",
281
307
  description:
282
308
  // biome-ignore lint/suspicious/noTemplateCurlyInString: literal `${secret:NAME}` token syntax documented for users, not interpolation
283
- "Run a command with the env file injected into its environment: `akm env run <ref> -- <command>`. Use `-- $SHELL` for an interactive session. Restrict which variables are injected with --only / --except. Values may embed `${secret:NAME}` tokens, replaced at run time with the sibling `secret:NAME` value from the same stash. Pass --clean to start the child with a minimal inherited environment instead of the full parent environment.",
309
+ "Run a command with the env file injected into its environment: `akm env run <ref> -- <command>`. Use `-- $SHELL` for an interactive session. Restrict which variables are injected with --only / --except. Values may embed `${secret:NAME}` tokens, replaced at run time with the sibling `${secret:NAME}` value from the same bundle. Pass --clean to start the child with a minimal inherited environment instead of the full parent environment.",
284
310
  },
285
311
  args: {
286
312
  target: { type: "positional", description: "Env ref", required: true },
@@ -315,12 +341,11 @@ const envRemoveCommand = defineJsonCommand({
315
341
  yes: { type: "boolean", alias: "y", description: "Skip confirmation prompt", default: false },
316
342
  target: {
317
343
  type: "string",
318
- description: "Override the write destination. Accepts a source name from your config; falls back to defaultWriteTarget then the working stash.",
344
+ description: "Override the write destination. Accepts a source name from your config; falls back to defaultWriteTarget then the working bundle.",
319
345
  },
320
346
  },
321
347
  async run({ args }) {
322
- const { name, absPath, target } = resolveEnvWriteTarget(args.ref, args.target);
323
- const displaySource = writeTargetDisplaySource(target);
348
+ const { name, absPath, target, ref } = resolveEnvWriteTarget(args.ref, args.target);
324
349
  const { confirmDestructive } = await import("../../cli/confirm.js");
325
350
  const confirmed = await confirmDestructive(`Remove env "${args.ref}"? This cannot be undone.`, {
326
351
  yes: args.yes === true,
@@ -330,124 +355,18 @@ const envRemoveCommand = defineJsonCommand({
330
355
  return;
331
356
  }
332
357
  if (!fs.existsSync(absPath)) {
333
- throw new NotFoundError(`Env not found: ${makeEnvRef(name, displaySource)}`);
358
+ throw new NotFoundError(`Env not found: ${ref}`);
334
359
  }
335
360
  const { removeEnv } = await import("./env.js");
336
- const removed = removeEnv(absPath);
337
- commitEnvSecretWrite(target, { type: "env", name }, "Remove", [absPath, `${absPath}.sensitive`]);
338
- output("env-remove", { ref: makeEnvRef(name, displaySource), removed });
339
- },
340
- });
341
- const envSetCommand = defineJsonCommand({
342
- meta: {
343
- name: "set",
344
- description: "Set (create or update) a single KEY in an env file: `akm env set <ref> <KEY>`. The value is read from stdin by default (never via argv); use --from-env <VAR> or --from-file <path>. Preserves existing comments and key order; the value is never printed. Creates the env file if it does not exist.",
345
- },
346
- args: {
347
- ref: { type: "positional", description: "Env ref (e.g. env:prod or just prod)", required: true },
348
- key: { type: "positional", description: "Key name to set (e.g. API_URL)", required: true },
349
- "from-env": { type: "string", description: "Read the value from the named environment variable" },
350
- "from-file": { type: "string", description: "Read the value from this file" },
351
- target: {
352
- type: "string",
353
- description: "Override the write destination. Accepts a source name from your config; falls back to defaultWriteTarget then the working stash.",
354
- },
355
- },
356
- async run({ args }) {
357
- const { name, absPath, target } = resolveEnvWriteTarget(args.ref, args.target);
358
- const displaySource = writeTargetDisplaySource(target);
359
- const key = String(args.key);
360
- const { ENV_KEY_RE, setEnvKey } = await import("./env.js");
361
- if (!ENV_KEY_RE.test(key)) {
362
- throw new UsageError(`Invalid env key "${key}". Keys match [A-Za-z_][A-Za-z0-9_]*.`, "INVALID_FLAG_VALUE");
363
- }
364
- const fromEnv = args["from-env"];
365
- const fromFile = args["from-file"];
366
- if (fromEnv !== undefined && fromFile !== undefined) {
367
- throw new UsageError("Pass only one of --from-file or --from-env (or use stdin).", "INVALID_FLAG_VALUE");
368
- }
369
- const MAX_ENV_VALUE_BYTES = 1024 * 1024; // 1 MB
370
- let value;
371
- if (fromFile !== undefined) {
372
- if (!fs.existsSync(fromFile)) {
373
- throw new NotFoundError(`File not found: ${fromFile}`, "FILE_NOT_FOUND");
374
- }
375
- const buf = fs.readFileSync(fromFile);
376
- if (buf.byteLength > MAX_ENV_VALUE_BYTES)
377
- throw new UsageError("Value exceeds the 1 MB limit.");
378
- value = buf.toString("utf8");
379
- }
380
- else if (fromEnv !== undefined) {
381
- const v = process.env[fromEnv];
382
- if (v === undefined) {
383
- throw new UsageError(`Environment variable "${fromEnv}" is not set.`, "INVALID_FLAG_VALUE");
384
- }
385
- value = v;
386
- }
387
- else {
388
- const buf = await readStdin(MAX_ENV_VALUE_BYTES, () => new UsageError("Value exceeds the 1 MB limit."));
389
- // Strip a single trailing newline so `echo "$VAL" | akm env set` is exact.
390
- value = buf.toString("utf8").replace(/\n$/, "");
391
- }
392
- setEnvKey(absPath, key, value);
393
- commitEnvSecretWrite(target, { type: "env", name }, "Update", [absPath]);
394
- // Warn (never block) on process-hijacking key names, matching the env-run audit.
395
- const { isDangerousEnvKey } = await import("../lint/env-key-rules.js");
396
- if (isDangerousEnvKey(key) && !isQuiet()) {
397
- process.stderr.write(`warning: "${key}" can influence process execution when this env is loaded via 'akm env run'.\n`);
398
- }
399
- output("env-set", { ref: makeEnvRef(name, displaySource), key });
400
- },
401
- });
402
- const envUnsetCommand = defineJsonCommand({
403
- meta: {
404
- name: "unset",
405
- description: "Remove one or more KEYs from an env file: `akm env unset <ref> <KEY...>`. Preserves other keys and comments. To remove the whole file, use `akm env remove`.",
406
- },
407
- args: {
408
- ref: { type: "positional", description: "Env ref (e.g. env:prod or just prod)", required: true },
409
- // `key` is read from the raw positionals (one or more) in run(); declared
410
- // non-required so citty doesn't block before we emit a structured error.
411
- key: { type: "positional", description: "Key name(s) to remove (one or more)", required: false },
412
- target: {
413
- type: "string",
414
- description: "Override the write destination. Accepts a source name from your config; falls back to defaultWriteTarget then the working stash.",
415
- },
416
- },
417
- async run({ args }) {
418
- const { name, absPath, target } = resolveEnvWriteTarget(args.ref, args.target);
419
- const displaySource = writeTargetDisplaySource(target);
420
- if (!fs.existsSync(absPath)) {
421
- throw new NotFoundError(`Env not found: ${makeEnvRef(name, displaySource)}`);
422
- }
423
- // citty puts every positional in `args._` (incl. the ref at [0]); the keys
424
- // are the remaining positionals. citty also mis-captures the space-separated
425
- // value of a global flag (`--format json`) as a positional, so drop any
426
- // token that is actually a global flag's value (cli.ts:1335 documents this).
427
- const invocation = getParsedInvocation();
428
- const globalFlagValues = new Set(["--format", "--shape", "--detail", "--scope", "--filter", "--target"]
429
- .map((flag) => invocation.getFlagValue(flag))
430
- .filter((v) => typeof v === "string"));
431
- const keys = (Array.isArray(args._) ? args._.map(String) : [])
432
- .slice(1)
433
- .filter((k) => !globalFlagValues.has(k));
434
- if (keys.length === 0) {
435
- throw new UsageError("Usage: akm env unset <ref> <KEY...> (one or more keys).", "MISSING_REQUIRED_ARGUMENT");
436
- }
437
- const { ENV_KEY_RE, unsetEnvKeys } = await import("./env.js");
438
- const invalid = keys.filter((k) => !ENV_KEY_RE.test(k));
439
- if (invalid.length > 0) {
440
- throw new UsageError(`Invalid env key(s): ${invalid.join(", ")}.`, "INVALID_FLAG_VALUE");
441
- }
442
- const { removed, missing } = unsetEnvKeys(absPath, keys);
443
- commitEnvSecretWrite(target, { type: "env", name }, "Update", [absPath]);
444
- output("env-unset", { ref: makeEnvRef(name, displaySource), removed, missing });
361
+ const removed = withEnvSecretWrite(target, { type: "env", name }, "Remove", [absPath, sensitiveMarkerPath(absPath, "env")], () => removeEnv(absPath));
362
+ output("env-remove", { ref, removed });
445
363
  },
446
364
  });
447
365
  export const envCommand = defineGroupCommand({
448
366
  meta: {
449
367
  name: "env",
450
- description: "Manage `.env` files — a group of related CONFIGURATION values for an app or service (URLs, flags, plus any credentials it needs), loaded together. Values may or may not be sensitive; akm protects them all the same (key names visible, values never in structured output). For a single sensitive value used on its own (an auth token, key, or cert), use `akm secret`.",
368
+ description: "Manage `.env` files — configuration values an app loads together (URLs, flags, and any credentials it needs).\n\n" +
369
+ "Values may or may not be sensitive; akm protects them all the same way — key names are visible, values never appear in structured output. For a single sensitive value used on its own (an auth token, key, or cert), use `akm secret`.",
451
370
  },
452
371
  subCommands: {
453
372
  list: envListCommand,
@@ -455,12 +374,9 @@ export const envCommand = defineGroupCommand({
455
374
  export: envExportCommand,
456
375
  run: envRunCommand,
457
376
  create: envCreateCommand,
458
- set: envSetCommand,
459
- unset: envUnsetCommand,
460
377
  remove: envRemoveCommand,
461
378
  },
462
- async defaultRun() {
463
- const { listKeys } = await import("./env.js");
464
- output("env-list", { envs: listEnvsRecursive(listKeys) });
465
- },
379
+ // No `defaultRun`: bare `akm env` is a usage error (exit 2), the canonical
380
+ // bare-group behavior — owner ruling 12. Run `akm env list` for what the
381
+ // bare form used to print.
466
382
  });