akm-cli 0.7.5 → 0.8.0-rc.11

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 (300) hide show
  1. package/{.github/CHANGELOG.md → CHANGELOG.md} +192 -2
  2. package/README.md +22 -6
  3. package/SECURITY.md +93 -0
  4. package/dist/cli/config-migrate.js +144 -0
  5. package/dist/cli/config-validate.js +39 -0
  6. package/dist/cli/confirm.js +73 -0
  7. package/dist/cli/parse-args.js +133 -0
  8. package/dist/cli/shared.js +129 -0
  9. package/dist/cli.js +2569 -1449
  10. package/dist/commands/add-cli.js +279 -0
  11. package/dist/commands/agent-dispatch.js +110 -0
  12. package/dist/commands/agent-support.js +68 -0
  13. package/dist/commands/completions.js +3 -0
  14. package/dist/commands/config-cli.js +130 -534
  15. package/dist/commands/consolidate.js +2122 -0
  16. package/dist/commands/curate.js +44 -3
  17. package/dist/commands/db-cli.js +23 -0
  18. package/dist/commands/distill-promotion-policy.js +660 -0
  19. package/dist/commands/distill.js +1075 -77
  20. package/dist/commands/env.js +213 -0
  21. package/dist/commands/eval-cases.js +43 -0
  22. package/dist/commands/events.js +5 -23
  23. package/dist/commands/extract-cli.js +127 -0
  24. package/dist/commands/extract-prompt.js +204 -0
  25. package/dist/commands/extract.js +477 -0
  26. package/dist/commands/feedback-cli.js +331 -0
  27. package/dist/commands/graph.js +477 -0
  28. package/dist/commands/health.js +1302 -0
  29. package/dist/commands/help/help-accept.md +12 -0
  30. package/dist/commands/help/help-improve.md +69 -0
  31. package/dist/commands/help/help-proposals.md +18 -0
  32. package/dist/commands/help/help-propose.md +17 -0
  33. package/dist/commands/help/help-reject.md +11 -0
  34. package/dist/commands/history.js +54 -46
  35. package/dist/commands/improve-auto-accept.js +97 -0
  36. package/dist/commands/improve-cli.js +217 -0
  37. package/dist/commands/improve-profiles.js +166 -0
  38. package/dist/commands/improve-result-file.js +167 -0
  39. package/dist/commands/improve.js +2373 -0
  40. package/dist/commands/info.js +5 -2
  41. package/dist/commands/init.js +50 -2
  42. package/dist/commands/installed-stashes.js +102 -139
  43. package/dist/commands/knowledge.js +136 -0
  44. package/dist/commands/lint/agent-linter.js +49 -0
  45. package/dist/commands/lint/base-linter.js +479 -0
  46. package/dist/commands/lint/command-linter.js +49 -0
  47. package/dist/commands/lint/default-linter.js +16 -0
  48. package/dist/commands/lint/env-key-rules.js +154 -0
  49. package/dist/commands/lint/index.js +196 -0
  50. package/dist/commands/lint/knowledge-linter.js +16 -0
  51. package/dist/commands/lint/markdown-insertion.js +343 -0
  52. package/dist/commands/lint/memory-linter.js +61 -0
  53. package/dist/commands/lint/registry.js +36 -0
  54. package/dist/commands/lint/skill-linter.js +45 -0
  55. package/dist/commands/lint/task-linter.js +50 -0
  56. package/dist/commands/lint/types.js +4 -0
  57. package/dist/commands/lint/workflow-linter.js +56 -0
  58. package/dist/commands/lint.js +4 -0
  59. package/dist/commands/migration-help.js +5 -2
  60. package/dist/commands/proposal.js +67 -12
  61. package/dist/commands/propose.js +86 -31
  62. package/dist/commands/reflect.js +1091 -73
  63. package/dist/commands/registry-cli.js +150 -0
  64. package/dist/commands/registry-search.js +5 -2
  65. package/dist/commands/remember-cli.js +257 -0
  66. package/dist/commands/remember.js +69 -6
  67. package/dist/commands/schema-repair.js +203 -0
  68. package/dist/commands/search.js +115 -14
  69. package/dist/commands/secret.js +173 -0
  70. package/dist/commands/self-update.js +3 -0
  71. package/dist/commands/show.js +148 -25
  72. package/dist/commands/source-add.js +17 -45
  73. package/dist/commands/source-clone.js +3 -0
  74. package/dist/commands/source-manage.js +14 -19
  75. package/dist/commands/tasks.js +437 -0
  76. package/dist/commands/url-checker.js +42 -0
  77. package/dist/core/action-contributors.js +28 -0
  78. package/dist/core/asset-ref.js +17 -2
  79. package/dist/core/asset-registry.js +12 -17
  80. package/dist/core/asset-serialize.js +88 -0
  81. package/dist/core/asset-spec.js +67 -1
  82. package/dist/core/common.js +182 -0
  83. package/dist/core/concurrent.js +25 -0
  84. package/dist/core/config-io.js +347 -0
  85. package/dist/core/config-migration.js +622 -0
  86. package/dist/core/config-schema.js +534 -0
  87. package/dist/core/config-sources.js +108 -0
  88. package/dist/core/config-types.js +4 -0
  89. package/dist/core/config-walker.js +337 -0
  90. package/dist/core/config.js +364 -981
  91. package/dist/core/errors.js +42 -20
  92. package/dist/core/events.js +91 -138
  93. package/dist/core/file-lock.js +104 -0
  94. package/dist/core/frontmatter.js +75 -8
  95. package/dist/core/lesson-lint.js +3 -0
  96. package/dist/core/markdown.js +20 -0
  97. package/dist/core/memory-belief.js +62 -0
  98. package/dist/core/memory-contradiction-detect.js +274 -0
  99. package/dist/core/memory-improve.js +806 -0
  100. package/dist/core/parse.js +158 -0
  101. package/dist/core/paths.js +280 -14
  102. package/dist/core/proposal-quality-validators.js +380 -0
  103. package/dist/core/proposal-validators.js +69 -0
  104. package/dist/core/proposals.js +512 -42
  105. package/dist/core/state-db.js +1068 -0
  106. package/dist/core/text-truncation.js +107 -0
  107. package/dist/core/time.js +54 -0
  108. package/dist/core/tty.js +59 -0
  109. package/dist/core/warn.js +64 -1
  110. package/dist/core/write-source.js +3 -0
  111. package/dist/indexer/db-backup.js +391 -0
  112. package/dist/indexer/db-search.js +178 -256
  113. package/dist/indexer/db.js +975 -103
  114. package/dist/indexer/ensure-index.js +64 -0
  115. package/dist/indexer/file-context.js +3 -0
  116. package/dist/indexer/graph-boost.js +376 -101
  117. package/dist/indexer/graph-db.js +391 -0
  118. package/dist/indexer/graph-dedup.js +95 -0
  119. package/dist/indexer/graph-extraction.js +550 -124
  120. package/dist/indexer/index-context.js +4 -0
  121. package/dist/indexer/indexer.js +523 -301
  122. package/dist/indexer/llm-cache.js +52 -0
  123. package/dist/indexer/manifest.js +3 -0
  124. package/dist/indexer/matchers.js +167 -160
  125. package/dist/indexer/memory-inference.js +152 -74
  126. package/dist/indexer/metadata-contributors.js +29 -0
  127. package/dist/indexer/metadata.js +275 -196
  128. package/dist/indexer/path-resolver.js +92 -0
  129. package/dist/indexer/project-context.js +192 -0
  130. package/dist/indexer/ranking-contributors.js +331 -0
  131. package/dist/indexer/ranking.js +81 -0
  132. package/dist/indexer/search-fields.js +5 -9
  133. package/dist/indexer/search-hit-enrichers.js +111 -0
  134. package/dist/indexer/search-source.js +44 -10
  135. package/dist/indexer/semantic-status.js +6 -17
  136. package/dist/indexer/staleness-detect.js +447 -0
  137. package/dist/indexer/usage-events.js +12 -9
  138. package/dist/indexer/walker.js +28 -0
  139. package/dist/integrations/agent/builders.js +135 -0
  140. package/dist/integrations/agent/config.js +122 -230
  141. package/dist/integrations/agent/detect.js +3 -0
  142. package/dist/integrations/agent/index.js +7 -13
  143. package/dist/integrations/agent/model-aliases.js +55 -0
  144. package/dist/integrations/agent/profiles.js +70 -5
  145. package/dist/integrations/agent/prompts.js +214 -80
  146. package/dist/integrations/agent/runner.js +151 -0
  147. package/dist/integrations/agent/sdk-runner.js +126 -0
  148. package/dist/integrations/agent/spawn.js +118 -23
  149. package/dist/integrations/github.js +3 -0
  150. package/dist/integrations/lockfile.js +32 -69
  151. package/dist/integrations/session-logs/index.js +69 -0
  152. package/dist/integrations/session-logs/inline-refs.js +35 -0
  153. package/dist/integrations/session-logs/pre-filter.js +152 -0
  154. package/dist/integrations/session-logs/providers/claude-code.js +282 -0
  155. package/dist/integrations/session-logs/providers/opencode.js +258 -0
  156. package/dist/integrations/session-logs/types.js +4 -0
  157. package/dist/llm/call-ai.js +62 -0
  158. package/dist/llm/client.js +77 -124
  159. package/dist/llm/embedder.js +20 -29
  160. package/dist/llm/embedders/cache.js +3 -7
  161. package/dist/llm/embedders/local.js +42 -1
  162. package/dist/llm/embedders/remote.js +20 -8
  163. package/dist/llm/embedders/types.js +3 -7
  164. package/dist/llm/feature-gate.js +95 -48
  165. package/dist/llm/graph-extract.js +676 -70
  166. package/dist/llm/index-passes.js +44 -29
  167. package/dist/llm/memory-infer.js +77 -71
  168. package/dist/llm/metadata-enhance.js +42 -29
  169. package/dist/llm/prompts/extract-session.md +80 -0
  170. package/dist/llm/prompts/graph-extract-user-prompt.md +35 -0
  171. package/dist/output/cli-hints-full.md +292 -0
  172. package/dist/output/cli-hints-short.md +66 -0
  173. package/dist/output/cli-hints.js +7 -320
  174. package/dist/output/context.js +60 -8
  175. package/dist/output/renderers.js +300 -257
  176. package/dist/output/shapes/curate.js +56 -0
  177. package/dist/output/shapes/distill.js +10 -0
  178. package/dist/output/shapes/env-list.js +19 -0
  179. package/dist/output/shapes/events.js +11 -0
  180. package/dist/output/shapes/helpers.js +424 -0
  181. package/dist/output/shapes/history.js +7 -0
  182. package/dist/output/shapes/passthrough.js +102 -0
  183. package/dist/output/shapes/proposal-accept.js +7 -0
  184. package/dist/output/shapes/proposal-diff.js +7 -0
  185. package/dist/output/shapes/proposal-list.js +7 -0
  186. package/dist/output/shapes/proposal-producer.js +11 -0
  187. package/dist/output/shapes/proposal-reject.js +7 -0
  188. package/dist/output/shapes/proposal-show.js +7 -0
  189. package/dist/output/shapes/registry-search.js +6 -0
  190. package/dist/output/shapes/registry.js +30 -0
  191. package/dist/output/shapes/search.js +6 -0
  192. package/dist/output/shapes/secret-list.js +19 -0
  193. package/dist/output/shapes/show.js +6 -0
  194. package/dist/output/shapes/vault-list.js +19 -0
  195. package/dist/output/shapes.js +51 -516
  196. package/dist/output/text/add.js +6 -0
  197. package/dist/output/text/clone.js +6 -0
  198. package/dist/output/text/config.js +6 -0
  199. package/dist/output/text/curate.js +6 -0
  200. package/dist/output/text/distill.js +7 -0
  201. package/dist/output/text/enable-disable.js +7 -0
  202. package/dist/output/text/events.js +10 -0
  203. package/dist/output/text/feedback.js +6 -0
  204. package/dist/output/text/helpers.js +1039 -0
  205. package/dist/output/text/history.js +7 -0
  206. package/dist/output/text/import.js +6 -0
  207. package/dist/output/text/index.js +6 -0
  208. package/dist/output/text/info.js +6 -0
  209. package/dist/output/text/init.js +6 -0
  210. package/dist/output/text/list.js +6 -0
  211. package/dist/output/text/proposal-producer.js +8 -0
  212. package/dist/output/text/proposal.js +11 -0
  213. package/dist/output/text/registry-commands.js +11 -0
  214. package/dist/output/text/registry.js +30 -0
  215. package/dist/output/text/remember.js +6 -0
  216. package/dist/output/text/remove.js +6 -0
  217. package/dist/output/text/save.js +6 -0
  218. package/dist/output/text/search.js +6 -0
  219. package/dist/output/text/show.js +6 -0
  220. package/dist/output/text/update.js +6 -0
  221. package/dist/output/text/upgrade.js +6 -0
  222. package/dist/output/text/vault.js +16 -0
  223. package/dist/output/text/wiki.js +15 -0
  224. package/dist/output/text/workflow.js +14 -0
  225. package/dist/output/text.js +44 -1092
  226. package/dist/registry/build-index.js +3 -0
  227. package/dist/registry/create-provider-registry.js +3 -0
  228. package/dist/registry/factory.js +4 -1
  229. package/dist/registry/origin-resolve.js +3 -0
  230. package/dist/registry/providers/index.js +3 -0
  231. package/dist/registry/providers/skills-sh.js +71 -50
  232. package/dist/registry/providers/static-index.js +53 -48
  233. package/dist/registry/providers/types.js +3 -24
  234. package/dist/registry/resolve.js +11 -16
  235. package/dist/registry/types.js +3 -0
  236. package/dist/scripts/migrate-storage.js +17750 -0
  237. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +9031 -0
  238. package/dist/scripts/migrations/v16-to-v17.js +141 -0
  239. package/dist/setup/detect.js +3 -0
  240. package/dist/setup/ripgrep-install.js +3 -0
  241. package/dist/setup/ripgrep-resolve.js +3 -0
  242. package/dist/setup/setup.js +775 -37
  243. package/dist/setup/steps.js +3 -15
  244. package/dist/sources/include.js +3 -0
  245. package/dist/sources/provider-factory.js +5 -12
  246. package/dist/sources/provider.js +3 -20
  247. package/dist/sources/providers/filesystem.js +19 -23
  248. package/dist/sources/providers/git.js +138 -21
  249. package/dist/sources/providers/index.js +3 -0
  250. package/dist/sources/providers/install-types.js +3 -13
  251. package/dist/sources/providers/npm.js +3 -4
  252. package/dist/sources/providers/provider-utils.js +3 -0
  253. package/dist/sources/providers/sync-from-ref.js +3 -11
  254. package/dist/sources/providers/tar-utils.js +3 -0
  255. package/dist/sources/providers/website.js +18 -22
  256. package/dist/sources/resolve.js +3 -0
  257. package/dist/sources/types.js +3 -0
  258. package/dist/sources/website-ingest.js +7 -0
  259. package/dist/tasks/backends/cron.js +203 -0
  260. package/dist/tasks/backends/exec-utils.js +28 -0
  261. package/dist/tasks/backends/index.js +24 -0
  262. package/dist/tasks/backends/launchd-template.xml +19 -0
  263. package/dist/tasks/backends/launchd.js +187 -0
  264. package/dist/tasks/backends/schtasks-template.xml +29 -0
  265. package/dist/tasks/backends/schtasks.js +215 -0
  266. package/dist/tasks/parser.js +211 -0
  267. package/dist/tasks/resolveAkmBin.js +87 -0
  268. package/dist/tasks/runner.js +458 -0
  269. package/dist/tasks/schedule.js +227 -0
  270. package/dist/tasks/schema.js +15 -0
  271. package/dist/tasks/validator.js +62 -0
  272. package/dist/version.js +3 -0
  273. package/dist/wiki/index-template.md +12 -0
  274. package/dist/wiki/ingest-workflow-template.md +54 -0
  275. package/dist/wiki/log-template.md +8 -0
  276. package/dist/wiki/schema-template.md +61 -0
  277. package/dist/wiki/wiki-templates.js +15 -0
  278. package/dist/wiki/wiki.js +13 -61
  279. package/dist/workflows/authoring.js +8 -25
  280. package/dist/workflows/cli.js +3 -0
  281. package/dist/workflows/db.js +140 -10
  282. package/dist/workflows/document-cache.js +3 -10
  283. package/dist/workflows/parser.js +3 -0
  284. package/dist/workflows/renderer.js +11 -3
  285. package/dist/workflows/runs.js +77 -92
  286. package/dist/workflows/schema.js +3 -0
  287. package/dist/workflows/scope-key.js +3 -0
  288. package/dist/workflows/validator.js +4 -8
  289. package/dist/workflows/workflow-template.md +24 -0
  290. package/docs/README.md +10 -2
  291. package/docs/data-and-telemetry.md +225 -0
  292. package/docs/migration/release-notes/0.7.0.md +1 -1
  293. package/docs/migration/release-notes/0.7.5.md +2 -2
  294. package/docs/migration/release-notes/0.8.0.md +48 -0
  295. package/docs/migration/v0.7-to-v0.8.md +1307 -0
  296. package/package.json +30 -12
  297. package/.github/LICENSE +0 -374
  298. package/dist/commands/install-audit.js +0 -381
  299. package/dist/commands/vault.js +0 -328
  300. package/dist/templates/wiki-templates.js +0 -100
@@ -6,6 +6,196 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ### Performance
10
+
11
+ - **`akm consolidate`**: all-hot chunk early-exit. When every memory in a chunk
12
+ is `captureMode: hot` (user-explicit), the only operations the LLM could ever
13
+ propose are deletes — all refused unconditionally by the downstream guard.
14
+ Such chunks now skip the model entirely and are counted as `judgedNoAction`
15
+ up front, instead of relying on a prompt-level hint and spending a wasted
16
+ request. Mixed chunks are unaffected.
17
+
18
+ ## [0.8.0] - 2026-05-28
19
+
20
+ ### Breaking changes (deprecation aliases, removed 0.9.0)
21
+
22
+ The 0.8 line is the clean-break window for CLI ergonomics. Every rename below
23
+ keeps the **old spelling working** as a deprecated alias that prints a stderr
24
+ warning (never on stdout, so JSON consumers are unaffected) and delegates to the
25
+ canonical form. **All of these deprecated aliases are removed in 0.9.0.** See
26
+ [`docs/migration/v0.8-to-v0.9.md`](docs/migration/v0.8-to-v0.9.md) for the full
27
+ old → new table.
28
+
29
+ - **Proposal queue is now a noun group**: `akm proposal {list,show,diff,accept,reject,revert}`.
30
+ The flat verbs `akm proposals`, `akm show proposal <id>`, `akm accept`,
31
+ `akm reject`, `akm diff`, and `akm revert` are deprecated aliases.
32
+ Bare `akm proposal` behaves as `akm proposal list`.
33
+ - **`--detail` is now verbosity only** (`brief|normal|full`). The output
34
+ *projection* moved to a new **`--shape`** flag (`human|agent|summary`).
35
+ `--detail summary` and `--detail agent` are deprecated aliases that map to
36
+ `--shape summary` / `--shape agent`.
37
+ - **`--for-agent`** is a deprecated alias for `--shape agent`.
38
+ - **`--generator`** replaces `--source` on `accept` / `reject` / `history`
39
+ (which generator produced the proposal/event). `--source` is a deprecated
40
+ alias on **those three commands only** — it is unchanged on
41
+ `search` / `curate` / `graph` / `remember`, where it means "read from here".
42
+ - **`akm save` → `akm sync`** (commit + optional push; `sync` connotes push
43
+ better). `akm save` is a deprecated alias. `akm sync` adds `--no-push`.
44
+ - **`akm enable` / `akm disable` → `akm config enable` / `akm config disable`**.
45
+ The top-level `enable` / `disable` are deprecated aliases.
46
+ - **`akm events` → `akm log`**: `log` is an additive alias for the same
47
+ state.db stream in 0.8 and becomes primary in 0.9.0. (`akm history` remains the
48
+ asset-scoped, cross-source analytical trail — a different surface.)
49
+ - **`akm wiki remove --force` → `-y` / `--yes`** for skipping the confirmation
50
+ prompt. `wiki remove` now also *prompts* interactively when a TTY is present;
51
+ `--force` is a deprecated alias for `-y`.
52
+ - **`akm feedback --note` → `--reason`**: `--note` is a deprecated alias and
53
+ warns when used without `--reason`.
54
+ - **`akm workflow next --dry-run` removed**: the flag is no longer declared, so
55
+ it no longer appears in `--help`. The explicit "next does not support
56
+ --dry-run" guard remains (read from argv) so existing callers still get a clear
57
+ message instead of silent acceptance.
58
+ - **Singular aliases added** (additive, non-breaking): `akm task` for
59
+ `akm tasks`, `akm lesson` for `akm lessons`.
60
+
61
+ ### Safety
62
+
63
+ Two destructive paths that previously acted with no confirmation now guard
64
+ behind an interactive prompt (or `-y` / `--yes` in non-interactive use).
65
+ **Scripts that ran these non-interactively must add `-y`.**
66
+
67
+ - **`akm registry remove`** now confirms before splicing the registry out of the
68
+ config (`confirmDestructive`). Pass `-y` / `--yes` to skip the prompt;
69
+ non-interactive use without `-y` aborts.
70
+ - **Bulk `akm proposal accept --generator <g>`** (the multi-proposal branch) now
71
+ confirms before promoting every matching proposal, mirroring the existing
72
+ guard on bulk `reject`. Single-id accept stays unguarded (it is revertable).
73
+
74
+ ### Fixed
75
+
76
+ - **Consolidation `delete_failed` on stale index entries** — when consolidation
77
+ successfully deleted a memory file, the index DB was not re-indexed between
78
+ runs. Subsequent runs loaded the stale DB entry into their memory map, the LLM
79
+ re-proposed the deletion, and `deleteAssetFromSource` threw "not found in
80
+ source" — appearing as `delete_failed` in skipReasons. Fix: `loadMemoriesForSource`
81
+ now filters entries whose file no longer exists on disk before building chunks,
82
+ so phantom memories are never sent to the LLM. A secondary catch in the delete
83
+ handler emits `delete_already_gone` instead of `delete_failed` when the file
84
+ is confirmed absent.
85
+
86
+ > **CI / Docker users:** the 0.8.0 storage split moved `akm.lock`, the event
87
+ > database, and the registry cache out of `$XDG_CONFIG_HOME/akm/` into
88
+ > `$XDG_DATA_HOME`, `$XDG_STATE_HOME`, and `$XDG_CACHE_HOME` respectively. If
89
+ > you override any of `AKM_CONFIG_DIR`, `AKM_DATA_DIR`, `AKM_STATE_DIR`,
90
+ > `AKM_CACHE_DIR` in CI to isolate per-job state, set **all four** (or none,
91
+ > and rely on XDG defaults). Overriding only `AKM_CONFIG_DIR` will leave the
92
+ > lock file / event DB pointing at the host's default `$XDG_DATA_HOME`,
93
+ > causing lock contention and bleed between jobs.
94
+
95
+ ### Removed
96
+
97
+ - **Install-time security audit (`security.installAudit`) and the `--trust`
98
+ flag**. The audit scanned incoming stash assets for risky patterns (e.g.
99
+ `curl ... | bash`, "ignore previous instructions") and blocked installs on
100
+ critical findings. In practice it produced too many false positives on
101
+ benign documentation strings and forced first-time users to pass `--trust`
102
+ or twiddle config just to install the official stash. The whole feature is
103
+ gone:
104
+ - `akm add` and `akm update` no longer scan synced content.
105
+ - The `--trust` flag is removed from `akm add` and `akm wiki register`.
106
+ - The `security.installAudit.*` config keys (`enabled`, `blockOnCritical`,
107
+ `registryAllowlist`, `registryWhitelist`, `blockUnlistedRegistries`,
108
+ `allowedFindings`) are no longer recognised; the entire `security` block
109
+ is removed from the config schema.
110
+ - The `akm config set security.installAudit.*` keys now error as unknown.
111
+ - `audit` fields are removed from `AddResponse.installed` and
112
+ `SourceInstallStatus`.
113
+
114
+ ### Breaking Changes
115
+
116
+ - **Project-level `.akm/config.json` files are no longer merged**. The
117
+ multi-layer config discovery introduced in the 0.7 line was deprecated
118
+ in late-0.8.x with a warning; that warning is now backed by removal.
119
+ `loadConfig` walks cwd-ancestors only to emit a one-time deprecation
120
+ warning per discovered file. Move any needed settings to
121
+ `~/.config/akm/config.json`. `stashInheritance` (a multi-layer-only
122
+ field) is removed from the schema.
123
+
124
+ - **`${VAR}` env-var expansion only resolves at the apiKey consumption
125
+ sites**. The recursive expansion walker that ran on the load path is
126
+ gone. Other config string values now round-trip verbatim: a literal
127
+ `${HOME}` in (say) `stashDir` is preserved as the literal `${HOME}`
128
+ on read. The new exported `resolveSecret(value)` helper is applied
129
+ only where authorization headers are built (`src/llm/client.ts`,
130
+ `src/llm/embedders/remote.ts`, `src/integrations/agent/sdk-runner.ts`).
131
+ Documented `${OPENAI_API_KEY}` recipes in `docs/configuration.md`
132
+ continue to work because expansion still happens at request time for
133
+ apiKey fields.
134
+
135
+ - **`AKM_FORCE_DOWNGRADE_CONFIG` env var removed**. The newer-than-binary
136
+ read-only guard (`configReadOnlyReason`, `markConfigReadOnlyIfNewer`,
137
+ `getConfigReadOnlyReason`) is gone. Configs declaring a `configVersion`
138
+ newer than the running binary now save through silently — unknown
139
+ fields are stripped on save by `sanitizeConfigForWrite` plus the
140
+ strict-walled Zod schema. Users on 0.9.x configs should not open them
141
+ with a 0.8.x binary in writable workflows.
142
+
143
+ ### Changed
144
+
145
+ - **Rebrand**: the full name "Agent Kit Manager" is now **Agent Knowledge Management** — `akm` stands for Agent Knowledge Management going forward. The binary name, npm package (`akm-cli`), and all APIs remain unchanged.
146
+
147
+ - **Config layer rewrite** — single-source-of-truth Zod schema in
148
+ `src/core/config-schema.ts` replaces the per-field parse switch AND
149
+ the per-shape load-time parser. Adding a new config field is now one
150
+ line of schema + zero lines of CLI code. `loadConfig` now consists of
151
+ parse-text → migrate (pure JSON transforms) → Zod safeParse → overlay
152
+ defaults — a ~30-line pipeline that absorbs ~900 LOC of legacy
153
+ per-shape parsers (`parseLlmConfig`, `parseEmbeddingConfig`,
154
+ `parseIndexConfig`, `parseSourceConfigEntry`, and ~20 more).
155
+ - **#454**: `akm config set llm.apiKey` / `embedding.apiKey` /
156
+ `profiles.llm.<name>.apiKey` now throws `UsageError` pointing at the
157
+ corresponding env var (`AKM_LLM_API_KEY`, `AKM_EMBED_API_KEY`,
158
+ `AKM_PROFILE_<NAME>_API_KEY`). Was previously a silent strip.
159
+ - **#455**: every schema-leaf key is now reachable via `akm config set`.
160
+ Includes previously hand-listed gaps: `defaults.agent`, `search.minScore`,
161
+ `improve.eventRetentionDays`, `embedding.provider`, `llm.temperature`,
162
+ `profiles.llm.<name>.*`, `profiles.agent.<name>.*`, etc.
163
+ - **#456**: `akm config validate` and `akm config migrate` are now real
164
+ registered subcommands. The orphan implementations in `config-validate.ts`
165
+ have been removed; the new entry points live in `src/cli/`.
166
+ - **#457**: project-level `.akm/config.json` files are now flagged with a
167
+ deprecation warning ("will be ignored in 0.9.0+"). The merge still
168
+ happens in 0.8.x — one release of grace.
169
+ - **#458**: malformed JSON or non-object root in the config file now raises
170
+ `ConfigError("INVALID_CONFIG_FILE")` with the underlying parse error.
171
+ Was previously a silent fallback to `DEFAULT_CONFIG`, which masked
172
+ corruption. File-not-existing remains the legitimate cold-start case.
173
+ - **#459**: `~/.cache/akm/config-backups/` is now bounded to the 5 most
174
+ recent timestamped backups. Pruning runs on each `saveConfig`.
175
+ `config.latest.json` is preserved separately.
176
+ - **#460**: `UNKNOWN_CONFIG_KEY_HINT` is now auto-generated from the
177
+ schema via `listTopLevelConfigKeys()`. No more stale hand-maintained string.
178
+ - **#461**: if the auto-migration disk-write fails, `loadConfig` now throws
179
+ a hard error instead of returning the in-memory migrated shape. Eliminates
180
+ the silent infinite re-migrate loop on every `akm` command.
181
+ - **#462**: nested registries[], sources[], profiles.* objects are
182
+ `.strict()` — unknown keys are rejected with a path-pointing error at
183
+ both set time and saveConfig time.
184
+ - **#463**: `schemas/akm-config.json` is now auto-generated from the Zod
185
+ source via `bun scripts/gen-config-schema.ts`. A drift test fails CI if
186
+ the committed file disagrees with the regeneration output.
187
+ - **#464.a**: `defaultWriteTarget` is validated via Zod `.refine()` against
188
+ `sources[].name`. With no sources configured, save-time validation
189
+ rejects instead of silently accepting (no implicit "first writable" fallback).
190
+ - **#464.b**: generic unset works on `semanticSearchMode` and every other
191
+ key via the dotted-path walker.
192
+ - **#464.c**: all write paths route through `writeFileAtomic`.
193
+ - **#464.d**: duplicate `mergeSecurityConfig` / `mergeInstallAuditConfig`
194
+ in `config-cli.ts` are deleted; merging happens via re-parse through the
195
+ Zod schema.
196
+
197
+ See `docs/migration/v0.7-to-v0.8.md` for the user-facing migration guide.
198
+
9
199
  ## [0.7.5] - 2026-05-08
10
200
 
11
201
  ### Added
@@ -19,7 +209,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
19
209
  - **Workflow runs are now scoped to the current workspace** — ref-based workflow commands (`workflow next/status/list`) now resolve runs within the current project, worktree, or non-repo directory instead of sharing active-run state globally across the whole cache. Direct run-id commands still target the exact run.
20
210
  - **Help, hints, and workflow docs now explain run scoping** — CLI descriptions, embedded hints, operator docs, and workflow guides now describe the current-scope semantics so users understand how ref-based run resolution behaves across repos and local sandboxes.
21
211
  - **`akm show` auto-indexes stale state instead of falling back to raw filesystem reads** — show/search parity is tighter because stale index state now triggers refresh rather than silently drifting to a separate fallback path.
22
- - **Release metadata lookup follows the published `.github/CHANGELOG.md` layout** — migration-help, package publish metadata, and related docs now consistently reference the shipped changelog location.
212
+ - **Release metadata lookup follows the published `CHANGELOG.md` layout** — migration-help, package publish metadata, and related docs now consistently reference the shipped changelog location at the package root.
23
213
  - **Documentation refresh across README and posts** — README positioning, command-tour docs, workflow examples, and dev.to post organization were refreshed to better match the current CLI surface.
24
214
 
25
215
  ### Fixed
@@ -83,7 +273,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
83
273
  - **Proposal queue (`akm proposal *`)** (#225, #226, #233) — durable queue for proposal-producing commands. New verbs `akm proposal {list, show, diff, accept, reject}`. Promotion runs full validation before routing through `writeAssetToSource()`. Multiple proposals for the same `ref` coexist without filesystem collisions. Auto-accept is gated per-source via `autoAcceptProposals: true` (default off; requires a writable source). See v1 spec §11.
84
274
  - **`akm reflect`, `akm propose`, `akm distill`** (#225, #226, #227) — three new commands that write **only** to the proposal queue. `reflect` and `propose` shell out via the agent CLI (`agent.*` config); `distill` is the canonical bounded in-tree LLM call gated behind `llm.features.feedback_distillation`. Usage events `reflect_invoked`, `propose_invoked`, `distill_invoked`.
85
275
  - **`lesson` asset type** (#227) — first-class well-known type with required frontmatter `description` and `when_to_use`, stored under `lessons/<name>.md`. Normally produced by `akm distill <ref>` as a `proposed`-quality proposal and promoted via `akm proposal accept`.
86
- - **`llm.features.*` map with default-false gates** (#227, #284) — every bounded in-tree LLM call site is gated behind exactly one feature flag. Four keys ship: `curate_rerank`, `feedback_distillation`, `memory_inference`, `graph_extraction`. All defaults are `false`. Wrapper `tryLlmFeature(feature, config, fn, fallback)` in `src/llm/feature-gate.ts` guarantees disabled/throw/timeout fall back without crashing the call site. See v1 spec §14.
276
+ - **`llm.features.*` map with mixed defaults** (#227, #284) — every bounded in-tree LLM call site is gated behind exactly one feature flag. Four keys ship: `curate_rerank`, `feedback_distillation`, `memory_inference`, `graph_extraction`. `memory_inference` and `graph_extraction` default to `true`; the others default to `false`. Wrapper `tryLlmFeature(feature, config, fn, fallback)` in `src/llm/feature-gate.ts` guarantees disabled/throw/timeout fall back without crashing the call site. See v1 spec §14.
87
277
  - **`quality: "proposed"` and `--include-proposed`** — `SearchHit.quality` open string set; `proposed` is excluded from default search and surfaces only via `akm search ... --include-proposed` or `akm proposal *`. Unknown values parse-warn-include. `SearchHit` gains optional `quality?` and `warnings?` fields.
88
278
  - **`akm-bench` v1** (#234, PRs #266, #268, #269) — paired-utility benchmark framework. Track A runs each task with and without akm available and emits a comparable score pair; `akm-bench compare` aggregates paired runs into a delta report; `akm-bench attribute` maps utility deltas back to specific `[origin//]type:name` refs (Track B); `akm-bench evolve` is a stub for the closed-loop workflow that lands in 0.8.
89
279
  - **Operator env-var documentation** (#284 Wave B, PR #285) — `docs/configuration.md` now documents `AKM_NPM_REGISTRY`, `AKM_REGISTRY_URL`, `AKM_CACHE_DIR`, `HF_HOME`, and `GH_TOKEN`.
package/README.md CHANGED
@@ -1,23 +1,38 @@
1
- # akm -- Agent Kit Manager
1
+ # akm -- Agent Knowledge Management
2
2
 
3
- > **akm** (Agent Kit Manager) -- A package manager for AI agent skills, commands, tools, and knowledge.
3
+ > **akm** (Agent Knowledge Management) -- A package manager for AI agent skills, commands, tools, and knowledge.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/akm-cli)](https://www.npmjs.com/package/akm-cli)
6
6
  [![npm downloads](https://img.shields.io/npm/dm/akm-cli)](https://www.npmjs.com/package/akm-cli)
7
7
  [![license](https://img.shields.io/github/license/itlackey/akm)](https://github.com/itlackey/akm/blob/main/LICENSE)
8
8
 
9
9
  `akm` is a package manager for AI agent capabilities -- scripts, skills, commands,
10
- agents, knowledge, and memories. It works with any AI coding assistant that can
11
- run shell commands, including [Claude Code](https://claude.ai/code),
10
+ agents, knowledge, memories, workflows, wikis, vaults, lessons, and scheduled
11
+ tasks. It works with any AI coding assistant that can run shell commands,
12
+ including [Claude Code](https://claude.ai/code),
12
13
  [OpenCode](https://opencode.ai), [Cursor](https://cursor.com), and more.
13
14
 
14
15
  ## Install
15
16
 
17
+ **Option 1 — Prebuilt binary (recommended, no runtime required):**
18
+
19
+ ```sh
20
+ # Linux / macOS
21
+ curl -fsSL https://github.com/itlackey/akm/releases/latest/download/install.sh | bash
22
+
23
+ # Windows (PowerShell)
24
+ irm https://github.com/itlackey/akm/releases/latest/download/install.ps1 | iex
25
+ ```
26
+
27
+ **Option 2 — Bun (requires [Bun](https://bun.sh) >= 1.0):**
28
+
16
29
  ```sh
17
30
  bun install -g akm-cli
18
31
  ```
19
32
 
20
- Requires [Bun](https://bun.sh) runtime. Upgrade in place with `akm upgrade`.
33
+ Upgrade in place with `akm upgrade`.
34
+
35
+ > **AKM 0.8 requires the prebuilt binary or the Bun runtime. Node.js / npm / pnpm are not supported in 0.8.0** — running `npm install -g akm-cli` on a Node.js-only machine will print an error from the preinstall hook and exit without installing. Cross-runtime support (Node, npm, pnpm) is planned for 0.9.0.
21
36
 
22
37
  ## Quick Start
23
38
 
@@ -44,7 +59,8 @@ Add this to your `AGENTS.md`, `CLAUDE.md`, or system prompt:
44
59
  ## Resources & Capabilities
45
60
 
46
61
  You have access to a searchable library of scripts, skills, commands, agents,
47
- knowledge, and memories via the `akm` CLI. Use `akm -h` for details.
62
+ knowledge, memories, workflows, wikis, vaults, lessons, and scheduled tasks
63
+ via the `akm` CLI. Use `akm -h` for details.
48
64
  ```
49
65
 
50
66
  ## Install Stashes from Anywhere
package/SECURITY.md ADDED
@@ -0,0 +1,93 @@
1
+ # Security policy
2
+
3
+ ## Supported versions
4
+
5
+ Security fixes are made on the latest minor release line of `akm-cli`. The
6
+ 0.x line is pre-1.0 — please upgrade promptly when a fix lands.
7
+
8
+ | Version | Supported |
9
+ | --- | --- |
10
+ | 0.8.x | ✅ active |
11
+ | 0.7.x | ❌ no longer maintained |
12
+ | < 0.7 | ❌ no longer maintained |
13
+
14
+ ## Reporting a vulnerability
15
+
16
+ Please report security issues **privately** via GitHub Security Advisories:
17
+
18
+ - https://github.com/itlackey/akm/security/advisories/new
19
+
20
+ If GitHub Security Advisories is unavailable, email `itlackey@gmail.com`
21
+ with the word `SECURITY` in the subject. Please include reproduction steps,
22
+ the impacted akm version, and your operating environment.
23
+
24
+ We will acknowledge receipt within 72 hours and aim to ship a fix or a
25
+ mitigation guidance within two weeks, depending on severity.
26
+
27
+ ## Threat model
28
+
29
+ `akm` is a local CLI that reads and writes user files, executes user-authored
30
+ shell commands (via scripts, workflows, and agent dispatch), and talks to
31
+ explicitly configured external services (LLM endpoints, git remotes, npm,
32
+ HTTP sources). It does **not** ship telemetry, send data to anyone by
33
+ default, or open network listeners. See
34
+ [`docs/data-and-telemetry.md`](docs/data-and-telemetry.md) for the on-disk
35
+ inventory.
36
+
37
+ Several akm surfaces execute user-controlled code or data with the full
38
+ permissions of the akm process. These are documented design decisions, not
39
+ bugs, but you should be aware of them:
40
+
41
+ ### Workflows execute shell commands with full environment access
42
+
43
+ Workflow steps run in your shell with your PATH and your environment
44
+ variables — including any secrets you have exported or loaded via
45
+ `akm vault load`. **Only add workflow sources you trust.** See
46
+ [`docs/features/workflows.md` — "Security: workflow sources are executed
47
+ code"](docs/features/workflows.md#security-workflow-sources-are-executed-code)
48
+ for the full discussion.
49
+
50
+ ### Scripts execute shell commands
51
+
52
+ `akm show script:<name>` returns a `run:` command line the user (or an
53
+ integrating agent) then executes. The same trust model applies: scripts you
54
+ install from third-party stashes are third-party code.
55
+
56
+ ### Agents and commands embed user-authored prompts
57
+
58
+ `akm show agent:<name>` and `akm show command:<name>` return prompt
59
+ templates and system prompts that an LLM will execute. A malicious stash
60
+ maintainer could write a system prompt that instructs the LLM to read
61
+ sensitive files in your working tree and exfiltrate them via the LLM
62
+ response. Audit the prompt body the same way you'd audit a script.
63
+
64
+ ### Vaults are plaintext on disk
65
+
66
+ `akm vault` files are `0o600`-permissioned plaintext at
67
+ `<stash>/vaults/<name>.env`. They are protected against other local users
68
+ by filesystem permissions but not encrypted at rest. Do not commit vault
69
+ files to source control — they are `.gitignore`d in the default stash
70
+ layout for that reason. The `akm vault show` / `akm vault list` commands
71
+ never echo values; `akm vault load` produces shell-eval output meant to be
72
+ piped to `eval`, never displayed.
73
+
74
+ ### Improve / propose / distill send asset content to the configured LLM
75
+
76
+ `akm improve`, `akm propose`, `akm distill`, `akm reflect`, and `akm
77
+ consolidate` send asset frontmatter and body to whatever LLM endpoint is
78
+ configured in `~/.config/akm/config.json` (under `llm.endpoint`). If you
79
+ have configured a third-party LLM, your asset content goes to that
80
+ third-party. Use a local model (`http://localhost:11434` via Ollama, etc.)
81
+ for assets containing secrets or private notes.
82
+
83
+ ## Known non-issues
84
+
85
+ - **`akm` requires Bun or the prebuilt binary** — Node.js is not supported
86
+ in 0.8.0 because Bun-specific APIs are used in hot paths. This is a
87
+ compatibility limitation, not a security risk; the prebuilt binary is a
88
+ Bun-compiled standalone executable.
89
+ - **Workflows can read any file the akm process can read.** This is not a
90
+ bug — see "Threat model" above.
91
+ - **`bun install -g akm-cli` runs the preinstall hook.** The hook only
92
+ emits an error message and exits non-zero on Node.js; it does not phone
93
+ home or write outside the install directory.
@@ -0,0 +1,144 @@
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ import fs from "node:fs";
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";
12
+ const PROJECT_CONFIG_RELATIVE_PATH = path.join(".akm", "config.json");
13
+ function backupConfigFile(configPath) {
14
+ if (!fs.existsSync(configPath))
15
+ return;
16
+ const backupDir = path.join(getCacheDir(), "config-backups");
17
+ fs.mkdirSync(backupDir, { recursive: true });
18
+ const timestamp = new Date().toISOString().replace(/[.:]/g, "-");
19
+ const backupPath = path.join(backupDir, `config-${timestamp}.json`);
20
+ fs.copyFileSync(configPath, backupPath);
21
+ const latestPath = path.join(backupDir, "config.latest.json");
22
+ fs.copyFileSync(configPath, latestPath);
23
+ }
24
+ function acquireMigrateLock(lockPath, noWait) {
25
+ const lockDir = path.dirname(lockPath);
26
+ fs.mkdirSync(lockDir, { recursive: true });
27
+ const maxAttempts = noWait ? 1 : 20;
28
+ const delayMs = 200;
29
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
30
+ try {
31
+ fs.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
32
+ return () => {
33
+ try {
34
+ fs.unlinkSync(lockPath);
35
+ }
36
+ catch {
37
+ // ignore
38
+ }
39
+ };
40
+ }
41
+ catch {
42
+ if (noWait) {
43
+ return null;
44
+ }
45
+ // Simple busy-wait — synchronous since this is a one-shot CLI action
46
+ const deadline = Date.now() + delayMs;
47
+ while (Date.now() < deadline) {
48
+ // spin
49
+ }
50
+ }
51
+ }
52
+ return null;
53
+ }
54
+ export async function migrateConfigFile(filePath, opts) {
55
+ if (!fs.existsSync(filePath)) {
56
+ return { changed: false, result: {} };
57
+ }
58
+ const text = fs.readFileSync(filePath, "utf8");
59
+ let raw;
60
+ try {
61
+ const stripped = stripJsonComments(text);
62
+ const parsed = JSON.parse(stripped);
63
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
64
+ warn(`[akm] config-migrate: ${filePath} is not a valid JSON object, skipping.`);
65
+ return { changed: false, result: {} };
66
+ }
67
+ raw = parsed;
68
+ }
69
+ catch {
70
+ warn(`[akm] config-migrate: failed to parse ${filePath}, skipping.`);
71
+ return { changed: false, result: {} };
72
+ }
73
+ const { changed, result } = migrateConfigShape(raw);
74
+ if (!changed) {
75
+ return { changed: false, result };
76
+ }
77
+ const migratedText = `${JSON.stringify(result, null, 2)}\n`;
78
+ // WS-2: compute a diff when requested (always computed for --print-diff;
79
+ // never requires a write so safe in --dry-run).
80
+ const diff = opts.printDiff ? unifiedDiff(text, migratedText, filePath) : undefined;
81
+ if (opts.dryRun) {
82
+ return { changed: true, result, diff };
83
+ }
84
+ // WS-3: acquire config write lock + use atomic write (tmp → rename).
85
+ withConfigLock(() => {
86
+ backupConfigFile(filePath);
87
+ writeConfigAtomic(filePath, result);
88
+ });
89
+ return { changed: true, result, diff };
90
+ }
91
+ function discoverProjectConfigPaths(startDir) {
92
+ const paths = [];
93
+ let currentDir = path.resolve(startDir);
94
+ while (true) {
95
+ const configPath = path.join(currentDir, PROJECT_CONFIG_RELATIVE_PATH);
96
+ if (fs.existsSync(configPath) && fs.statSync(configPath).isFile()) {
97
+ paths.unshift(configPath);
98
+ }
99
+ const parentDir = path.dirname(currentDir);
100
+ if (parentDir === currentDir)
101
+ break;
102
+ currentDir = parentDir;
103
+ }
104
+ return paths;
105
+ }
106
+ export async function runConfigMigrate(opts) {
107
+ const userConfigPath = getConfigPath();
108
+ const projectPaths = discoverProjectConfigPaths(process.cwd());
109
+ const allPaths = [userConfigPath, ...projectPaths].filter((p, i, arr) => arr.indexOf(p) === i && fs.existsSync(p));
110
+ if (allPaths.length === 0) {
111
+ console.log("No config files found to migrate.");
112
+ return;
113
+ }
114
+ const lockPath = path.join(getCacheDir(), "config-migrate.lock");
115
+ const releaseLock = acquireMigrateLock(lockPath, opts.noWait ?? false);
116
+ if (!releaseLock) {
117
+ warn("[akm] config-migrate: another migration is already in progress, skipping.");
118
+ return;
119
+ }
120
+ try {
121
+ let anyChanged = false;
122
+ for (const filePath of allPaths) {
123
+ const { changed, diff } = await migrateConfigFile(filePath, { dryRun: opts.dryRun, printDiff: opts.printDiff });
124
+ if (changed) {
125
+ const action = opts.dryRun ? "would migrate" : "migrated";
126
+ console.log(`[akm] ${action}: ${filePath}`);
127
+ // WS-2: print the unified diff to stdout when --print-diff is set.
128
+ if (diff) {
129
+ console.log(diff);
130
+ }
131
+ anyChanged = true;
132
+ }
133
+ else {
134
+ console.log(`[akm] already up to date: ${filePath}`);
135
+ }
136
+ }
137
+ if (!anyChanged) {
138
+ console.log("All config files are already at the current version.");
139
+ }
140
+ }
141
+ finally {
142
+ releaseLock();
143
+ }
144
+ }
@@ -0,0 +1,39 @@
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ /**
5
+ * `akm config validate` — verify the on-disk config matches the schema.
6
+ *
7
+ * Reads the user config file, validates against AkmConfigSchema, and either
8
+ * prints "All checks passed." or a list of structured errors (path + message).
9
+ * Exits non-zero on errors so it composes well in CI hooks.
10
+ */
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";
16
+ export async function runConfigValidate() {
17
+ const configPath = getConfigPath();
18
+ if (!fs.existsSync(configPath)) {
19
+ console.log(`No config file at ${configPath} — nothing to validate.`);
20
+ return;
21
+ }
22
+ let text;
23
+ try {
24
+ text = fs.readFileSync(configPath, "utf8");
25
+ }
26
+ catch (err) {
27
+ const detail = err instanceof Error ? err.message : String(err);
28
+ throw new ConfigError(`Could not read config at ${configPath}: ${detail}`, "INVALID_CONFIG_FILE");
29
+ }
30
+ // parseConfigText throws ConfigError on malformed JSON (#458). Surface as-is.
31
+ const raw = parseConfigText(text, configPath);
32
+ const result = validateConfigShape(raw);
33
+ if (result.ok) {
34
+ console.log(`All checks passed. (${configPath})`);
35
+ return;
36
+ }
37
+ const lines = result.errors.map((e) => ` - ${e.path || "(root)"}: ${e.message}`).join("\n");
38
+ throw new ConfigError(`Config at ${configPath} has ${result.errors.length} validation error${result.errors.length === 1 ? "" : "s"}:\n${lines}`, "INVALID_CONFIG_FILE", "Fix the listed fields, or run `akm config migrate` if the errors look like legacy-shape leftovers.");
39
+ }
@@ -0,0 +1,73 @@
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ /**
5
+ * Confirmation helper for destructive CLI commands.
6
+ *
7
+ * ## Usage
8
+ *
9
+ * ```ts
10
+ * import { confirmDestructive } from "../cli/confirm";
11
+ *
12
+ * async function run({ args }) {
13
+ * const yes = await confirmDestructive(
14
+ * `Remove source "${args.target}"? This cannot be undone.`,
15
+ * { yes: args.yes === true }
16
+ * );
17
+ * if (!yes) { console.error("Aborted."); return; }
18
+ * // proceed...
19
+ * }
20
+ * ```
21
+ *
22
+ * ## Non-TTY policy
23
+ *
24
+ * In non-interactive contexts (stdin is not a TTY), destructive commands
25
+ * **fail by default** and require explicit `--yes` to proceed. This prevents
26
+ * accidental destruction in scripts that forget to pass `-y`.
27
+ *
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
30
+ * recovered after deletion, so the cost of requiring `--yes` in scripts is
31
+ * low and the cost of accidental deletion is high.
32
+ *
33
+ * ## Safety exemptions
34
+ *
35
+ * `--quiet` NEVER suppresses the confirmation prompt — it is safety-critical
36
+ * output. The auto-migration banner is similarly exempt from `--quiet`.
37
+ */
38
+ import * as p from "@clack/prompts";
39
+ import { UsageError } from "../core/errors";
40
+ /**
41
+ * Prompt the user to confirm a destructive action.
42
+ *
43
+ * Returns `true` if the user confirmed (or `--yes` was passed).
44
+ * Returns `false` if the user declined.
45
+ * Throws `UsageError("NON_INTERACTIVE_REQUIRES_YES")` when stdin is not a
46
+ * TTY and `--yes` was not passed — callers should propagate this error.
47
+ *
48
+ * The prompt defaults to NO so accidental Enter presses do not proceed.
49
+ *
50
+ * @param message Human-readable description of what will be destroyed.
51
+ * @param opts Options controlling confirmation behaviour.
52
+ */
53
+ export async function confirmDestructive(message, opts) {
54
+ // --yes always skips the prompt
55
+ if (opts.yes)
56
+ return true;
57
+ // Non-TTY: fail loudly — require explicit --yes in scripts
58
+ const isInteractive = process.stdin.isTTY === true;
59
+ if (!isInteractive) {
60
+ throw new UsageError(`This command requires confirmation in non-interactive mode. Pass --yes / -y to proceed without prompting.\n\nAction: ${message}`, "NON_INTERACTIVE_REQUIRES_YES");
61
+ }
62
+ // Interactive: prompt with default = NO (false)
63
+ const confirmed = await p.confirm({
64
+ message,
65
+ initialValue: false,
66
+ });
67
+ // p.confirm returns a symbol when the user cancels (Ctrl+C)
68
+ if (p.isCancel(confirmed)) {
69
+ p.outro("Cancelled.");
70
+ process.exit(0);
71
+ }
72
+ return confirmed === true;
73
+ }