akm-cli 0.8.3 → 0.9.0-beta.1

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 (316) hide show
  1. package/CHANGELOG.md +209 -0
  2. package/dist/assets/help/help-proposals.md +1 -2
  3. package/dist/assets/hints/cli-hints-full.md +34 -19
  4. package/dist/assets/hints/cli-hints-short.md +1 -1
  5. package/dist/assets/profiles/catchup.json +13 -0
  6. package/dist/assets/profiles/consolidate.json +13 -0
  7. package/dist/assets/profiles/frequent.json +13 -0
  8. package/dist/assets/tasks/core/backup.yml +4 -0
  9. package/dist/assets/tasks/core/extract.yml +4 -0
  10. package/dist/assets/tasks/core/improve.yml +4 -0
  11. package/dist/assets/tasks/core/index-refresh.yml +4 -0
  12. package/dist/assets/tasks/core/sync.yml +4 -0
  13. package/dist/assets/tasks/core/update-stashes.yml +4 -0
  14. package/dist/assets/tasks/core/version-check.yml +4 -0
  15. package/dist/cli/config-migrate.js +6 -6
  16. package/dist/cli/config-validate.js +4 -4
  17. package/dist/cli/confirm.js +3 -3
  18. package/dist/cli/parse-args.js +1 -1
  19. package/dist/cli/shared.js +51 -14
  20. package/dist/cli-node.mjs +26 -0
  21. package/dist/cli.js +171 -3862
  22. package/dist/commands/{agent-dispatch.js → agent/agent-dispatch.js} +6 -6
  23. package/dist/commands/{agent-support.js → agent/agent-support.js} +2 -2
  24. package/dist/commands/agent/contribute-cli.js +200 -0
  25. package/dist/commands/completions.js +1 -1
  26. package/dist/commands/config-cli.js +240 -3
  27. package/dist/commands/config-edit.js +344 -0
  28. package/dist/commands/db-cli.js +2 -2
  29. package/dist/commands/env/env-cli.js +529 -0
  30. package/dist/commands/env/env.js +410 -0
  31. package/dist/commands/env/secret-cli.js +259 -0
  32. package/dist/commands/{secret.js → env/secret.js} +6 -47
  33. package/dist/commands/events.js +4 -4
  34. package/dist/commands/feedback-cli.js +18 -34
  35. package/dist/commands/graph/graph-cli.js +132 -0
  36. package/dist/commands/{graph.js → graph/graph.js} +22 -16
  37. package/dist/commands/health/checks.js +279 -0
  38. package/dist/commands/health.js +94 -262
  39. package/dist/commands/{consolidate.js → improve/consolidate.js} +48 -36
  40. package/dist/commands/{distill-promotion-policy.js → improve/distill-promotion-policy.js} +3 -3
  41. package/dist/commands/{distill.js → improve/distill.js} +39 -18
  42. package/dist/commands/{eval-cases.js → improve/eval-cases.js} +1 -1
  43. package/dist/commands/{extract-cli.js → improve/extract-cli.js} +4 -4
  44. package/dist/commands/{extract-prompt.js → improve/extract-prompt.js} +2 -2
  45. package/dist/commands/{extract.js → improve/extract.js} +185 -26
  46. package/dist/commands/{improve-auto-accept.js → improve/improve-auto-accept.js} +4 -4
  47. package/dist/commands/{improve-cli.js → improve/improve-cli.js} +44 -22
  48. package/dist/commands/{improve-profiles.js → improve/improve-profiles.js} +13 -7
  49. package/dist/commands/{improve-result-file.js → improve/improve-result-file.js} +1 -1
  50. package/dist/commands/{improve.js → improve/improve.js} +517 -253
  51. package/dist/{core → commands/improve/memory}/memory-belief.js +2 -2
  52. package/dist/{core → commands/improve/memory}/memory-contradiction-detect.js +5 -5
  53. package/dist/{core → commands/improve/memory}/memory-improve.js +4 -4
  54. package/dist/commands/{reflect.js → improve/reflect.js} +33 -28
  55. package/dist/commands/improve/session-asset.js +248 -0
  56. package/dist/commands/lint/agent-linter.js +1 -1
  57. package/dist/commands/lint/base-linter.js +55 -37
  58. package/dist/commands/lint/command-linter.js +1 -1
  59. package/dist/commands/lint/default-linter.js +1 -1
  60. package/dist/commands/lint/env-key-rules.js +1 -1
  61. package/dist/commands/lint/index.js +19 -25
  62. package/dist/commands/lint/knowledge-linter.js +1 -1
  63. package/dist/commands/lint/memory-linter.js +1 -1
  64. package/dist/commands/lint/registry.js +8 -8
  65. package/dist/commands/lint/skill-linter.js +1 -1
  66. package/dist/commands/lint/task-linter.js +1 -1
  67. package/dist/commands/lint/workflow-linter.js +1 -1
  68. package/dist/commands/lint.js +1 -1
  69. package/dist/commands/observability-cli.js +244 -0
  70. package/dist/commands/proposal/drain-policies.js +3 -3
  71. package/dist/commands/proposal/drain.js +15 -10
  72. package/dist/commands/proposal/proposal-cli.js +478 -0
  73. package/dist/commands/{proposal.js → proposal/proposal.js} +5 -5
  74. package/dist/commands/{propose.js → proposal/propose.js} +11 -11
  75. package/dist/{core → commands/proposal/validators}/proposal-quality-validators.js +8 -3
  76. package/dist/{core → commands/proposal/validators}/proposal-validators.js +5 -5
  77. package/dist/{core → commands/proposal/validators}/proposals.js +13 -7
  78. package/dist/commands/{curate.js → read/curate.js} +7 -7
  79. package/dist/commands/{knowledge.js → read/knowledge.js} +22 -9
  80. package/dist/commands/{registry-search.js → read/registry-search.js} +5 -5
  81. package/dist/commands/{remember-cli.js → read/remember-cli.js} +15 -7
  82. package/dist/commands/read/search-cli.js +207 -0
  83. package/dist/commands/{search.js → read/search.js} +22 -27
  84. package/dist/commands/{show.js → read/show.js} +31 -45
  85. package/dist/commands/registry-cli.js +8 -8
  86. package/dist/commands/remember.js +8 -8
  87. package/dist/commands/sources/add-cli.js +293 -0
  88. package/dist/commands/{history.js → sources/history.js} +27 -25
  89. package/dist/commands/{info.js → sources/info.js} +6 -6
  90. package/dist/commands/{init.js → sources/init.js} +6 -6
  91. package/dist/commands/{installed-stashes.js → sources/installed-stashes.js} +12 -12
  92. package/dist/commands/{migration-help.js → sources/migration-help.js} +3 -2
  93. package/dist/commands/{schema-repair.js → sources/schema-repair.js} +8 -8
  94. package/dist/commands/{self-update.js → sources/self-update.js} +10 -9
  95. package/dist/commands/{source-add.js → sources/source-add.js} +10 -10
  96. package/dist/commands/{source-clone.js → sources/source-clone.js} +7 -7
  97. package/dist/commands/{source-manage.js → sources/source-manage.js} +4 -4
  98. package/dist/commands/sources/sources-cli.js +305 -0
  99. package/dist/commands/sources/stash-cli.js +219 -0
  100. package/dist/commands/{stash-skeleton.js → sources/stash-skeleton.js} +2 -1
  101. package/dist/commands/tasks/default-tasks.js +173 -0
  102. package/dist/commands/tasks/tasks-cli.js +210 -0
  103. package/dist/commands/{tasks.js → tasks/tasks.js} +14 -14
  104. package/dist/commands/wiki-cli.js +307 -0
  105. package/dist/commands/workflow-cli.js +329 -0
  106. package/dist/core/action-contributors.js +1 -1
  107. package/dist/core/assert.js +40 -0
  108. package/dist/core/asset/asset-create.js +54 -0
  109. package/dist/core/{asset-ref.js → asset/asset-ref.js} +21 -4
  110. package/dist/core/{asset-registry.js → asset/asset-registry.js} +3 -3
  111. package/dist/core/{asset-spec.js → asset/asset-spec.js} +17 -31
  112. package/dist/core/{markdown.js → asset/markdown.js} +1 -1
  113. package/dist/core/{stash-meta.js → asset/stash-meta.js} +1 -1
  114. package/dist/core/best-effort.js +64 -0
  115. package/dist/core/common.js +32 -18
  116. package/dist/core/{config-io.js → config/config-io.js} +29 -19
  117. package/dist/core/{config-migration.js → config/config-migration.js} +11 -9
  118. package/dist/core/{config-schema.js → config/config-schema.js} +45 -1
  119. package/dist/core/config/config-types.js +16 -0
  120. package/dist/core/{config-walker.js → config/config-walker.js} +2 -2
  121. package/dist/core/{config.js → config/config.js} +10 -8
  122. package/dist/core/env-secret-ref.js +90 -0
  123. package/dist/core/errors.js +13 -3
  124. package/dist/core/events.js +27 -4
  125. package/dist/core/file-lock.js +1 -1
  126. package/dist/core/improve-types.js +48 -0
  127. package/dist/core/lesson-lint.js +2 -2
  128. package/dist/core/paths.js +2 -2
  129. package/dist/core/ripgrep/install.js +2 -2
  130. package/dist/core/ripgrep/resolve.js +2 -2
  131. package/dist/core/state-db.js +88 -46
  132. package/dist/core/text-truncation.js +148 -0
  133. package/dist/core/time.js +1 -1
  134. package/dist/core/write-source.js +98 -85
  135. package/dist/indexer/{db-backup.js → db/db-backup.js} +9 -24
  136. package/dist/indexer/{db.js → db/db.js} +126 -116
  137. package/dist/indexer/{graph-db.js → db/graph-db.js} +9 -4
  138. package/dist/indexer/{llm-cache.js → db/llm-cache.js} +15 -12
  139. package/dist/indexer/ensure-index.js +4 -4
  140. package/dist/indexer/{graph-boost.js → graph/graph-boost.js} +1 -1
  141. package/dist/indexer/{graph-extraction.js → graph/graph-extraction.js} +55 -13
  142. package/dist/indexer/indexer.js +37 -30
  143. package/dist/indexer/init.js +54 -0
  144. package/dist/indexer/manifest.js +10 -10
  145. package/dist/indexer/{memory-inference.js → passes/memory-inference.js} +92 -23
  146. package/dist/indexer/{metadata-contributors.js → passes/metadata-contributors.js} +10 -8
  147. package/dist/indexer/{metadata.js → passes/metadata.js} +15 -19
  148. package/dist/indexer/{staleness-detect.js → passes/staleness-detect.js} +53 -12
  149. package/dist/indexer/{db-search.js → search/db-search.js} +28 -16
  150. package/dist/indexer/{ranking-contributors.js → search/ranking-contributors.js} +1 -1
  151. package/dist/indexer/{ranking.js → search/ranking.js} +2 -2
  152. package/dist/indexer/{search-hit-enrichers.js → search/search-hit-enrichers.js} +3 -3
  153. package/dist/indexer/{search-source.js → search/search-source.js} +8 -8
  154. package/dist/indexer/{semantic-status.js → search/semantic-status.js} +3 -3
  155. package/dist/indexer/usage/unmigrated-vaults-guard.js +94 -0
  156. package/dist/indexer/{usage-events.js → usage/usage-events.js} +32 -0
  157. package/dist/indexer/{file-context.js → walk/file-context.js} +10 -15
  158. package/dist/indexer/{matchers.js → walk/matchers.js} +13 -9
  159. package/dist/indexer/{path-resolver.js → walk/path-resolver.js} +6 -6
  160. package/dist/indexer/{project-context.js → walk/project-context.js} +1 -1
  161. package/dist/indexer/{walker.js → walk/walker.js} +4 -3
  162. package/dist/integrations/agent/builder-shared.js +39 -0
  163. package/dist/integrations/agent/builders.js +14 -81
  164. package/dist/integrations/agent/config.js +6 -4
  165. package/dist/integrations/agent/detect.js +1 -1
  166. package/dist/integrations/agent/index.js +23 -8
  167. package/dist/integrations/agent/prompts.js +2 -3
  168. package/dist/integrations/agent/runner.js +22 -3
  169. package/dist/integrations/agent/spawn.js +9 -10
  170. package/dist/integrations/harnesses/claude/agent-builder.js +48 -0
  171. package/dist/integrations/harnesses/claude/config-import.js +70 -0
  172. package/dist/integrations/harnesses/claude/index.js +64 -0
  173. package/dist/integrations/{session-logs/providers/claude-code.js → harnesses/claude/session-log.js} +16 -1
  174. package/dist/integrations/harnesses/index.js +144 -0
  175. package/dist/integrations/harnesses/opencode/agent-builder.js +43 -0
  176. package/dist/integrations/harnesses/opencode/config-import.js +82 -0
  177. package/dist/integrations/harnesses/opencode/index.js +59 -0
  178. package/dist/integrations/{session-logs/providers/opencode.js → harnesses/opencode/session-log.js} +1 -1
  179. package/dist/integrations/harnesses/opencode-sdk/index.js +49 -0
  180. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +234 -0
  181. package/dist/integrations/harnesses/types.js +43 -0
  182. package/dist/integrations/lockfile.js +7 -16
  183. package/dist/integrations/session-logs/index.js +82 -9
  184. package/dist/llm/call-ai.js +4 -4
  185. package/dist/llm/client.js +131 -6
  186. package/dist/llm/embedder.js +6 -6
  187. package/dist/llm/embedders/local.js +9 -22
  188. package/dist/llm/embedders/remote.js +2 -2
  189. package/dist/llm/embedders/types.js +1 -1
  190. package/dist/llm/graph-extract.js +31 -12
  191. package/dist/llm/index-passes.js +1 -1
  192. package/dist/llm/memory-infer.js +12 -5
  193. package/dist/llm/metadata-enhance.js +2 -2
  194. package/dist/output/context.js +6 -44
  195. package/dist/output/renderers.js +88 -58
  196. package/dist/output/shapes/curate.js +7 -3
  197. package/dist/output/shapes/distill.js +7 -3
  198. package/dist/output/shapes/env-list.js +18 -16
  199. package/dist/output/shapes/events.js +5 -4
  200. package/dist/output/shapes/helpers.js +2 -4
  201. package/dist/output/shapes/history.js +7 -3
  202. package/dist/output/shapes/passthrough.js +8 -11
  203. package/dist/output/shapes/{proposal-accept.js → proposal/accept.js} +7 -3
  204. package/dist/output/shapes/{proposal-diff.js → proposal/diff.js} +7 -3
  205. package/dist/output/shapes/{proposal-list.js → proposal/list.js} +7 -3
  206. package/dist/output/shapes/{proposal-producer.js → proposal/producer.js} +5 -4
  207. package/dist/output/shapes/{proposal-reject.js → proposal/reject.js} +7 -3
  208. package/dist/output/shapes/{proposal-show.js → proposal/show.js} +7 -3
  209. package/dist/output/shapes/registry-search.js +7 -3
  210. package/dist/output/shapes/registry.js +12 -0
  211. package/dist/output/shapes/search.js +7 -3
  212. package/dist/output/shapes/secret-list.js +18 -16
  213. package/dist/output/shapes/show.js +7 -3
  214. package/dist/output/shapes.js +55 -30
  215. package/dist/output/text/add.js +2 -3
  216. package/dist/output/text/clone.js +2 -3
  217. package/dist/output/text/config.js +2 -3
  218. package/dist/output/text/curate.js +4 -3
  219. package/dist/output/text/distill.js +2 -3
  220. package/dist/output/text/enable-disable.js +5 -4
  221. package/dist/output/text/env.js +13 -0
  222. package/dist/output/text/events.js +5 -4
  223. package/dist/output/text/feedback.js +4 -3
  224. package/dist/output/text/helpers.js +54 -39
  225. package/dist/output/text/history.js +2 -3
  226. package/dist/output/text/import.js +2 -3
  227. package/dist/output/text/index.js +2 -3
  228. package/dist/output/text/info.js +2 -3
  229. package/dist/output/text/init.js +2 -3
  230. package/dist/output/text/list.js +2 -3
  231. package/dist/output/text/proposal/producer.js +9 -0
  232. package/dist/output/text/proposal/proposal.js +13 -0
  233. package/dist/output/text/registry-commands.js +8 -7
  234. package/dist/output/text/registry.js +12 -0
  235. package/dist/output/text/remember.js +4 -3
  236. package/dist/output/text/remove.js +2 -3
  237. package/dist/output/text/save.js +2 -3
  238. package/dist/output/text/search.js +4 -3
  239. package/dist/output/text/show.js +4 -3
  240. package/dist/output/text/update.js +2 -3
  241. package/dist/output/text/upgrade.js +2 -3
  242. package/dist/output/text/wiki.js +12 -11
  243. package/dist/output/text/workflow.js +12 -10
  244. package/dist/output/text.js +66 -32
  245. package/dist/registry/build-index.js +11 -10
  246. package/dist/registry/factory.js +1 -1
  247. package/dist/registry/origin-resolve.js +1 -1
  248. package/dist/registry/providers/index.js +2 -2
  249. package/dist/registry/providers/skills-sh.js +91 -72
  250. package/dist/registry/providers/static-index.js +75 -52
  251. package/dist/registry/resolve.js +3 -3
  252. package/dist/runtime.js +242 -0
  253. package/dist/scripts/migrate-storage.js +1594 -673
  254. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +240 -166
  255. package/dist/setup/detect.js +311 -9
  256. package/dist/setup/harness-config-import.js +6 -120
  257. package/dist/setup/setup.js +454 -43
  258. package/dist/sources/include.js +1 -1
  259. package/dist/sources/provider-factory.js +2 -2
  260. package/dist/sources/providers/filesystem.js +3 -3
  261. package/dist/sources/providers/git.js +9 -9
  262. package/dist/sources/providers/index.js +4 -4
  263. package/dist/sources/providers/npm.js +6 -6
  264. package/dist/sources/providers/provider-utils.js +13 -20
  265. package/dist/sources/providers/sync-from-ref.js +5 -5
  266. package/dist/sources/providers/tar-utils.js +2 -2
  267. package/dist/sources/providers/website.js +2 -2
  268. package/dist/sources/resolve.js +5 -5
  269. package/dist/sources/website-ingest.js +5 -5
  270. package/dist/storage/database.js +102 -0
  271. package/dist/storage/engines/sqlite-migrations.js +42 -0
  272. package/dist/storage/locations.js +25 -0
  273. package/dist/storage/repositories/index-db.js +43 -0
  274. package/dist/storage/repositories/workflow-runs-repository.js +141 -0
  275. package/dist/tasks/backends/cron.js +4 -4
  276. package/dist/tasks/backends/exec-utils.js +32 -0
  277. package/dist/tasks/backends/index.js +3 -3
  278. package/dist/tasks/backends/launchd.js +7 -14
  279. package/dist/tasks/backends/schtasks.js +7 -16
  280. package/dist/tasks/embedded.js +71 -0
  281. package/dist/tasks/parser.js +2 -2
  282. package/dist/tasks/resolveAkmBin.js +1 -1
  283. package/dist/tasks/runner.js +28 -15
  284. package/dist/tasks/schedule.js +1 -1
  285. package/dist/tasks/validator.js +7 -7
  286. package/dist/text-import-hook.mjs +51 -0
  287. package/dist/version.js +2 -1
  288. package/dist/wiki/wiki.js +7 -7
  289. package/dist/workflows/{authoring.js → authoring/authoring.js} +6 -6
  290. package/dist/workflows/{scope-key.js → authoring/scope-key.js} +1 -1
  291. package/dist/workflows/cli.js +1 -1
  292. package/dist/workflows/db.js +50 -32
  293. package/dist/workflows/parser.js +4 -4
  294. package/dist/workflows/renderer.js +5 -5
  295. package/dist/workflows/runtime/agent-identity.js +56 -0
  296. package/dist/workflows/runtime/checkin.js +57 -0
  297. package/dist/workflows/{runs.js → runtime/runs.js} +197 -101
  298. package/dist/workflows/validate-summary.js +82 -0
  299. package/docs/README.md +1 -1
  300. package/docs/data-and-telemetry.md +6 -6
  301. package/package.json +16 -8
  302. package/dist/commands/add-cli.js +0 -279
  303. package/dist/commands/env.js +0 -213
  304. package/dist/integrations/agent/sdk-runner.js +0 -126
  305. package/dist/output/shapes/vault-list.js +0 -19
  306. package/dist/output/text/proposal-producer.js +0 -8
  307. package/dist/output/text/proposal.js +0 -12
  308. package/dist/output/text/vault.js +0 -16
  309. /package/dist/core/{asset-serialize.js → asset/asset-serialize.js} +0 -0
  310. /package/dist/core/{frontmatter.js → asset/frontmatter.js} +0 -0
  311. /package/dist/core/{config-sources.js → config/config-sources.js} +0 -0
  312. /package/dist/indexer/{graph-dedup.js → graph/graph-dedup.js} +0 -0
  313. /package/dist/{core/config-types.js → indexer/passes/pass-context.js} +0 -0
  314. /package/dist/indexer/{search-fields.js → search/search-fields.js} +0 -0
  315. /package/dist/indexer/{index-context.js → walk/index-context.js} +0 -0
  316. /package/dist/workflows/{document-cache.js → runtime/document-cache.js} +0 -0
package/CHANGELOG.md CHANGED
@@ -4,6 +4,215 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
6
 
7
+ ## [Unreleased]
8
+
9
+ ## [0.9.0-beta.1] - 2026-06-08
10
+
11
+ ### Fixed
12
+
13
+ - **`improve.lock` leaked on signal death (cron timeout)** — forward-ported from
14
+ 0.8.3. The improve SIGTERM/SIGINT/SIGHUP handler calls `process.exit()`, which
15
+ skips `finally` blocks, so the `finally` releasing `improve.lock` never ran and
16
+ every timed-out cron run leaked the lock. It is now released from a
17
+ `process.on("exit", …)` handler registered at acquire time, via a new
18
+ ownership-checked `releaseLockIfOwned(path, pid)`.
19
+ - **`quick` profile was not quick** — forward-ported from 0.8.3. It did not
20
+ disable the default-ON session-`extract` process, so a `quick` run processed
21
+ the entire session backlog (~40 min). `quick` now sets
22
+ `processes.extract.enabled: false`.
23
+ - **`akm-eval` smoke suite adapted to the 0.9.0 CLI** (CI/tooling only). The
24
+ eval harness called `akm search --detail agent`, but 0.9.0 moved the
25
+ agent/summary projections to `--shape`; it now uses `--shape agent`.
26
+ Additionally, the improve-run history readers (`listRecentImproveRunIds` /
27
+ `resolveImproveRunId`) treated a missing `state.db` as an error rather than
28
+ "no runs", which broke the read-only smoke + replay-determinism gates on a
29
+ fresh checkout; a missing `state.db` is now handled as an empty history.
30
+
31
+ ## [0.9.0-beta.0] - 2026-06-08
32
+
33
+ ### Added
34
+
35
+ - **Cross-runtime: akm now runs on Node.js (≥ 20) in addition to Bun** (#560,
36
+ #465). A two-file runtime boundary (`src/storage/database.ts` owns SQLite via
37
+ `bun:sqlite` on Bun / `better-sqlite3` on Node; `src/runtime.ts` owns every
38
+ `Bun.*` API) contains all runtime-specific code, enforced by a lint guard so it
39
+ cannot leak back out. A CI `node-smoke` matrix runs the built CLI under Node
40
+ 20 and 22. **Minimum Node is 20** — the prompts dependency (`@clack/core`) uses
41
+ `node:util.styleText`, added in Node 20.12; Node 18 is EOL and unsupported.
42
+ Bun remains the primary/default runtime.
43
+ - **`session` asset type — agent sessions are now searchable** (#561). The
44
+ `extract` pass, after distilling memory proposals from a session, additionally
45
+ writes the session itself as a first-class `session` asset
46
+ (`sessions/<harness>/<id>.md`) with an LLM-generated `## Summary` /
47
+ `## Key topics` body plus `harness` / `session_id` / `started_at` / `ended_at`
48
+ / `project` / `log_path` / `access` frontmatter. Sessions become discoverable
49
+ via `akm search --type session` and `akm curate`, and the `access` + `log_path`
50
+ fields tell any agent how to open the raw session log. The behaviour is
51
+ ADDITIVE, FAIL-OPEN, and config-gated via
52
+ `profiles.improve.default.processes.extract.indexSessions` (default on when an
53
+ LLM is configured; set `false` for byte-identical legacy extract behaviour) and
54
+ `…extract.minSessionDuration` (default 5 minutes). Session assets are not
55
+ graph-extracted. No new LLM call is made when no provider is configured.
56
+
57
+ - **`akm env set` / `akm env unset` — single-key `.env` management.** `akm env
58
+ set <ref> <KEY>` sets/updates one key (value from stdin by default, or
59
+ `--from-env <VAR>` / `--from-file <path>` — never argv, never echoed); `akm env
60
+ unset <ref> <KEY...>` removes one or more keys. Both do a minimal edit that
61
+ preserves existing comments and key order, and use `dotenv` as the
62
+ serialisation oracle: a value is only written if `dotenv.parse` reads it back
63
+ exactly, and the whole edit is re-verified so no sibling key is disturbed. This
64
+ reintroduces key-level management (the deprecated `vault set`/`vault unset`
65
+ pointed here); `akm env remove` still removes the whole file.
66
+
67
+ - **`--path` for subdirectory asset creation** (#503) — a consistent `--path
68
+ <relative-dir>` flag across the asset-creating command surface: `akm remember`,
69
+ `akm import`, `akm propose`, `akm workflow create`, `akm env create`, and
70
+ `akm secret set`. `--path` is a directory applied rooted at the asset's type
71
+ directory (e.g. `akm remember "buy milk" --path personal --name grocery-list`
72
+ → `memories/personal/grocery-list.md`; `akm workflow create ship --path
73
+ release` → `workflows/release/ship.md`). The filename/name still comes from the
74
+ `--name`/name positional (or, for `remember`/`import`, the content/source slug).
75
+ The explicit name is now a **flat** name everywhere: a `/` in it is rejected
76
+ with guidance to use `--path`. System-derived names (e.g. a URL-path-derived
77
+ knowledge name from `akm import <url>`) may still nest. Shared semantics live in
78
+ `src/core/asset-create.ts`. (Replaces #503's earlier nested-`--name` approach.)
79
+ - **Workflow runs record agent harness + session identity** — `akm workflow start`
80
+ now persists the agent harness (e.g. `claude-code`, `opencode`) and the
81
+ platform-native session id that owns each run. Identity is resolved best-effort
82
+ from the environment (`AKM_AGENT_HARNESS` / `AKM_SESSION_ID`, falling back to the
83
+ harness-native session env var) or can be passed explicitly to `startWorkflowRun`.
84
+ Stored via additive migration `002-add-agent-identity` and surfaced on
85
+ `WorkflowRunSummary.agentHarness` / `.agentSessionId`. This is the first concrete,
86
+ scoped slice toward workflow session monitoring (#501).
87
+ - **Workflow agent check-in + step-summary validation** (#506) — workflow runs now
88
+ use a file-signal / command-loop check-in model (no resident background thread, per
89
+ the ADR in `docs/technical/workflow-agent-checkin-adr.md`). `akm workflow start`
90
+ arms a durable check-in timestamp; `akm workflow complete --summary` now **requires**
91
+ a per-step summary and runs it through an LLM completion-criteria validation gate —
92
+ on failure the step stays pending and structured corrective feedback is returned
93
+ (`workflow-complete-rejected`). A pure `evaluateCheckin` surfaces a strong `continue`
94
+ directive through `getNextWorkflowStep` when an active run looks stalled. Migration
95
+ `002` adds `agent_harness`, `agent_session_id`, `checkin_armed_at` on
96
+ `workflow_runs` and `summary` on `workflow_run_steps`.
97
+ - **Default improve profiles + scheduled task set** (#552) — three new bundled
98
+ profiles in `src/assets/profiles/` — `frequent` (extract + inference; distill /
99
+ consolidate excluded), `consolidate` (consolidation-only), and `catchup` (manual
100
+ recovery: consolidate + triage drain) — alongside the existing `default` / `quick` /
101
+ `thorough` / `memory-focus` / `graph-refresh`. `akm setup` and the new `akm tasks
102
+ init` register a multi-cadence task set **idempotently**: `akm-improve-frequent`
103
+ (60 min), `akm-improve-consolidate` (4 h), `akm-improve-nightly` (`thorough`, daily
104
+ 2 am, server-gated), `akm-improve-catchup` (registered but unscheduled), and
105
+ `akm-graph-refresh-weekly` (Sun 3 am). Registration is CI-aware (skips when
106
+ `CI=true`) and asks a single "Is this a server install?" prompt to gate the nightly
107
+ task (default yes on Linux-without-battery, no on macOS/laptop).
108
+
109
+ ### Design notes
110
+
111
+ - **#501 narrowed; superseded by #506 for the monitoring design.** Issue #501
112
+ ("Add background thread for workflow command session monitoring and agent
113
+ prompting") was an epic. Per #506's stated preference to avoid always-on
114
+ background threads/daemons, the background-thread requirement is **not**
115
+ implemented here. #501 is narrowed to the one tractable, prerequisite sub-feature
116
+ — persisting harness + session identity on each workflow run — which any future
117
+ monitor needs regardless of design. The session-monitoring/agent-steering loop is
118
+ deferred to #506 and requires a separately approved design.
119
+
120
+ ### Changed
121
+
122
+ - **`improve`: consolidation runs before extract + smarter pool-delta gate**
123
+ (#551). The consolidation phase now runs **before** the session-extract pass
124
+ in the improve pipeline. Extract auto-accept writes new memory `.md` files on
125
+ every run, which previously made the consolidation pool-delta gate
126
+ (`memoryUpdatedAfterLastConsolidate`) fire unconditionally — consolidation
127
+ never skipped and wastefully re-judged freshly-promoted single-source
128
+ memories with no merge/contradiction candidates yet. Running consolidation
129
+ first means it only ever sees memories from **prior** runs; current-run
130
+ extract promotions are not on disk yet. The pool-delta gate is additionally
131
+ narrowed: a memory whose only mtime bump since the last consolidate came from
132
+ its **own** auto-accept promotion (tracked via the `promoted` event's
133
+ `assetPath`) is excluded from the "work to do" check, so adjacent-run
134
+ promotions get a full improve cycle to settle before consolidation considers
135
+ them. When the gate now correctly skips, the existing
136
+ `improve_skipped` / `consolidation_no_memory_updates` event is emitted so
137
+ health reflects it. No event-shape changes; emitted-event order changes only
138
+ because consolidation moved earlier.
139
+
140
+ - **Unified git commit model — single batch-at-boundary commit** (#507). Writing
141
+ or deleting an asset on a git-backed source no longer commits (and optionally
142
+ pushes) **per asset**. `writeAssetToSource` / `deleteAssetFromSource` now
143
+ perform a plain filesystem write/unlink for every kind, and git-backed targets
144
+ are committed **once** at the operation boundary (`akm remember --target`,
145
+ proposal accept/revert, consolidate) as a single complete commit — `git add -A`
146
+ stages `.akm/` state + sibling assets together — pushed under the same
147
+ `writable + remote` gate as `akm save`/`akm sync`. This removes the noisy,
148
+ incomplete per-asset commits (~25 per improve run) and leaves no dirty
149
+ working-tree residue.
150
+
151
+ - **`improve/consolidate`: `minPoolSize` guard** (#553). Consolidation now skips
152
+ itself when the eligible memory pool is below `processes.consolidate.minPoolSize`
153
+ (default **500**), emitting a `consolidation_skipped` event with
154
+ `reason: pool_below_min_size` and making **zero** LLM calls — so the always-enabled
155
+ consolidate task self-activates only once a stash is large enough to have real
156
+ merge/contradiction candidates. `minPoolSize: 0` disables the guard. The skip
157
+ surfaces in `akm health` improve output. The bundled `consolidate` profile sets
158
+ `500`, `catchup` sets `0`.
159
+
160
+ - **`improve/extract`: `minNewSessions` gate** (#554). The extract phase now counts
161
+ in-window, not-yet-seen candidate sessions **before** any LLM call and skips the
162
+ pass (emitting `extract_skipped` / `reason: below_min_new_sessions`, visible in
163
+ `akm health`) when the count is below `processes.extract.minNewSessions`. The
164
+ in-code default is **0 (disabled)**, so existing profiles keep always-run behaviour;
165
+ only the new `frequent` profile opts in with `3`. This removes the ~22% of improve
166
+ runs that previously ran the full `ensureIndex` + extract pipeline for zero new
167
+ sessions.
168
+
169
+ ### Deprecated
170
+
171
+ - **`options.pushOnCommit`** (#507). The per-asset push-on-commit knob is retired.
172
+ Existing configs still parse — its push intent is mapped onto the batch push
173
+ gate and a one-time deprecation warning is emitted when the option is
174
+ encountered. Remove it and rely on `writable: true` + a configured remote.
175
+
176
+ ### Fixed
177
+
178
+ - **Memory inference re-queued `hot` parents forever** (#550). `markParentProcessed`
179
+ was only called when a derived child was newly written; when the child already
180
+ existed (`written = 0`), the parent never got `inferenceProcessed: true` and was
181
+ re-queued on every `akm improve` run (~37 wasted LLM calls/run on one production
182
+ stash). The child-exists path now marks the parent done (a genuine write failure
183
+ still leaves it unmarked for retry), while `skippedChildExists` accounting is
184
+ unchanged.
185
+ - **Auto-accept rejected truncated LLM descriptions** (#556). ~9.3% of proposals
186
+ failed auto-accept validation because the LLM cut the description mid-clause (ending
187
+ in `to`/`for`/`and`/a comma/etc.) or lost a YAML continuation line. A deterministic
188
+ post-generation repair pass (`repairTruncatedDescription` in
189
+ `src/core/text-truncation.ts`) now trims the truncated fragment to the last complete
190
+ clause or swaps in the first complete sentence from the body — never fabricating
191
+ text — wired into the extract and distill proposal-write paths before validation.
192
+ Already-valid descriptions pass through byte-identical. (Plus a one-line prompt
193
+ tightening requiring a complete sentence.)
194
+ - **Semantic index verification stuck on stashes with vault entries** (#502).
195
+ Verification compared the stored embedding count against the *full* entry count, but
196
+ the embedding phase intentionally excludes vault rows — so any index with vault
197
+ entries reported `embeddingCount < totalEntries` forever and stayed in
198
+ semantic-blocked / verification-failed state. A new `getEmbeddableEntryCount`
199
+ (`entry_type != 'vault'`) now feeds the zero-entry short-circuit, the readiness gate,
200
+ the "Semantic search ready (X/Y)" message, and the persisted `entryCount`; a
201
+ genuinely missing embedding on an embeddable entry still reports `ok:false`.
202
+
203
+ ### Internal
204
+
205
+ - **#490 architecture refactor.** Decomposed `src/cli.ts` from **4,589 → 620 LOC**
206
+ across 16 per-family command modules under `src/commands/*-cli.ts` (adopting a
207
+ `defineJsonCommand` factory for byte-identical JSON envelopes); converted `akm
208
+ health` checks to an ordered `HealthCheck` registry; and turned the
209
+ `migrate-storage` bin's 54 hand-rolled `recordStep` sites into a `MigrationStep`
210
+ registry with 3 recursive copy helpers unified into one `copyTree`. Shipped as
211
+ serialized local merges with a zero-behaviour-change contract (byte-identical CLI
212
+ surface + JSON envelopes), each gated and reviewed; the secret-migrating
213
+ `migrate-storage` change is pinned by a sha256 + file-mode fixture-stash
214
+ differential test.
215
+
7
216
  ## [0.8.3] - 2026-06-08
8
217
 
9
218
  ### Fixed
@@ -4,8 +4,7 @@ Usage:
4
4
  Description:
5
5
  List proposal queue entries.
6
6
 
7
- (`akm proposals` is a deprecated alias for `akm proposal list`; it warns on
8
- stderr and is removed in 0.9.0.)
7
+ (The flat `akm proposals` alias was removed in 0.9.0 — use `akm proposal list`.)
9
8
 
10
9
  Options:
11
10
  --status <status> Filter by pending, accepted, or rejected
@@ -22,7 +22,6 @@ akm search "<query>" --detail full # Include scores, paths, timing
22
22
  | `--format` | `json`, `jsonl`, `text`, `yaml` | `json` |
23
23
  | `--detail` | `brief`, `normal`, `full` | `brief` |
24
24
  | `--shape` | `human`, `agent`, `summary` (`summary` only on `show`) | `human` |
25
- | `--for-agent` | boolean (deprecated — use `--shape agent`) | `false` |
26
25
 
27
26
  ## Curate
28
27
 
@@ -59,7 +58,8 @@ akm show knowledge:my-doc # Show content (local or remote)
59
58
  | knowledge | `content` (with view modes: `full`, `toc`, `frontmatter`, `section`, `lines`) |
60
59
  | workflow | `workflowTitle`, `workflowParameters`, `steps` |
61
60
  | memory | `content` (recalled context) |
62
- | vault | `keys`, `comments` |
61
+ | env | `keys`, `comments` (key names + comments only — values never returned) |
62
+ | secret | `name` only (the whole file is the value — never returned) |
63
63
  | wiki | `content` (same view modes as knowledge). For any wiki task, run `akm wiki list`. To ingest sources, `akm wiki ingest <name>` dispatches the configured agent (defaults.agent or `--profile`) to execute the ingest workflow. |
64
64
 
65
65
  ## Capture Knowledge While You Work
@@ -78,7 +78,7 @@ akm workflow next workflow:ship-release # Start or resume the next workfl
78
78
  akm feedback skill:code-review --positive # Record that an asset helped
79
79
  akm feedback agent:reviewer --negative # Record that an asset missed the mark
80
80
  akm feedback memory:deployment-notes --positive # Works for memories too
81
- akm feedback vault:prod --positive # Records vault feedback without surfacing values
81
+ akm feedback env:prod --positive # Records env feedback without surfacing values
82
82
  ```
83
83
 
84
84
  Use `akm feedback` whenever an asset materially helps or fails so future search
@@ -119,20 +119,37 @@ page create/update, log entry, lint, reindex.** Wiki pages are also addressable
119
119
  `schema.md`, `index.md`, and `log.md` are not indexed and do not appear in
120
120
  search results. No `--llm` anywhere — akm never reasons about page content.
121
121
 
122
- ## Vaults
122
+ ## Env files
123
123
 
124
- Encrypted-at-rest key/value stores for secrets. Each vault is a `.env`-format
125
- file at `<stashDir>/vaults/<name>.env`.
124
+ A group of related CONFIGURATION for an app/service in one `.env` file at
125
+ `<stashDir>/env/<name>.env`, sourced/injected wholesale. Key names + comments
126
+ are discoverable; values stay on disk and never reach stdout or the index. akm
127
+ does not edit entries — you edit the file with your own editor and akm loads it.
126
128
 
127
129
  ```sh
128
- akm vault create prod # Create a new vault
129
- akm vault set prod DB_URL postgres://... # Set a key (or KEY=VALUE combined form)
130
- akm vault set prod DB_URL=postgres://... # Combined KEY=VALUE form also works
131
- akm vault unset prod DB_URL # Remove a key
132
- akm vault list # List all vaults across all stashes with key names
133
- akm vault path vault:prod # Print the vault file path for shell loading
134
- akm vault run vault:prod -- env # Run one command with all vault vars injected
135
- akm vault run vault:prod/DB_URL -- printenv DB_URL # Inject one key for one command
130
+ akm env create prod # Create an empty env file
131
+ akm env create prod --from-file ./.env # Ingest an existing .env
132
+ akm env list # List all env files across stashes with key names
133
+ akm show env:prod # Inspect key names + comments (never values)
134
+ akm env run env:prod -- ./deploy.sh # Run a command with the whole .env injected (the safe path)
135
+ akm env run env:prod -- $SHELL # Open an interactive shell with values injected
136
+ akm env export env:prod --out ./env.sh # Write a sourceable script to a file (mode 0600)
137
+ akm env path env:prod --quiet # Print the raw file path (for Docker `_FILE` / `--env-file`)
138
+ akm env remove env:prod # Delete the env file
139
+ ```
140
+
141
+ ## Secrets
142
+
143
+ A single sensitive value used on its own for authentication (a token, key, or
144
+ cert) — one file = one value at `<stashDir>/secrets/<name>`. The ENTIRE file is
145
+ the value; only the name is ever surfaced.
146
+
147
+ ```sh
148
+ printf '%s' "$TOKEN" | akm secret set secret:deploy-token # Store a single value
149
+ akm secret list # List secrets (names only)
150
+ akm secret path secret:deploy-token # Print the file path (Docker `_FILE`)
151
+ akm secret run secret:deploy-token GITHUB_TOKEN -- gh release create v1.0.0 # Inject into one env var
152
+ akm secret remove secret:deploy-token # Delete the secret
136
153
  ```
137
154
 
138
155
  ## Workflows
@@ -171,8 +188,7 @@ When `--dest` is provided, `akm init` is not required first.
171
188
  ## Sync
172
189
 
173
190
  Commit local changes in a git-backed stash. Behaviour adapts automatically.
174
- (`akm save` is the deprecated 0.7 spelling it still works but warns; removed
175
- in 0.9.0.)
191
+ (`akm save` was the pre-0.8 spelling; it was removed in 0.9.0 — use `akm sync`.)
176
192
 
177
193
  - **No `.git` directory** — no-op (silent skip)
178
194
  - **Git repo, no remote** — stage and commit only (the default stash always falls here)
@@ -279,8 +295,8 @@ akm proposal revert <id> # Restore the pre-promot
279
295
  ```
280
296
 
281
297
  The flat verbs `akm proposals` / `akm show proposal` / `akm accept` /
282
- `akm reject` / `akm diff` / `akm revert` still work as deprecated aliases
283
- (warn on stderr; removed in 0.9.0).
298
+ `akm reject` / `akm diff` / `akm revert` were removed in 0.9.0 — use the
299
+ `akm proposal <verb>` forms above.
284
300
 
285
301
  Per-task `timeoutMs`: task markdown frontmatter may set `timeoutMs: null` to
286
302
  disable the agent kill timer for long-running local-model tasks, or a number
@@ -300,6 +316,5 @@ All commands accept `--format`, `--detail`, and `--shape` flags:
300
316
  - `--shape human` (default) — standard projection
301
317
  - `--shape agent` — agent-optimized output: strips non-actionable fields
302
318
  - `--shape summary` — metadata only (no content/template/prompt), under 200 tokens; only valid on `akm show`
303
- - `--for-agent` — deprecated alias for `--shape agent` (removed 0.9.0)
304
319
 
305
320
  Run `akm -h` or `akm <command> -h` for per-command help.
@@ -58,7 +58,7 @@ akm registry search "<query>" # Search all registries
58
58
  | knowledge | A reference doc (use `toc` or `section "..."` to navigate) |
59
59
  | workflow | Parsed steps plus workflow-specific execution commands |
60
60
  | memory | Recalled context (read the content for background information) |
61
- | env | A `.env` file of related CONFIGURATION (many vars; sensitive or not — all protected); key names only. Inject with `akm env run <ref> -- <cmd>` (the agent-safe path — values stay on disk). `vault` is the deprecated alias. |
61
+ | env | A `.env` file of related CONFIGURATION (many vars; sensitive or not — all protected); key names only. Inject with `akm env run <ref> -- <cmd>` (the agent-safe path — values stay on disk). |
62
62
  | secret | A single sensitive value for AUTHENTICATION (token, key, cert); name only. Use `akm secret path` / `akm secret run`. |
63
63
  | wiki | A page in a multi-wiki knowledge base. For any wiki task, start with `akm wiki list`. To ingest sources, run `akm wiki ingest <name>` — it dispatches the configured agent profile to execute the ingest workflow against the wiki's `raw/` directory. Run `akm wiki -h` for the full surface. |
64
64
 
@@ -0,0 +1,13 @@
1
+ {
2
+ "description": "Manual catch-up — consolidation + triage drain with no interval minimum.",
3
+ "processes": {
4
+ "reflect": { "enabled": false },
5
+ "distill": { "enabled": false },
6
+ "consolidate": { "enabled": true, "allowedTypes": ["memory"], "maxChunkSize": 50, "minPoolSize": 0 },
7
+ "memoryInference": { "enabled": false },
8
+ "graphExtraction": { "enabled": false },
9
+ "extract": { "enabled": false },
10
+ "triage": { "enabled": true, "applyMode": "queue", "policy": "personal-stash", "maxAcceptsPerRun": 100 }
11
+ },
12
+ "sync": { "enabled": true, "push": true }
13
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "description": "Consolidation-only pass — merges, deduplicates, and prunes memories every 4h.",
3
+ "processes": {
4
+ "reflect": { "enabled": false },
5
+ "distill": { "enabled": false },
6
+ "consolidate": { "enabled": true, "allowedTypes": ["memory"], "maxChunkSize": 25, "minPoolSize": 500 },
7
+ "memoryInference": { "enabled": false },
8
+ "graphExtraction": { "enabled": false },
9
+ "extract": { "enabled": false },
10
+ "triage": { "enabled": false }
11
+ },
12
+ "sync": { "enabled": true, "push": true }
13
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "description": "Frequent extract + inference pass — sessions, memory inference, graph, reflect.",
3
+ "processes": {
4
+ "reflect": { "enabled": true },
5
+ "distill": { "enabled": false },
6
+ "consolidate": { "enabled": false },
7
+ "memoryInference": { "enabled": true },
8
+ "graphExtraction": { "enabled": true },
9
+ "extract": { "enabled": true, "minNewSessions": 3 },
10
+ "triage": { "enabled": false }
11
+ },
12
+ "sync": { "enabled": true, "push": true }
13
+ }
@@ -0,0 +1,4 @@
1
+ schedule: "0 3 * * 0"
2
+ command: akm db backups
3
+ enabled: true
4
+ description: Weekly config/DB backup
@@ -0,0 +1,4 @@
1
+ schedule: "*/30 * * * *"
2
+ command: akm extract
3
+ enabled: true
4
+ description: Extract insights from session files every 30 min
@@ -0,0 +1,4 @@
1
+ schedule: "0 2 * * *"
2
+ command: akm improve --auto-accept safe
3
+ enabled: true
4
+ description: Run improve pipeline nightly
@@ -0,0 +1,4 @@
1
+ schedule: "0 4 * * *"
2
+ command: akm index
3
+ enabled: true
4
+ description: Nightly incremental index refresh
@@ -0,0 +1,4 @@
1
+ schedule: "*/15 * * * *"
2
+ command: akm sync
3
+ enabled: true
4
+ description: Sync stash changes to git remote every 15 min
@@ -0,0 +1,4 @@
1
+ schedule: "0 1 * * *"
2
+ command: akm update
3
+ enabled: true
4
+ description: Pull latest changes for all managed stash sources
@@ -0,0 +1,4 @@
1
+ schedule: "0 9 * * 1"
2
+ command: akm info --check-version
3
+ enabled: true
4
+ description: Weekly check for new akm releases
@@ -3,12 +3,12 @@
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
4
  import fs from "node:fs";
5
5
  import path from "node:path";
6
- import { stripJsonComments } from "../core/config";
7
- import { unifiedDiff, withConfigLock, writeConfigAtomic } from "../core/config-io";
8
- import { migrateConfigShape } from "../core/config-migration";
9
- import { getCacheDir, getConfigPath } from "../core/paths";
10
- import { warn } from "../core/warn";
11
- export { migrateConfigShape } from "../core/config-migration";
6
+ import { stripJsonComments } from "../core/config/config.js";
7
+ import { unifiedDiff, withConfigLock, writeConfigAtomic } from "../core/config/config-io.js";
8
+ import { migrateConfigShape } from "../core/config/config-migration.js";
9
+ import { getCacheDir, getConfigPath } from "../core/paths.js";
10
+ import { warn } from "../core/warn.js";
11
+ export { migrateConfigShape } from "../core/config/config-migration.js";
12
12
  const PROJECT_CONFIG_RELATIVE_PATH = path.join(".akm", "config.json");
13
13
  function backupConfigFile(configPath) {
14
14
  if (!fs.existsSync(configPath))
@@ -9,10 +9,10 @@
9
9
  * Exits non-zero on errors so it composes well in CI hooks.
10
10
  */
11
11
  import fs from "node:fs";
12
- import { parseConfigText } from "../core/config-io";
13
- import { validateConfigShape } from "../core/config-schema";
14
- import { ConfigError } from "../core/errors";
15
- import { getConfigPath } from "../core/paths";
12
+ import { parseConfigText } from "../core/config/config-io.js";
13
+ import { validateConfigShape } from "../core/config/config-schema.js";
14
+ import { ConfigError } from "../core/errors.js";
15
+ import { getConfigPath } from "../core/paths.js";
16
16
  export async function runConfigValidate() {
17
17
  const configPath = getConfigPath();
18
18
  if (!fs.existsSync(configPath)) {
@@ -7,7 +7,7 @@
7
7
  * ## Usage
8
8
  *
9
9
  * ```ts
10
- * import { confirmDestructive } from "../cli/confirm";
10
+ * import { confirmDestructive } from "../cli/confirm.js";
11
11
  *
12
12
  * async function run({ args }) {
13
13
  * const yes = await confirmDestructive(
@@ -26,7 +26,7 @@
26
26
  * accidental destruction in scripts that forget to pass `-y`.
27
27
  *
28
28
  * This is intentionally stricter than many CLIs that silently proceed in
29
- * non-TTY mode. The rationale: vault keys, sources, and proposals cannot be
29
+ * non-TTY mode. The rationale: secrets, sources, and proposals cannot be
30
30
  * recovered after deletion, so the cost of requiring `--yes` in scripts is
31
31
  * low and the cost of accidental deletion is high.
32
32
  *
@@ -36,7 +36,7 @@
36
36
  * output. The auto-migration banner is similarly exempt from `--quiet`.
37
37
  */
38
38
  import * as p from "@clack/prompts";
39
- import { UsageError } from "../core/errors";
39
+ import { UsageError } from "../core/errors.js";
40
40
  /**
41
41
  * Prompt the user to confirm a destructive action.
42
42
  *
@@ -7,7 +7,7 @@
7
7
  * These were extracted from `src/cli.ts` to eliminate repetition and keep the
8
8
  * main CLI file focused on command definitions and routing.
9
9
  */
10
- import { UsageError } from "../core/errors";
10
+ import { UsageError } from "../core/errors.js";
11
11
  // ── Subcommand detection ─────────────────────────────────────────────────────
12
12
  /**
13
13
  * Return true when `args._[0]` is a member of `validSet`.
@@ -7,11 +7,13 @@
7
7
  *
8
8
  * Exported: output, runWithJsonErrors, parseAllFlagValues, emitJsonError
9
9
  */
10
+ import { defineCommand } from "citty";
10
11
  import { stringify as yamlStringify } from "yaml";
11
- import { ConfigError, NotFoundError, UsageError } from "../core/errors";
12
- import { getOutputMode } from "../output/context";
13
- import { shapeForCommand } from "../output/shapes";
14
- import { formatPlain, outputJsonl } from "../output/text";
12
+ import { assertNever } from "../core/assert.js";
13
+ import { AkmError } from "../core/errors.js";
14
+ import { getOutputMode } from "../output/context.js";
15
+ import { shapeForCommand } from "../output/shapes.js";
16
+ import { formatPlain, outputJsonl } from "../output/text.js";
15
17
  // ── Exit codes ───────────────────────────────────────────────────────────────
16
18
  /**
17
19
  * Canonical process exit-code table for the akm CLI. Single source of truth —
@@ -22,6 +24,7 @@ import { formatPlain, outputJsonl } from "../output/text";
22
24
  * 1 general / not-found
23
25
  * 2 usage error
24
26
  * 4 health warn (health command only)
27
+ * 70 internal / unclassified (sysexits EX_SOFTWARE — akm threw unexpectedly)
25
28
  * 78 config error
26
29
  */
27
30
  export const EXIT_CODES = {
@@ -29,17 +32,35 @@ export const EXIT_CODES = {
29
32
  GENERAL: 1,
30
33
  USAGE: 2,
31
34
  HEALTH_WARN: 4,
35
+ // sysexits.h EX_SOFTWARE. Distinct from GENERAL(1) so scripts can tell an
36
+ // expected "not found" outcome from akm itself throwing an unexpected error.
37
+ INTERNAL: 70,
32
38
  CONFIG: 78,
33
39
  };
34
40
  // ── Helpers ──────────────────────────────────────────────────────────────────
41
+ /**
42
+ * Map a thrown value to a process exit code.
43
+ *
44
+ * Known, classified errors (instances of `AkmError`) are dispatched through an
45
+ * exhaustive switch on the `kind` discriminant — `assertNever` makes a missing
46
+ * case a compile-time error, so adding a new error class can't silently inherit
47
+ * the wrong code. Anything that is NOT an `AkmError` is treated as a genuinely
48
+ * unexpected internal failure and maps to INTERNAL(70) rather than GENERAL(1),
49
+ * so callers can distinguish "akm threw" from a normal not-found outcome.
50
+ */
35
51
  function classifyExitCode(error) {
36
- if (error instanceof UsageError)
37
- return EXIT_CODES.USAGE;
38
- if (error instanceof ConfigError)
39
- return EXIT_CODES.CONFIG;
40
- if (error instanceof NotFoundError)
41
- return EXIT_CODES.GENERAL;
42
- return EXIT_CODES.GENERAL;
52
+ if (!(error instanceof AkmError))
53
+ return EXIT_CODES.INTERNAL;
54
+ switch (error.kind) {
55
+ case "usage":
56
+ return EXIT_CODES.USAGE;
57
+ case "config":
58
+ return EXIT_CODES.CONFIG;
59
+ case "not-found":
60
+ return EXIT_CODES.GENERAL;
61
+ default:
62
+ return assertNever(error.kind, "classifyExitCode");
63
+ }
43
64
  }
44
65
  function extractHint(error) {
45
66
  if (error instanceof Error && "hint" in error && typeof error.hint === "function") {
@@ -55,9 +76,9 @@ export function emitJsonError(error) {
55
76
  const message = error instanceof Error ? error.message : String(error);
56
77
  const hint = extractHint(error);
57
78
  const exitCode = classifyExitCode(error);
58
- const code = error instanceof UsageError || error instanceof ConfigError || error instanceof NotFoundError
59
- ? error.code
60
- : undefined;
79
+ // Classified akm errors carry a stable machine-readable `code`; unexpected
80
+ // internal errors have none.
81
+ const code = error instanceof AkmError ? error.code : undefined;
61
82
  console.error(JSON.stringify({ ok: false, error: message, ...(code ? { code } : {}), hint }, null, 2));
62
83
  process.exit(exitCode);
63
84
  }
@@ -73,6 +94,22 @@ export async function runWithJsonErrors(fn) {
73
94
  emitJsonError(error);
74
95
  }
75
96
  }
97
+ /**
98
+ * Define a citty command whose `run` body is automatically wrapped in
99
+ * `runWithJsonErrors`, so the handler emits a byte-identical JSON error
100
+ * envelope (stdout/stderr/exit-code) on throw without the boilerplate. A
101
+ * command without a `run` (a pure subcommand group) is passed through
102
+ * unchanged.
103
+ */
104
+ export function defineJsonCommand(def) {
105
+ const { run, ...rest } = def;
106
+ if (!run)
107
+ return defineCommand({ ...rest });
108
+ return defineCommand({
109
+ ...rest,
110
+ run: (context) => runWithJsonErrors(() => run(context)),
111
+ });
112
+ }
76
113
  /**
77
114
  * Render a command result according to the active output mode (json/jsonl/yaml/text).
78
115
  */
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+ // This Source Code Form is subject to the terms of the Mozilla Public
3
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
5
+
6
+ // Node entry wrapper for akm.
7
+ //
8
+ // The primary entry `dist/cli.js` carries a `#!/usr/bin/env bun` shebang and,
9
+ // when run under Node, relies on a text-import loader hook for its embedded
10
+ // `.md`/`.xml` assets (Bun loads those natively; Node needs a hook). Those
11
+ // imports are static and hoisted, so the hook MUST be registered before the
12
+ // module graph is evaluated — i.e. before `cli.js` is imported. This wrapper
13
+ // does exactly that: register the hook, then dynamically import the real CLI.
14
+ //
15
+ // Bun users never touch this file; it exists solely so `node dist/cli-node.mjs`
16
+ // (and the `test:node-smoke` script / Node CI matrix) can run akm end-to-end.
17
+
18
+ import { register } from "node:module";
19
+
20
+ register("./text-import-hook.mjs", import.meta.url);
21
+
22
+ // cli.js gates its startup block on `import.meta.main`, which is false when we
23
+ // `import()` it here. Opt in explicitly so the CLI actually runs `runMain`.
24
+ process.env.AKM_NODE_ENTRY = "1";
25
+
26
+ await import("./cli.js");