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
@@ -0,0 +1,1307 @@
1
+ # Migrating from akm 0.7.x to 0.8.0
2
+
3
+ 0.8.0 is a storage reorganization release. The pre-0.8 self-improvement flow
4
+ based on `reflect` / `distill` has been consolidated into the `improve` +
5
+ `proposals` flow (`improve`, `propose`, `proposals`, `accept`, `reject`).
6
+ This guide focuses on what changed in storage/runtime state and which legacy
7
+ write paths were removed.
8
+
9
+ Most users need to do exactly one thing: run the migration script. The guide
10
+ below explains what the script does and what you need to verify afterward.
11
+
12
+ ## Table of contents
13
+
14
+ - [Installing 0.8.0](#installing-080)
15
+ - [What changed in 0.8.0](#what-changed-in-080)
16
+ - [Behavior change: `--auto-accept` is now OFF by default](#behavior-change---auto-accept-is-now-off-by-default)
17
+ - [Running the migration script](#running-the-migration-script)
18
+ - [New XDG directory layout](#new-xdg-directory-layout)
19
+ - [Event log: events.jsonl → state.db](#event-log-eventsjsonl--statedb)
20
+ - [Registry index cache: files → index.db](#registry-index-cache-files--indexdb)
21
+ - [Task history: JSONL files → state.db](#task-history-jsonl-files--statedb)
22
+ - [Task definition files: .md+frontmatter → .yml](#task-definition-files-mdfrontmatter--yml)
23
+ - [akm.lock moved to \$DATA](#akmlock-moved-to-data)
24
+ - [Graph extraction will re-run after upgrade](#graph-extraction-will-re-run-after-upgrade)
25
+ - [Removed fallbacks and aliases](#removed-fallbacks-and-aliases)
26
+ - [`akm enable context-hub` / `akm disable context-hub` removed](#akm-enable-context-hub--akm-disable-context-hub-removed)
27
+ - [`akm improve` no longer accepts `--format`](#akm-improve-no-longer-accepts---format)
28
+ - [`akm index --enrich` / `--re-enrich` removed](#akm-index---enrich----re-enrich-removed)
29
+ - [Memory inference and graph extraction moved out of `index`](#memory-inference-and-graph-extraction-moved-out-of-index)
30
+ - [`akm wiki ingest` now dispatches an agent](#akm-wiki-ingest-now-dispatches-an-agent)
31
+ - [`config.agent.processes["task"]` and `improve.schedule` removed](#configagentprocessestask-and-improveschedule-removed)
32
+ - [Bootstrap-from-file: `akm setup --from <file>`](#bootstrap-from-file-akm-setup---from-file)
33
+ - [Manual actions required](#manual-actions-required)
34
+ - [Verifying the upgrade](#verifying-the-upgrade)
35
+ - [Troubleshooting](#troubleshooting)
36
+ - [Rolling back](#rolling-back)
37
+ - [Config v2 migration (reflect multi-mode)](#config-v2-migration-reflect-multi-mode)
38
+
39
+ ## Installing 0.8.0
40
+
41
+ Pick whichever method you used for 0.7.x:
42
+
43
+ ```sh
44
+ # npm
45
+ npm install -g akm-cli@0.8.0
46
+
47
+ # bun
48
+ bun install -g akm-cli@0.8.0
49
+
50
+ # From source (contributors)
51
+ git fetch origin && git checkout v0.8.0 && bun install
52
+ ```
53
+
54
+ **Do not run any akm commands yet.** Run the migration script first (see
55
+ below). The 0.8.0 binary reads from the new locations; data that has not been
56
+ migrated will appear missing.
57
+
58
+ ## What changed in 0.8.0
59
+
60
+ 0.8.0 reorganizes on-disk storage around four XDG-compliant directories and
61
+ replaces two remaining JSONL write paths with SQLite tables:
62
+
63
+ | Area | 0.7.x | 0.8.0 |
64
+ | --- | --- | --- |
65
+ | Durable databases | `$CONFIG` | `$DATA` (`~/.local/share/akm`) |
66
+ | `akm.lock` location | `$CONFIG/akm.lock` | `$DATA/akm.lock` |
67
+ | Event log | `$CACHE/events.jsonl` | `events` table in `$DATA/state.db` |
68
+ | Registry index cache | `$CACHE/registry-index/<slug>.json` | `registry_index_cache` table in `$DATA/index.db` |
69
+ | Task run history | `$STATE/tasks/history/<id>.jsonl` | `task_history` table in `$DATA/state.db` |
70
+ | Task definition files | `<stash>/tasks/<id>.md` (Markdown + YAML frontmatter) | `<stash>/tasks/<id>.yml` (pure YAML) |
71
+ | `--target` flag | inconsistent across commands | uniform on `remember`, `import`, `wiki stash` |
72
+ | JSONL fallbacks | present for backward compat | removed; `state.db` only |
73
+
74
+ No asset files under `$STASH/` are touched. Your memories, skills, lessons,
75
+ workflows, and other content are unaffected.
76
+
77
+ ## Behavior change: `--auto-accept` is now OFF by default
78
+
79
+ > **Action required if you relied on the 0.8.0-RC auto-accept default.**
80
+
81
+ The `--auto-accept` flag on `akm improve` has been redefined as a confidence
82
+ threshold. Earlier 0.8.0 RCs shipped this flag **on by default at threshold
83
+ 90**, which surprised operators who expected Phase B operations to require
84
+ confirmation. The 0.8.0 release flips that default: **auto-accept is OFF
85
+ unless you explicitly pass a threshold value**.
86
+
87
+ If you were already on 0.7.x and never touched the flag, this is a no-op for
88
+ you — both 0.7.x and 0.8.0 default to interactive review. The only audience
89
+ that must update scripts is users who were tracking a 0.8.0 RC where the
90
+ default was ON.
91
+
92
+ ### New semantics
93
+
94
+ | Form | Effect |
95
+ | --- | --- |
96
+ | (flag absent) | Auto-accept **OFF** (default) |
97
+ | `--auto-accept` (bare) | Auto-accept **OFF** (treated as flag absent) |
98
+ | `--auto-accept=<N>` | Auto-accept ON at integer threshold `N` (0-100) |
99
+ | `--auto-accept=safe` | Permanent alias for `--auto-accept=90` |
100
+ | `--auto-accept=false` | Auto-accept OFF (explicit form, matches the default) |
101
+ | any other value | Error |
102
+
103
+ ### What you need to do
104
+
105
+ - **To get the legacy "always auto-accept" behavior**, pass
106
+ `--auto-accept=safe` (or `--auto-accept=90`) explicitly to `akm improve`
107
+ and to any scheduled task YAML, agent prompt, or CI invocation that
108
+ expects un-prompted Phase B operations.
109
+ - The literal string `safe` is a permanent alias for `90` and is **not**
110
+ deprecated.
111
+ - **Confidence scoring is not yet implemented.** Proposals do not currently
112
+ carry per-item confidence scores, so any non-`undefined` value of
113
+ `--auto-accept` behaves identically to the legacy `safe` mode (whole-batch
114
+ auto-accept). Once confidence scores ship, the threshold will gate
115
+ individual proposals.
116
+
117
+ ```sh
118
+ # 0.8.0 default (interactive review of proposals):
119
+ akm improve
120
+
121
+ # Opt in to legacy whole-batch auto-accept:
122
+ akm improve --auto-accept=safe # or --auto-accept=90
123
+
124
+ # Opt in to a stricter threshold (no-op until confidence scoring lands):
125
+ akm improve --auto-accept=95
126
+
127
+ # Explicit OFF (matches the default; useful for self-documenting scripts):
128
+ akm improve --auto-accept=false
129
+ ```
130
+
131
+ Review any scheduled `akm improve` tasks, agent prompts, or CI invocations
132
+ that were authored against a 0.8.0 RC with the previous default and update
133
+ them before upgrading.
134
+
135
+ ## Running the migration script
136
+
137
+ The migration script is the canonical upgrade path. It is idempotent — safe to
138
+ run more than once.
139
+
140
+ The `akm-migrate-storage` binary ships alongside `akm` in the global install
141
+ (both npm and prebuilt-binary distributions). Developers working from a
142
+ source clone can run `bun scripts/migrate-storage.ts` instead — both invoke
143
+ identical code.
144
+
145
+ **Step 1 — preview:**
146
+
147
+ ```sh
148
+ akm-migrate-storage --dry-run
149
+ ```
150
+
151
+ The dry run prints every file move and database import it would perform without
152
+ touching disk. Review the output. If any paths look unexpected, check your
153
+ `AKM_CONFIG_DIR`, `AKM_CACHE_DIR`, `AKM_DATA_DIR`, and `AKM_STATE_DIR`
154
+ environment variables (see [New XDG directory layout](#new-xdg-directory-layout)).
155
+
156
+ **Step 2 — apply:**
157
+
158
+ ```sh
159
+ akm-migrate-storage --yes
160
+ ```
161
+
162
+ The script performs these steps in order:
163
+
164
+ 1. Creates `$DATA` and `$STATE` directories if they do not exist.
165
+ 2. Moves `$CONFIG/index.db` → `$DATA/index.db`.
166
+ 3. Moves `$CONFIG/workflow.db` → `$DATA/workflow.db`.
167
+ 4. Creates `$DATA/state.db` with the initial schema.
168
+ 5. Copies `$CONFIG/akm.lock` → `$DATA/akm.lock` (leaves the original in place
169
+ until you confirm everything works).
170
+ 6. Imports `$CACHE/events.jsonl` into the `events` table in `state.db`.
171
+ Duplicate rows (same `ts` + `eventType` + `ref`) are skipped.
172
+ 7. Imports per-task JSONL files from `$STATE/tasks/history/` (or
173
+ `$CACHE/tasks/history/` for older installations) into the `task_history`
174
+ table in `state.db`.
175
+ 8. Prints a summary of rows imported and files moved.
176
+
177
+ After step 2 completes successfully you can delete the legacy files:
178
+
179
+ ```sh
180
+ # Optional cleanup — only after confirming akm works
181
+ rm -f ~/.config/akm/index.db ~/.config/akm/workflow.db
182
+ rm -f ~/.cache/akm/events.jsonl
183
+ rm -rf ~/.cache/akm/registry-index/
184
+ ```
185
+
186
+ The old `$CONFIG/akm.lock` can be left in place; 0.8.0 reads only from
187
+ `$DATA/akm.lock` and ignores the old location.
188
+
189
+ ## New XDG directory layout
190
+
191
+ akm 0.8.0 uses four XDG base directories:
192
+
193
+ | Variable | Default (Linux/macOS) | Default (Windows) | Override env var |
194
+ | --- | --- | --- | --- |
195
+ | `$CONFIG` | `~/.config/akm` | `%APPDATA%\akm` | `AKM_CONFIG_DIR` |
196
+ | `$CACHE` | `~/.cache/akm` | `%LOCALAPPDATA%\akm` | `AKM_CACHE_DIR` |
197
+ | `$DATA` | `~/.local/share/akm` | `%LOCALAPPDATA%\akm\data` | `AKM_DATA_DIR` |
198
+ | `$STATE` | `~/.local/state/akm` | `%LOCALAPPDATA%\akm\state` | `AKM_STATE_DIR` |
199
+ | `$STASH` | `~/akm` | `%USERPROFILE%\Documents\akm` | `AKM_STASH_DIR` |
200
+
201
+ **What lives where after migration:**
202
+
203
+ | Path | Contents |
204
+ | --- | --- |
205
+ | `$CONFIG/config.json` | User configuration (unchanged location) |
206
+ | `$DATA/index.db` | Main search index, embeddings, registry cache |
207
+ | `$DATA/workflow.db` | Workflow run state |
208
+ | `$DATA/state.db` | Events, proposals, task history |
209
+ | `$DATA/akm.lock` | Installed stash lockfile |
210
+ | `$DATA/config-backups/` | Pre-save config snapshots |
211
+ | `$CACHE/` | Regenerable data: registry downloads, binary cache |
212
+ | `$STATE/tasks/logs/` | Per-run stdout/stderr logs (unchanged) |
213
+
214
+ If you set `AKM_CONFIG_DIR` or `AKM_DATA_DIR` in CI or agent environments,
215
+ update those environment variables to reflect the new split. The
216
+ `AKM_CONFIG_DIR`-as-data-directory fallback (which previously caused data
217
+ files to land in `$CONFIG` if `AKM_DATA_DIR` was unset) is removed in 0.8.0.
218
+
219
+ ## Event log: events.jsonl → state.db
220
+
221
+ The file `$CACHE/events.jsonl` (one JSON object per line) is replaced by the
222
+ `events` table in `$DATA/state.db`.
223
+
224
+ **What this means for you:**
225
+
226
+ - The migration script imports all existing JSONL events into `state.db`. After
227
+ a successful import, the JSONL file can be deleted.
228
+ - Any tooling that read `events.jsonl` directly must be updated. Use
229
+ `akm events` to inspect the event stream from the CLI, or query `state.db`
230
+ directly with any SQLite client.
231
+ - **Cursor reset**: The old cursor was a byte offset into the JSONL file. The
232
+ new cursor is the integer `id` (autoincrement rowid) in the `events` table.
233
+ If you have scripts that persist a byte-offset cursor to resume event
234
+ polling, reset the cursor to `0` after migration. The new `readEvents()`
235
+ API accepts `sinceOffset` as a row `id`.
236
+
237
+ The event schema is unchanged on the wire — the same `eventType`, `ts`, `ref`,
238
+ and `metadata` fields are present. New event types added in 0.8.0:
239
+
240
+ | Event type | Emitted by | Purpose |
241
+ | --- | --- | --- |
242
+ | `select` | `akm show` (after search within 60s) | MemRL selection signal |
243
+ | `improve_skipped` | `akm improve` (cooldown guards) | Observability for skip distribution |
244
+ | `promoted` / `rejected` | `akm accept` / `akm reject` | Proposal lifecycle decisions |
245
+
246
+ The `search` event now carries `mode: "semantic" | "keyword"` in its
247
+ `metadata` field.
248
+
249
+ ## Registry index cache: files → index.db
250
+
251
+ Per-registry JSON files at `$CACHE/registry-index/<slug>.json` are replaced
252
+ by the `registry_index_cache` table in `$DATA/index.db`.
253
+
254
+ **No manual action is required.** The cache is regenerable data: akm fetches
255
+ and stores a fresh copy the next time it queries each registry. The migration
256
+ script does not import the old JSON files — they are simply superseded.
257
+
258
+ After confirming akm works normally, you can delete the directory:
259
+
260
+ ```sh
261
+ rm -rf ~/.cache/akm/registry-index/
262
+ ```
263
+
264
+ ## Task history: JSONL files → state.db
265
+
266
+ Per-task JSONL execution logs at `$STATE/tasks/history/<task-id>.jsonl`
267
+ (or `$CACHE/tasks/history/` for older installations) are replaced by the
268
+ `task_history` table in `$DATA/state.db`.
269
+
270
+ The migration script imports all discovered JSONL history files into
271
+ `task_history`. Existing entries are not overwritten.
272
+
273
+ After confirming task history is visible via `akm tasks history`, you can
274
+ delete the old files:
275
+
276
+ ```sh
277
+ rm -rf ~/.local/state/akm/tasks/history/
278
+ # or, for older installations:
279
+ rm -rf ~/.cache/akm/tasks/history/
280
+ ```
281
+
282
+ Note: per-run stdout/stderr log files at `$CACHE/tasks/logs/<task-id>/` are
283
+ **not** moved. They remain under `$CACHE` and are unaffected by this migration.
284
+
285
+ ## Task definition files: .md+frontmatter → .yml
286
+
287
+ In 0.7.x and earlier, scheduled task definitions lived at
288
+ `<stash>/tasks/<id>.md` — a Markdown file where the schedule, target, and
289
+ options were specified as YAML frontmatter and, for inline prompts, the
290
+ Markdown body served as the prompt text:
291
+
292
+ ```markdown
293
+ ---
294
+ schedule: "0 9 * * 1-5"
295
+ workflow: workflow:daily-backup
296
+ params:
297
+ region: us-east-1
298
+ enabled: true
299
+ ---
300
+ ```
301
+
302
+ For inline prompts the old format used `prompt: inline` as a sentinel and the
303
+ Markdown body held the text:
304
+
305
+ ```markdown
306
+ ---
307
+ schedule: "@daily"
308
+ prompt: inline
309
+ profile: opencode
310
+ enabled: true
311
+ ---
312
+
313
+ Do the thing.
314
+ And the other thing.
315
+ ```
316
+
317
+ In 0.8.0 task definitions are **pure YAML files** at `<stash>/tasks/<id>.yml`.
318
+ The frontmatter wrapper and the Markdown body are gone. Inline prompts are
319
+ written as a YAML block scalar:
320
+
321
+ ```yaml
322
+ # <stash>/tasks/daily-backup.yml
323
+ schedule: "0 9 * * 1-5"
324
+ workflow: workflow:daily-backup
325
+ params:
326
+ region: us-east-1
327
+ enabled: true
328
+ ```
329
+
330
+ ```yaml
331
+ # <stash>/tasks/morning-prompt.yml
332
+ schedule: "@daily"
333
+ prompt: |
334
+ Do the thing.
335
+ And the other thing.
336
+ profile: opencode
337
+ enabled: true
338
+ ```
339
+
340
+ The 0.8.0 release also adds optional display fields that were not present in
341
+ the old format:
342
+
343
+ ```yaml
344
+ schedule: "@weekly"
345
+ command: akm improve --auto-accept=90 --limit 25
346
+ enabled: true
347
+ name: Weekly improve cycle
348
+ description: Runs the improve pipeline against the full stash.
349
+ when_to_use: Run manually or let the scheduler trigger it every Monday.
350
+ tags: [scheduled, improve]
351
+ ```
352
+
353
+ ### What 0.8.0 does and does not do with old .md files
354
+
355
+ - `akm tasks list` — enumerates only `.yml` files. Old `.md` files are
356
+ **silently invisible**; they will not appear in the list or be registered
357
+ with the OS scheduler.
358
+ - `akm tasks show <id>` / `akm tasks run <id>` — if you pass an id that
359
+ includes the `.md` suffix (e.g. `akm tasks show daily-backup.md`), the
360
+ suffix is stripped before the lookup, so the command resolves to
361
+ `<stash>/tasks/daily-backup.yml`. If no `.yml` file exists the command
362
+ returns a "task not found" error, not a parse error.
363
+ - `akm tasks add` — always writes a new `.yml` file. It will not overwrite or
364
+ convert an existing `.md` file.
365
+
366
+ There is no automatic migration of task definition files. Each `.md` file must
367
+ be converted manually (see below).
368
+
369
+ ### Step-by-step migration
370
+
371
+ **Step 1 — list your existing task files:**
372
+
373
+ ```sh
374
+ ls ~/akm/tasks/*.md 2>/dev/null
375
+ # or, if AKM_STASH_DIR is set:
376
+ ls "$AKM_STASH_DIR/tasks"/*.md 2>/dev/null
377
+ ```
378
+
379
+ **Step 2 — for each task, create the equivalent `.yml` file.**
380
+
381
+ Extract the frontmatter keys from the old `.md` file and write them as a
382
+ top-level YAML document. If the old file used `prompt: inline`, replace it
383
+ with `prompt: |` followed by the indented body text.
384
+
385
+ Old file `~/akm/tasks/weekly-review.md`:
386
+
387
+ ```markdown
388
+ ---
389
+ schedule: "0 8 * * 1"
390
+ prompt: inline
391
+ profile: opencode
392
+ enabled: true
393
+ ---
394
+
395
+ Review the week's completed tasks and summarize action items.
396
+ ```
397
+
398
+ New file `~/akm/tasks/weekly-review.yml`:
399
+
400
+ ```yaml
401
+ schedule: "0 8 * * 1"
402
+ prompt: |
403
+ Review the week's completed tasks and summarize action items.
404
+ profile: opencode
405
+ enabled: true
406
+ ```
407
+
408
+ If the old file used `prompt: <asset-ref>` or `prompt: ./path/to/file.md`,
409
+ carry the value through unchanged:
410
+
411
+ ```yaml
412
+ schedule: "0 8 * * 1"
413
+ prompt: agent:weekly-review-agent
414
+ enabled: true
415
+ ```
416
+
417
+ **Step 3 — validate the new file:**
418
+
419
+ ```sh
420
+ akm tasks show weekly-review
421
+ ```
422
+
423
+ This command parses and prints the task. Any YAML errors or missing required
424
+ fields are reported here before the task is registered with the scheduler.
425
+
426
+ **Step 4 — register with the OS scheduler:**
427
+
428
+ ```sh
429
+ akm tasks sync
430
+ ```
431
+
432
+ `akm tasks sync` scans all `.yml` files, installs tasks that are not yet
433
+ registered, and removes scheduler entries whose backing `.yml` file no longer
434
+ exists. Run this once after converting all tasks.
435
+
436
+ **Step 5 — remove the old `.md` files:**
437
+
438
+ Once `akm tasks list` shows all your tasks correctly and `akm tasks sync`
439
+ reports them as installed, delete the old Markdown files:
440
+
441
+ ```sh
442
+ rm ~/akm/tasks/*.md
443
+ ```
444
+
445
+ ### Verifying task migration
446
+
447
+ ```sh
448
+ # All tasks appear with expected schedules
449
+ akm tasks list
450
+
451
+ # Each task resolves without errors
452
+ akm tasks show <id>
453
+
454
+ # Scheduler entries are in sync
455
+ akm tasks sync
456
+ ```
457
+
458
+ If a task that was registered on 0.7.x is now missing from the scheduler,
459
+ the most likely cause is that its `.md` file has not been converted yet.
460
+ Convert the file, run `akm tasks sync`, and confirm with `akm tasks list`.
461
+
462
+ ## akm.lock moved to $DATA
463
+
464
+ `akm.lock` (the installed stash lockfile) moves from `$CONFIG/akm.lock` to
465
+ `$DATA/akm.lock`.
466
+
467
+ The migration script copies the file to its new location. The original at
468
+ `$CONFIG/akm.lock` is left in place and can be deleted once you confirm `akm
469
+ list` shows your stashes correctly.
470
+
471
+ 0.8.0 reads **only** from `$DATA/akm.lock`. The old location is silently
472
+ ignored.
473
+
474
+ ## Graph extraction will re-run after upgrade
475
+
476
+ 0.8.0 includes a graph schema redesign and ships `DB_VERSION = 17` (the
477
+ v0.7.x line shipped at version 10; intermediate bumps land in this release).
478
+ On first launch after upgrade, akm detects the version mismatch and rebuilds
479
+ the affected `index.db` tables in place. A warning is logged during the
480
+ upgrade so this is visible in `akm` output.
481
+
482
+ **What rebuilds automatically (free, fast):**
483
+
484
+ - `entries`, `embeddings`, FTS, and the `entries_vec` table all regenerate from
485
+ disk via `akm index`. On most stashes this completes in a few seconds.
486
+
487
+ **What requires LLM calls (the only user-visible cost):**
488
+
489
+ - The graph tables (`graph_files`, `graph_file_entities`, `graph_file_relations`,
490
+ `graph_meta`) are dropped during the upgrade and repopulated by graph
491
+ extraction on the next `akm improve` cycle. Graph extraction makes LLM calls
492
+ to identify entities and relations in each asset, so the first improve cycle
493
+ after upgrade will be slower than steady state.
494
+
495
+ **Recommendation:**
496
+
497
+ ```sh
498
+ # Run after the storage migration to repopulate graph data.
499
+ akm improve
500
+ ```
501
+
502
+ Subsequent improve cycles return to their normal cadence; only the first run
503
+ pays the full re-extraction cost.
504
+
505
+ **Why this is faster than it would have been on 0.7.x:**
506
+
507
+ - Graph extraction now uses an incremental `candidatePaths` filter so it only
508
+ visits assets that have actually changed, dramatically reducing repeat work
509
+ on a cold cache.
510
+ - `graphExtractionBatchSize` defaults to 4 (was 1), auto-tuned against
511
+ `llm.contextLength`, so each LLM round-trip processes more assets.
512
+ - `replaceStoredGraph` is now incremental — unchanged entries skip, changed
513
+ entries swap their child rows in place, and removed entries cascade out.
514
+ - `listRelatedPathsForFile` is now a scoped SQL self-join instead of a full
515
+ in-memory scan, cutting cold-call latency from tens of milliseconds to
516
+ single-digit milliseconds.
517
+
518
+ No manual SQL or repair commands are required — the DB_VERSION bump (10 → 17)
519
+ and the DROP+rebuild path handle everything. If you query the graph tables directly
520
+ from external tooling, note that `graph_files` is now keyed on
521
+ `entry_id INTEGER PRIMARY KEY REFERENCES entries(id) ON DELETE CASCADE` and the
522
+ child tables (`graph_file_entities`, `graph_file_relations`) are re-keyed on
523
+ `entry_id` rather than `(stash_root, file_path)`. `body_hash` is now `NOT NULL`.
524
+ New columns: `extraction_run_id` (on `graph_files` and `graph_meta`) and
525
+ `extractor_id` (on `graph_meta`).
526
+
527
+ ## Removed fallbacks and aliases
528
+
529
+ The following backward-compatibility shims that shipped in 0.7.x are removed
530
+ in 0.8.0. Update scripts, agent prompts, and config before upgrading.
531
+
532
+ ### JSONL write fallbacks
533
+
534
+ `akm remember`, `akm import`, and `akm wiki stash` no longer fall back to
535
+ JSONL-based event writing. All writes go through `state.db`. If you were
536
+ relying on the JSONL file for event streaming, switch to `akm events` or
537
+ `akm events --tail`.
538
+
539
+ ### Deprecated `filePath` alias
540
+
541
+ The `filePath` field on task history entries (deprecated in 0.7.x in favour of
542
+ `log`) is removed. Tools that read task history records must use the `log`
543
+ field.
544
+
545
+ ### `--target` flag standardisation
546
+
547
+ The write-target flag is now uniformly `--target <stash-name>` on `akm
548
+ remember`, `akm import`, and `akm wiki stash`. Previous inconsistent flag
549
+ names are removed with no compatibility aliases.
550
+
551
+ ```sh
552
+ # Before (0.7.x inconsistent forms — removed in 0.8.0)
553
+ akm remember "note" --stash team-stash
554
+ akm wiki stash my-wiki ./page.md --source team-stash
555
+
556
+ # After (0.8.0)
557
+ akm remember "note" --target team-stash
558
+ akm import ./page.md --target team-stash
559
+ akm wiki stash my-wiki ./page.md --target team-stash
560
+ ```
561
+
562
+ ### Config-dir fallback removed
563
+
564
+ In 0.7.x and earlier, setting `AKM_CONFIG_DIR` without setting `AKM_DATA_DIR`
565
+ would cause data files (`index.db`, `workflow.db`) to land in `$CONFIG`. This
566
+ fallback is removed. Set `AKM_DATA_DIR` explicitly if you override paths.
567
+
568
+ ## `akm enable context-hub` / `akm disable context-hub` removed
569
+
570
+ `context-hub` is no longer a recognised target for `akm enable` / `akm
571
+ disable` in 0.8.0. The toggle was originally retired in 0.6.0 (context-hub
572
+ became a regular git stash); 0.8.0 tightens the parser so invoking either
573
+ form now errors with a usage message:
574
+
575
+ ```
576
+ Unsupported target "context-hub". Supported targets: skills.sh
577
+ ```
578
+
579
+ The only valid targets for `akm enable` / `akm disable` in 0.8.0 are
580
+ `skills.sh` (and its provider-id alias `skills-sh`). Every other component
581
+ that used to be toggled — context-hub, builtin stash sources, registry
582
+ defaults — is configured via the regular config file or `akm registry`
583
+ commands rather than via `enable` / `disable`.
584
+
585
+ ### What to do
586
+
587
+ If you still have automation or muscle-memory invocations of `akm enable
588
+ context-hub`:
589
+
590
+ 1. **Remove the toggle calls.** They never re-enable a "context-hub
591
+ provider" — that provider type was deleted in 0.6.0.
592
+ 2. **Add context-hub as a regular git stash** if you want its content in
593
+ your stash list. This is the same shape used by any other git source:
594
+
595
+ ```sh
596
+ akm add github:andrewyng/context-hub --name context-hub
597
+ ```
598
+
599
+ 3. **Verify with `akm list`.** The output should show `context-hub` as a
600
+ git-type stash, not as a special component.
601
+
602
+ If you only ever ran `akm disable context-hub` to suppress a phantom
603
+ provider entry, no replacement action is needed — there is nothing to
604
+ disable in 0.8.0.
605
+
606
+ ### Before / after
607
+
608
+ ```sh
609
+ # Before (0.7.x and earlier — silently no-op after 0.6.0)
610
+ akm enable context-hub
611
+ akm disable context-hub
612
+
613
+ # After (0.8.0 — explicit error; replace with regular stash management)
614
+ akm add github:andrewyng/context-hub --name context-hub
615
+ akm remove context-hub # if you previously disabled it
616
+ ```
617
+
618
+ ## `akm improve` no longer accepts `--format`
619
+
620
+ In 0.7.x `akm improve --format json` printed a JSON envelope to stdout. In
621
+ 0.8.0 the flag is rejected with `INVALID_FLAG_VALUE` to remove the implicit
622
+ "stdout swallow" failure mode that masked errors when the improve pipeline
623
+ emitted progress lines.
624
+
625
+ ### New behaviour
626
+
627
+ | Form | Effect in 0.8.0 |
628
+ | --- | --- |
629
+ | `akm improve` (no `--format`) | Human-readable output to stderr; full JSON result written to `.akm/runs/<run-id>/improve-result.json` |
630
+ | `akm improve --format json` | **Error** — `INVALID_FLAG_VALUE: --format is no longer supported on akm improve` |
631
+ | `akm improve --json-to-stdout` | Same as 0.7.x `--format json` — JSON envelope on stdout for capture by pipelines |
632
+
633
+ The on-disk JSON file at `.akm/runs/<run-id>/improve-result.json` is always
634
+ written regardless of stdout mode, so post-hoc inspection of an `akm
635
+ improve` run no longer depends on having captured stdout at the time.
636
+
637
+ ### What to do
638
+
639
+ If you have CI jobs, agent prompts, or shell scripts that pipe `akm
640
+ improve --format json` into `jq` or another consumer:
641
+
642
+ 1. **Replace `--format json` with `--json-to-stdout`.** The envelope shape is
643
+ unchanged — only the flag name moved.
644
+ 2. **Or read the run artifact.** If your pipeline can tolerate reading from
645
+ disk after the fact, drop the stdout capture entirely and read
646
+ `.akm/runs/<run-id>/improve-result.json` instead. The run id is printed
647
+ to stderr at the start of the run.
648
+
649
+ ### Before / after
650
+
651
+ ```sh
652
+ # Before (0.7.x)
653
+ akm improve --format json | jq '.proposals[] | select(.confidence > 80)'
654
+
655
+ # After (0.8.0)
656
+ akm improve --json-to-stdout | jq '.proposals[] | select(.confidence > 80)'
657
+
658
+ # After (0.8.0 — disk-based, no stdout capture required)
659
+ akm improve
660
+ jq '.proposals[] | select(.confidence > 80)' \
661
+ "$(ls -1t .akm/runs/*/improve-result.json | head -n1)"
662
+ ```
663
+
664
+ If you see this error after upgrading:
665
+
666
+ ```
667
+ INVALID_FLAG_VALUE: --format is no longer supported on akm improve.
668
+ ```
669
+
670
+ it is always a script-side fix — add `--json-to-stdout` (or switch to the
671
+ file-based path) and re-run.
672
+
673
+ ## `akm index --enrich` / `--re-enrich` removed
674
+
675
+ The `--enrich` and `--re-enrich` flags on `akm index` are gone. In 0.7.x they
676
+ forced a slow LLM-enrichment pass alongside the regular index rebuild; in
677
+ 0.8.0 the responsibilities are split:
678
+
679
+ - Plain `akm index` continues to own **fast metadata enhancement** when LLM
680
+ metadata enrichment is enabled (controlled by `index.metadataEnhance` in
681
+ the unified config tree).
682
+ - Slow LLM maintenance work — memory inference, graph extraction, lesson
683
+ distillation, consolidation — runs from `akm improve`, not from `akm index`.
684
+
685
+ If you have cron jobs or scripts that call `akm index --enrich` (or
686
+ `--re-enrich`) on a schedule:
687
+
688
+ ```sh
689
+ # Before (0.7.x)
690
+ akm index --enrich
691
+
692
+ # After (0.8.0)
693
+ akm improve # slow LLM maintenance work
694
+ akm index # fast metadata enrichment is already included
695
+ ```
696
+
697
+ Pure index rebuilds without any LLM work continue to call `akm index` with no
698
+ flag changes required.
699
+
700
+ ## Memory inference and graph extraction moved out of `index`
701
+
702
+ Closely related to the `--enrich` removal above: the LLM-driven memory
703
+ inference and graph-extraction passes that previously ran from `akm index
704
+ --enrich` now run exclusively from the `akm improve` maintenance phase, after
705
+ consolidation and (when relevant) the post-consolidation reindex.
706
+
707
+ If any automation relied on `akm index` to refresh derived memories or graph
708
+ data, switch it to call `akm improve` on the desired cadence. The improve
709
+ pipeline reindexes when memory inference writes new derived memories and
710
+ refreshes graph extraction against the post-improve corpus state, so a single
711
+ `akm improve` run settles both surfaces.
712
+
713
+ ```sh
714
+ # Before (0.7.x)
715
+ akm index --enrich # also refreshed memories + graph
716
+
717
+ # After (0.8.0)
718
+ akm improve # refreshes memories + graph as part of maintenance
719
+ ```
720
+
721
+ Read-only callers (`akm graph entities`, `akm graph relations`, etc.) are
722
+ unaffected — they continue to query whichever graph data is already in
723
+ `index.db`.
724
+
725
+ ## `akm wiki ingest` now dispatches an agent
726
+
727
+ `akm wiki ingest <name>` no longer prints the ingest workflow to stdout for a
728
+ human to copy/paste or pipe elsewhere. It now resolves an agent profile (from
729
+ `--profile` or `config.defaults.agent`) and **dispatches that agent directly
730
+ with the workflow as its prompt**. The agent does the work end-to-end.
731
+
732
+ **Behavior changes:**
733
+
734
+ - The `--execute` flag is removed. Dispatch is the only mode.
735
+ - Without an accessible agent profile, the command fails with a clear error
736
+ pointing at `profiles.agent`.
737
+ - Output is the agent's run envelope, not the workflow text.
738
+
739
+ **New flags:**
740
+
741
+ | Flag | Description |
742
+ | --- | --- |
743
+ | `--profile <name>` | Override the agent profile resolved from `config.defaults.agent` |
744
+ | `--model <model>` | Override the agent's model alias or platform ID |
745
+ | `--timeout-ms <ms>` | Override the agent CLI timeout in milliseconds |
746
+
747
+ **Migration:**
748
+
749
+ Scripts that piped the previous print-only output into another tool will
750
+ break. If you still need the raw workflow text (e.g. for an external
751
+ orchestrator), capture it from the wiki source files directly rather than
752
+ from `akm wiki ingest`.
753
+
754
+ ```sh
755
+ # Before (0.7.x — print-only or --execute)
756
+ akm wiki ingest my-wiki > workflow.md
757
+ akm wiki ingest my-wiki --execute
758
+
759
+ # After (0.8.0 — agent dispatch is the only mode)
760
+ akm wiki ingest my-wiki # uses defaults.agent
761
+ akm wiki ingest my-wiki --profile opencode-cli # explicit profile
762
+ akm wiki ingest my-wiki --model opus --timeout-ms 600000
763
+ ```
764
+
765
+ ## `config.agent.processes["task"]` and `improve.schedule` removed
766
+
767
+ Two keys are dropped during config auto-migration and not transformed to a
768
+ new location:
769
+
770
+ - **`config.agent.processes["task"]`** — task dispatch parameters now live in
771
+ the task's own stash YAML file. Each task declares its `mode` and `profile`
772
+ (and optional `timeoutMs`) directly. This removes the global "all tasks use
773
+ this dispatch config" coupling that the legacy key encoded.
774
+
775
+ - **`config.improve.schedule` / `improve.schedule`** — global improve
776
+ scheduling is no longer a config key. Scheduling is owned by stash task
777
+ YAMLs that wrap `akm improve` calls (via the `command:` task target).
778
+
779
+ **Migration:**
780
+
781
+ If your 0.7.x config had `config.agent.processes.task`, rewrite each task
782
+ file to set `mode` / `profile` / `timeoutMs` at the top level:
783
+
784
+ ```yaml
785
+ # <stash>/tasks/nightly-improve.yml
786
+ schedule: "@daily"
787
+ command: akm improve --auto-accept=90 --limit 50
788
+ profile: opencode-cli # was config.agent.processes.task.profile
789
+ timeoutMs: 7200000 # was config.agent.processes.task.timeoutMs
790
+ enabled: true
791
+ ```
792
+
793
+ If your 0.7.x config had `improve.schedule`, create an equivalent task file:
794
+
795
+ ```yaml
796
+ # <stash>/tasks/improve-cycle.yml
797
+ schedule: "0 3 * * *" # the cron you used to put in improve.schedule
798
+ command: akm improve
799
+ enabled: true
800
+ ```
801
+
802
+ Run `akm tasks sync` after creating or editing task files so the OS scheduler
803
+ picks them up. Auto-migration strips the old keys from `config.json`; the
804
+ backup it writes before doing so retains the original schedule string if you
805
+ need to recover it.
806
+
807
+ ## Bootstrap-from-file: `akm setup --from <file>`
808
+
809
+ 0.8.0 adds an `--from <file>` flag to `akm setup` that bootstraps the config
810
+ from a JSON or YAML file on disk. This is the recommended way to:
811
+
812
+ - Move an existing config to a new machine without typing the same answers
813
+ into the interactive wizard.
814
+ - Recover from a clobbered `~/.config/akm/config.json` using the timestamped
815
+ snapshots that akm writes to `~/.cache/akm/config-backups/` before every
816
+ destructive save.
817
+ - Replay a known-good config in CI without piping a large JSON literal on
818
+ the command line.
819
+
820
+ The flag accepts both JSON and YAML — detection is by file extension
821
+ (`.yml` / `.yaml` → YAML; anything else, including `.json`, parses as JSON).
822
+ A leading `~` in the path is expanded against `$HOME`, and relative paths
823
+ resolve against the current directory.
824
+
825
+ ### Recovering a clobbered config
826
+
827
+ If the post-incident audit
828
+ (`docs/technical/incidents/2026-05-23-setup-clobbers-user-config.md`) ever
829
+ fires for you and you lose your `~/.config/akm/config.json`, restore from
830
+ the most recent backup:
831
+
832
+ ```sh
833
+ # 1. Locate the backup written immediately before the destructive save.
834
+ ls -1t ~/.cache/akm/config-backups/ | head
835
+
836
+ # 2. Replay it through setup. The wizard will skip prompts for keys
837
+ # present in the backup and only prompt (or accept defaults) for
838
+ # anything missing.
839
+ akm setup --from ~/.cache/akm/config-backups/config-2026-05-22T18-44-31.json
840
+ ```
841
+
842
+ ### Bootstrapping from a YAML profile
843
+
844
+ YAML is easier to hand-edit and version-control than JSON. The flag accepts
845
+ the same top-level keys as `--config <json>` — `stashDir`, `llm`,
846
+ `embedding`, `agent`, `semanticSearchMode`, `output`, `profiles`,
847
+ `defaults`:
848
+
849
+ ```yaml
850
+ # ~/akm-bootstrap.yml
851
+ stashDir: /home/dev/akm
852
+ llm:
853
+ endpoint: http://localhost:11434/v1
854
+ model: gpt-oss-20b
855
+ semanticSearchMode: off
856
+ ```
857
+
858
+ ```sh
859
+ akm setup --from ~/akm-bootstrap.yml
860
+ ```
861
+
862
+ ### Validation
863
+
864
+ The CLI rejects with a friendly error when:
865
+
866
+ - The file does not exist: `ConfigError(INVALID_CONFIG_FILE): Config file not
867
+ found: <path>`.
868
+ - The file cannot be parsed: `ConfigError(INVALID_CONFIG_FILE): Failed to
869
+ parse JSON|YAML config file <path>: <parser error>`.
870
+ - The top-level payload is not an object (e.g. a JSON array or string).
871
+ - Both `--from <file>` and `--config <json>` are passed: `UsageError`.
872
+
873
+ Partial files are allowed — the wizard simply prompts (or in `--yes` mode,
874
+ accepts defaults) for any required keys that the file omitted.
875
+
876
+ ## Manual actions required
877
+
878
+ Most users need only run the migration script. The following are exceptions:
879
+
880
+ ### 1. Update event stream consumers
881
+
882
+ If any script, cron job, or agent prompt reads `$CACHE/events.jsonl` directly,
883
+ update it to use `akm events [--tail]` or query `state.db` directly:
884
+
885
+ ```sh
886
+ # Before
887
+ tail -f ~/.cache/akm/events.jsonl | jq .
888
+
889
+ # After
890
+ akm events --tail
891
+ # or for scripted consumption with cursor:
892
+ akm events --since-offset <last-row-id> --format json
893
+ ```
894
+
895
+ ### 2. Reset byte-offset event cursors
896
+
897
+ If you maintain a persistent cursor (file, env var, database) that stores a
898
+ byte offset into `events.jsonl` for incremental polling, reset it to `0` after
899
+ migration. The new cursor is a row `id` integer, not a byte offset.
900
+
901
+ ### 3. Update environment variable splits in CI
902
+
903
+ If your CI sets `AKM_CONFIG_DIR` and relies on data files landing there:
904
+
905
+ ```sh
906
+ # Before (implicit; worked because of config-dir fallback)
907
+ export AKM_CONFIG_DIR=/ci/akm-config
908
+
909
+ # After (explicit split required)
910
+ export AKM_CONFIG_DIR=/ci/akm-config
911
+ export AKM_DATA_DIR=/ci/akm-data
912
+ ```
913
+
914
+ ### 4. Update write-target flags in scripts
915
+
916
+ See [Removed fallbacks and aliases](#removed-fallbacks-and-aliases) above.
917
+ Replace any `--stash <name>` on `remember` / `import` / `wiki stash` with
918
+ `--target <name>`.
919
+
920
+ ### 5. Convert task definition files from .md to .yml
921
+
922
+ If you have existing task files at `<stash>/tasks/*.md`, they will not appear
923
+ in `akm tasks list` or be run by the scheduler. Convert each one to a `.yml`
924
+ file as described in [Task definition files: .md+frontmatter → .yml](#task-definition-files-mdfrontmatter--yml),
925
+ then run `akm tasks sync` to register them with the OS scheduler.
926
+
927
+ ### 6. Run `akm improve` once to repopulate graph data
928
+
929
+ The DB_VERSION bump from 10 to 17 drops and rebuilds the graph tables. The
930
+ non-graph tables regenerate automatically the first time akm opens `index.db`,
931
+ but graph extraction needs LLM calls and only runs from `akm improve`. See
932
+ [Graph extraction will re-run after upgrade](#graph-extraction-will-re-run-after-upgrade).
933
+
934
+ ## Verifying the upgrade
935
+
936
+ After running the migration script, run:
937
+
938
+ ```sh
939
+ akm info
940
+ ```
941
+
942
+ Look for:
943
+ - `version` reports `0.8.0` or higher.
944
+ - No `WARNING: legacy events.jsonl found` message.
945
+
946
+ Then run the post-upgrade checklist:
947
+
948
+ ```sh
949
+ akm info --format text # 1. version 0.8.x
950
+ akm config list --format json | head -40 # 2. stashes[] populated
951
+ akm list # 3. your sources resolve
952
+ akm events --limit 5 --format json # 4. event log readable from state.db
953
+ akm workflow list --format json | head -20 # 5. active workflow runs intact
954
+ akm search "<a query you know works>" # 6. indexed content still matches
955
+ ```
956
+
957
+ If step 4 returns zero events after a known-active installation, the migration
958
+ script may not have imported the JSONL file. Re-run:
959
+
960
+ ```sh
961
+ akm-migrate-storage --yes
962
+ ```
963
+
964
+ The import step is idempotent: each `(event_type, ts, ref, metadata_json)`
965
+ tuple is pre-checked against the `events` table and skipped if already
966
+ present. Re-running will only insert rows that are missing — it will not
967
+ double-import the rows already in `state.db`.
968
+
969
+ ## Troubleshooting
970
+
971
+ **`akm search` returns no results after upgrading.**
972
+ The search index (`index.db`) may not have been found at its new location. Run
973
+ `akm info` and check the `dataDir` field. If it points to `$CONFIG` instead of
974
+ `$DATA`, `AKM_DATA_DIR` may be unset but `AKM_CONFIG_DIR` was previously
975
+ relied upon as a fallback. Set `AKM_DATA_DIR` explicitly, re-run the migration
976
+ script, or move `index.db` manually.
977
+
978
+ **`akm events` shows 0 results but I had thousands.**
979
+ The migration script imports events in append order. If the JSONL file was
980
+ corrupted or truncated, some rows may have been skipped. Run:
981
+
982
+ ```sh
983
+ akm-migrate-storage --dry-run
984
+ ```
985
+
986
+ and look for import warnings. You can also inspect `state.db` directly:
987
+
988
+ ```sh
989
+ sqlite3 ~/.local/share/akm/state.db 'SELECT COUNT(*) FROM events;'
990
+ ```
991
+
992
+ **`akm list` shows empty stashes.**
993
+ Check that `$DATA/akm.lock` exists. If only `$CONFIG/akm.lock` exists, the
994
+ migration script may not have run. Run:
995
+
996
+ ```sh
997
+ akm-migrate-storage --yes
998
+ ```
999
+
1000
+ **Tasks show no history.**
1001
+ `akm tasks history <id>` reads from the `task_history` table in `state.db`.
1002
+ Run the migration script to import existing JSONL files. If you see records
1003
+ from the migration but are missing recent ones, confirm your akm binary is
1004
+ 0.8.0 (`akm info | grep version`).
1005
+
1006
+ **`akm tasks list` shows fewer tasks than expected (or none).**
1007
+ Task definitions are now read exclusively from `.yml` files. Any `.md` task
1008
+ files from 0.7.x are silently skipped. Run:
1009
+
1010
+ ```sh
1011
+ ls "$AKM_STASH_DIR/tasks"/*.md 2>/dev/null
1012
+ ```
1013
+
1014
+ If `.md` files exist, convert them to `.yml` as described in the
1015
+ [task migration section](#task-definition-files-mdfrontmatter--yml) and
1016
+ re-run `akm tasks sync`.
1017
+
1018
+ **`akm tasks show` says "task not found" for a task that used to exist.**
1019
+ The task's `.md` file has not been converted to `.yml` yet. The lookup
1020
+ resolves to `<id>.yml` regardless of whether you pass `.md` or no suffix in
1021
+ the command. Convert the file and run `akm tasks sync`.
1022
+
1023
+ **`--stash` flag not recognized on `remember`.**
1024
+ The `--stash` flag was removed in 0.8.0. Use `--target` instead:
1025
+
1026
+ ```sh
1027
+ akm remember "note" --target team-stash
1028
+ ```
1029
+
1030
+ ## Rolling back
1031
+
1032
+ If you need to roll back to 0.7.x:
1033
+
1034
+ 1. The migration script copies (not moves) `akm.lock` and imports (not
1035
+ removes) JSONL events. Your original files are intact.
1036
+ 2. Events written to `state.db` by 0.8.0 will not be visible to 0.7.x. If you
1037
+ wrote new events on 0.8.0 and need them in 0.7.x, export them:
1038
+
1039
+ ```sh
1040
+ sqlite3 ~/.local/share/akm/state.db \
1041
+ "SELECT json_object('schemaVersion', schema_version, 'ts', ts, 'eventType', event_type, 'ref', ref, 'metadata', json(metadata)) FROM events ORDER BY id;" \
1042
+ >> ~/.cache/akm/events.jsonl
1043
+ ```
1044
+
1045
+ 3. Reinstall 0.7.x:
1046
+
1047
+ ```sh
1048
+ npm install -g akm-cli@0.7
1049
+ # or
1050
+ bun install -g akm-cli@0.7
1051
+ ```
1052
+
1053
+ Asset files under `$STASH/` are unchanged — stash content is fully
1054
+ forward/backward compatible across 0.7.x and 0.8.x.
1055
+
1056
+ If you find a regression in 0.8.0, file an issue at
1057
+ <https://github.com/itlackey/akm/issues> with the output of `akm info` and a
1058
+ redacted copy of your `config.json`.
1059
+
1060
+ ---
1061
+
1062
+ ## Config 0.8.0 migration (unified profiles)
1063
+
1064
+ 0.8.0 ships a new config shape (`configVersion: "0.8.0"`) that **removes** the
1065
+ legacy `llm`, `agent`, and `features` top-level blocks and reorganizes LLM and
1066
+ agent configuration into a unified `profiles` + `defaults` tree alongside
1067
+ first-class `index.*` and `search.*` feature sections.
1068
+
1069
+ ### What changed
1070
+
1071
+ - **`profiles.llm.<name>`** — each named LLM endpoint declared once (endpoint,
1072
+ model, apiKey, temperature, supportsJsonSchema).
1073
+ - **`profiles.agent.<name>`** — each named agent runtime, with a required
1074
+ `platform: "opencode" | "claude" | "opencode-sdk"` field.
1075
+ - **`profiles.improve.<name>`** — named improve-pipeline profiles. Each lists
1076
+ per-process bindings under `processes.{reflect,distill,consolidate,memoryInference,graphExtraction,feedbackDistillation,validation}`.
1077
+ - **`defaults.llm` / `defaults.agent` / `defaults.improve`** — fallback profile names.
1078
+ - **`index.metadataEnhance` / `index.stalenessDetection`** — first-class
1079
+ feature sections replacing the legacy `features.index.*` entries.
1080
+ - **`search.curateRerank`** — first-class feature section replacing the legacy
1081
+ `features.search.curate_rerank` entry.
1082
+
1083
+ ### Keys removed (not migrated)
1084
+
1085
+ The following keys are **dropped** during migration — they are not transformed
1086
+ to a new location:
1087
+
1088
+ - `sdkMode: true` on agent profiles (replaced by `platform: "opencode-sdk"`)
1089
+ - `config.agent.processes["task"]` (tasks now declare `mode` + `profile` in
1090
+ their own YAML)
1091
+ - `config.improve.schedule` / `improve.schedule` (scheduling lives in stash
1092
+ task YAMLs)
1093
+
1094
+ ### Old key → new location mapping
1095
+
1096
+ | Old key | New location |
1097
+ | --- | --- |
1098
+ | `config.llm` (endpoint/model/apiKey/temperature) | `profiles.llm.default` + `defaults.llm = "default"` |
1099
+ | `config.llm.judgeModel` | preserved on the migrated `profiles.llm.default` entry |
1100
+ | `config.agent.profiles.<n>` | `profiles.agent.<n>` |
1101
+ | `config.agent.default` | `defaults.agent` |
1102
+ | `config.agent.processes.<name>` | `profiles.improve.default.processes.<name>` (`mode: "agent"` + `profile: <agent profile name>`) |
1103
+ | `config.improve.reflectCooldownByType` | `profiles.improve.default.processes.reflect.cooldownByType` |
1104
+ | `config.improve.limit` | `profiles.improve.default.limit` |
1105
+ | `config.llm.features.memory_inference` | `profiles.improve.default.processes.memoryInference.enabled` |
1106
+ | `config.llm.features.graph_extraction` | `profiles.improve.default.processes.graphExtraction.enabled` |
1107
+ | `config.llm.features.metadata_enhance` | `index.metadataEnhance.enabled` |
1108
+ | `config.llm.features.memory_consolidation` | `profiles.improve.default.processes.consolidate.enabled` |
1109
+ | `config.llm.features.feedback_distillation` | `profiles.improve.default.processes.feedbackDistillation.enabled` |
1110
+ | `config.llm.features.curate_rerank` | `search.curateRerank.enabled` |
1111
+ | `config.llm.features.lesson_quality_gate` | `profiles.improve.default.processes.distill.qualityGate.enabled` |
1112
+ | `config.llm.features.proposal_quality_gate` | `profiles.improve.default.processes.reflect.qualityGate.enabled` |
1113
+ | `config.llm.features.memory_contradiction_detection` | `profiles.improve.default.processes.consolidate.contradictionDetection.enabled` |
1114
+ | `config.features.improve.*` | identical migration to the matching `profiles.improve.default.processes.*` entry |
1115
+ | `config.features.index.staleness_detection.options.thresholdDays` | `index.stalenessDetection.thresholdDays` |
1116
+
1117
+ ### Auto-migration at first run
1118
+
1119
+ When akm loads a config that does not have `configVersion: "0.8.0"` (or that
1120
+ contains old keys), it:
1121
+
1122
+ 1. Prints a one-time notice describing the migration.
1123
+ 2. Writes a timestamped backup to `$DATA/config-backups/` before touching the
1124
+ file.
1125
+ 3. Rewrites the config file in place with the v2 shape.
1126
+ 4. Sets `configVersion: "0.8.0"` so subsequent launches skip migration.
1127
+
1128
+ If you run multiple config layers (user + project), each layer is visited and
1129
+ rewritten independently. Read-only layers print the migrated content for manual
1130
+ apply instead of writing.
1131
+
1132
+ **Suppress auto-migration:**
1133
+
1134
+ ```sh
1135
+ export AKM_NO_AUTO_MIGRATE=1
1136
+ ```
1137
+
1138
+ Useful on read-only CI mounts. With this flag set, akm still loads and uses
1139
+ the v1 config for the current run, but does not rewrite the file.
1140
+
1141
+ **Auto-migration banner:** Starting in 0.8.0, when akm auto-migrates your
1142
+ config it prints a loud banner to **both stderr and stdout** so you can see
1143
+ it regardless of which stream your shell or pipeline captures. The banner
1144
+ includes the resolved config file path, the backup directory path, the
1145
+ opt-out instruction, and the preview command:
1146
+
1147
+ ```
1148
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1149
+ akm: auto-migrated config → 0.8.0 format
1150
+ file: /home/user/.config/akm/config.json
1151
+ backup: /home/user/.cache/akm/config-backups/config-<timestamp>.json
1152
+ to opt out of future auto-migration: AKM_NO_AUTO_MIGRATE=1
1153
+ to preview a dry-run diff: akm config migrate --dry-run --print-diff
1154
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1155
+ ```
1156
+
1157
+ ### Manual migration
1158
+
1159
+ To preview the migration without writing (shows what would change):
1160
+
1161
+ ```sh
1162
+ akm config migrate --dry-run
1163
+ ```
1164
+
1165
+ To preview with a full unified diff of old vs new config:
1166
+
1167
+ ```sh
1168
+ akm config migrate --dry-run --print-diff
1169
+ ```
1170
+
1171
+ To apply:
1172
+
1173
+ ```sh
1174
+ akm config migrate
1175
+ ```
1176
+
1177
+ To apply and see a unified diff of what changed:
1178
+
1179
+ ```sh
1180
+ akm config migrate --print-diff
1181
+ ```
1182
+
1183
+ `akm config migrate` acquires a file lock (`<config-dir>/.akm/migrate.lock`)
1184
+ before writing, so concurrent akm commands launched while a migrate is in
1185
+ flight block cleanly rather than racing. Pass `--no-wait` to fail immediately
1186
+ instead of blocking.
1187
+
1188
+ **CI/CD note:** On read-only mounts (e.g. ephemeral containers), set
1189
+ `AKM_NO_AUTO_MIGRATE=1` and run `akm config migrate` explicitly during the
1190
+ deploy pipeline — before any command that reads config — so the config file is
1191
+ rewritten once at deploy time and the container never tries to write at runtime.
1192
+
1193
+ ### Verifying the config migration
1194
+
1195
+ ```sh
1196
+ # Confirm configVersion is present
1197
+ akm config get configVersion
1198
+
1199
+ # Confirm profiles resolved
1200
+ akm config get profiles.llm
1201
+
1202
+ # Confirm profile tree populated
1203
+ akm config get profiles.improve.default
1204
+
1205
+ # Smoke test: confirm improve resolves cleanly without invoking the runner
1206
+ akm improve --dry-run
1207
+ ```
1208
+
1209
+ ## Config layer rewrite (late-0.8.x)
1210
+
1211
+ Late in the 0.8.x cycle the config layer was rewritten end-to-end around a
1212
+ single Zod schema in `src/core/config-schema.ts`. The on-disk shape is
1213
+ unchanged — `~/.config/akm/config.json` from before the rewrite still loads.
1214
+ But several behaviors that used to be silent are now loud, and a handful of
1215
+ configurations that used to silently misbehave are now rejected.
1216
+
1217
+ ### Behavior changes you should know about
1218
+
1219
+ 1. **`akm config set llm.apiKey` and friends now throw.** Persisting API keys
1220
+ in the config file leaked them through backups and version control. The new
1221
+ behavior is to throw `UsageError` pointing at the environment variable
1222
+ instead:
1223
+
1224
+ - `llm.apiKey` → `AKM_LLM_API_KEY`
1225
+ - `embedding.apiKey` → `AKM_EMBED_API_KEY`
1226
+ - `profiles.llm.<name>.apiKey` → `AKM_PROFILE_<NAME>_API_KEY` (with `-`
1227
+ normalized to `_` and uppercased)
1228
+
1229
+ If you had `apiKey` in your config before, it was silently stripped on the
1230
+ first save; that behavior is unchanged for already-loaded configs. The
1231
+ throw only fires on new `akm config set` invocations.
1232
+
1233
+ 2. **Malformed config JSON now throws.** Before the rewrite, if your
1234
+ `config.json` had a syntax error, AKM silently fell back to `DEFAULT_CONFIG`
1235
+ and your settings appeared to vanish. Now you get a clear
1236
+ `ConfigError("Failed to parse config JSON")` with the underlying JSON parse
1237
+ error. Fix the syntax error and AKM resumes normal operation. The
1238
+ file-not-existing case is still the legitimate cold-start path.
1239
+
1240
+ 3. **Project-level `.akm/config.json` files are deprecated.** AKM still merges
1241
+ them into the loaded config in 0.8.x (giving you one release of grace), but
1242
+ you'll see a one-time warning on first discovery:
1243
+
1244
+ ```text
1245
+ [akm] DEPRECATED: project-level config file found at <path>.
1246
+ Project-level config files will be ignored in 0.9.0+.
1247
+ Move your settings to ~/.config/akm/config.json.
1248
+ ```
1249
+
1250
+ If you relied on this, plan to migrate your project-level settings to the
1251
+ user config before 0.9.0 ships.
1252
+
1253
+ 4. **Every schema-leaf key is now reachable via `akm config set`.** Previously
1254
+ the CLI maintained a hand-listed whitelist of settable keys, and several
1255
+ common ones (`defaults.agent`, `search.minScore`, `improve.eventRetentionDays`,
1256
+ `embedding.provider`, `llm.temperature`, `profiles.llm.<name>.*`,
1257
+ `profiles.agent.<name>.*`, `improve.utilityDecay.halfLifeDays`,
1258
+ `feedback.requireReason`, `index.metadataEnhance.enabled`, etc.) were
1259
+ missing. The schema is now the source of truth — if it's in the schema,
1260
+ you can set it. If you have a script that worked around this by editing
1261
+ `config.json` directly, you can now use `akm config set` instead.
1262
+
1263
+ 5. **Unknown keys in nested objects are now rejected.** Before, an unknown
1264
+ key inside `registries[]`, `sources[]`, or `profiles.llm.*` was silently
1265
+ accepted. The schema now uses `.strict()` on those objects — unknown keys
1266
+ throw with a path-pointing error (e.g. `registries.0.somethingTypo:
1267
+ Unrecognized key(s)`). Fix the typo or remove the key.
1268
+
1269
+ 6. **Config backups are bounded to 5.** Each `akm config set` keeps a
1270
+ timestamped backup of the previous config under `~/.cache/akm/config-backups/`.
1271
+ Before the rewrite this was unbounded and could accumulate thousands of
1272
+ entries; now it's pruned to the 5 most recent on each save.
1273
+ `config.latest.json` is preserved separately and continues to hold the
1274
+ most recent pre-write copy.
1275
+
1276
+ 7. **`akm config validate` and `akm config migrate` are real subcommands.**
1277
+ Before, both lived in the codebase but weren't wired into the CLI surface
1278
+ — error messages that referenced them were misleading. Now:
1279
+
1280
+ ```sh
1281
+ akm config validate # check the on-disk file against the schema
1282
+ akm config migrate # migrate legacy shape to 0.8.0 in place
1283
+ akm config migrate --dry-run # preview the migration without writing
1284
+ ```
1285
+
1286
+ 8. **`defaultWriteTarget` validation tightened.** It must name a configured
1287
+ source in `sources[]`. With no sources configured, save-time validation now
1288
+ errors rather than silently accepting an unresolvable name.
1289
+
1290
+ ### Manual migration
1291
+
1292
+ If you have a custom config beyond the documented surface — especially
1293
+ project-level `.akm/config.json` files, env-var literals stored in non-`apiKey`
1294
+ fields, or manually-edited unknown fields — read this section carefully.
1295
+
1296
+ - **Project-level configs (deprecated):** move every setting that lives in a
1297
+ `.akm/config.json` somewhere under your project tree into your
1298
+ `~/.config/akm/config.json`. After 0.9.0 the project-level discovery will
1299
+ be removed entirely.
1300
+ - **Unknown fields you added by hand:** the strict-mode parser will now reject
1301
+ them. Either move them to a documented key, or accept that they were never
1302
+ doing anything anyway.
1303
+ - **`apiKey` in your config file:** export the appropriate env var instead and
1304
+ remove the field from the JSON. AKM reads the env var on every connection.
1305
+ - **CI scripts that captured `akm config set llm.apiKey`:** swap for
1306
+ `export AKM_LLM_API_KEY=...` before the `akm` command.
1307
+