akm-cli 0.8.0-rc2 → 0.8.0

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