akm-cli 0.8.0-rc2 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (313) hide show
  1. package/{.github/CHANGELOG.md → CHANGELOG.md} +238 -3
  2. package/README.md +22 -6
  3. package/SECURITY.md +93 -0
  4. package/dist/assets/help/help-accept.md +12 -0
  5. package/dist/assets/help/help-improve.md +81 -0
  6. package/dist/{commands → assets}/help/help-proposals.md +7 -4
  7. package/dist/assets/help/help-reject.md +11 -0
  8. package/dist/{output → assets/hints}/cli-hints-full.md +60 -32
  9. package/dist/{output → assets/hints}/cli-hints-short.md +10 -7
  10. package/dist/assets/profiles/default.json +15 -0
  11. package/dist/assets/profiles/graph-refresh.json +13 -0
  12. package/dist/assets/profiles/memory-focus.json +12 -0
  13. package/dist/assets/profiles/quick.json +15 -0
  14. package/dist/assets/profiles/thorough.json +15 -0
  15. package/dist/assets/prompts/extract-session.md +80 -0
  16. package/dist/assets/prompts/graph-extract-user-prompt.md +35 -0
  17. package/dist/assets/tasks/graph-refresh-weekly.yml +10 -0
  18. package/dist/cli/config-migrate.js +144 -0
  19. package/dist/cli/config-validate.js +39 -0
  20. package/dist/cli/confirm.js +73 -0
  21. package/dist/cli/parse-args.js +93 -3
  22. package/dist/cli/shared.js +129 -0
  23. package/dist/cli.js +2141 -1268
  24. package/dist/commands/add-cli.js +279 -0
  25. package/dist/commands/agent-dispatch.js +20 -12
  26. package/dist/commands/agent-support.js +11 -5
  27. package/dist/commands/completions.js +3 -0
  28. package/dist/commands/config-cli.js +129 -517
  29. package/dist/commands/consolidate.js +1557 -147
  30. package/dist/commands/curate.js +44 -3
  31. package/dist/commands/db-cli.js +23 -0
  32. package/dist/commands/distill-promotion-policy.js +5 -3
  33. package/dist/commands/distill.js +906 -100
  34. package/dist/commands/env.js +213 -0
  35. package/dist/commands/eval-cases.js +3 -0
  36. package/dist/commands/events.js +3 -0
  37. package/dist/commands/extract-cli.js +127 -0
  38. package/dist/commands/extract-prompt.js +217 -0
  39. package/dist/commands/extract.js +477 -0
  40. package/dist/commands/feedback-cli.js +331 -0
  41. package/dist/commands/graph.js +260 -5
  42. package/dist/commands/health.js +1042 -55
  43. package/dist/commands/history.js +51 -16
  44. package/dist/commands/improve-auto-accept.js +97 -0
  45. package/dist/commands/improve-cli.js +236 -0
  46. package/dist/commands/improve-profiles.js +138 -0
  47. package/dist/commands/improve-result-file.js +167 -0
  48. package/dist/commands/improve.js +1736 -346
  49. package/dist/commands/info.js +26 -28
  50. package/dist/commands/init.js +49 -1
  51. package/dist/commands/installed-stashes.js +6 -23
  52. package/dist/commands/knowledge.js +3 -0
  53. package/dist/commands/lint/agent-linter.js +3 -0
  54. package/dist/commands/lint/base-linter.js +199 -5
  55. package/dist/commands/lint/command-linter.js +3 -0
  56. package/dist/commands/lint/default-linter.js +3 -0
  57. package/dist/commands/lint/env-key-rules.js +154 -0
  58. package/dist/commands/lint/index.js +92 -3
  59. package/dist/commands/lint/knowledge-linter.js +3 -0
  60. package/dist/commands/lint/markdown-insertion.js +343 -0
  61. package/dist/commands/lint/memory-linter.js +3 -0
  62. package/dist/commands/lint/registry.js +3 -0
  63. package/dist/commands/lint/skill-linter.js +3 -0
  64. package/dist/commands/lint/task-linter.js +15 -12
  65. package/dist/commands/lint/types.js +3 -0
  66. package/dist/commands/lint/workflow-linter.js +3 -0
  67. package/dist/commands/lint.js +3 -0
  68. package/dist/commands/migration-help.js +5 -2
  69. package/dist/commands/proposal-drain-policies.js +128 -0
  70. package/dist/commands/proposal-drain.js +477 -0
  71. package/dist/commands/proposal.js +60 -6
  72. package/dist/commands/propose.js +24 -19
  73. package/dist/commands/reflect.js +1004 -94
  74. package/dist/commands/registry-cli.js +150 -0
  75. package/dist/commands/registry-search.js +3 -0
  76. package/dist/commands/remember-cli.js +257 -0
  77. package/dist/commands/remember.js +15 -6
  78. package/dist/commands/schema-repair.js +88 -15
  79. package/dist/commands/search.js +99 -14
  80. package/dist/commands/secret.js +173 -0
  81. package/dist/commands/self-update.js +3 -0
  82. package/dist/commands/show.js +32 -13
  83. package/dist/commands/source-add.js +7 -35
  84. package/dist/commands/source-clone.js +3 -0
  85. package/dist/commands/source-manage.js +3 -0
  86. package/dist/commands/tasks.js +161 -95
  87. package/dist/commands/url-checker.js +3 -0
  88. package/dist/core/action-contributors.js +3 -0
  89. package/dist/core/asset-ref.js +13 -2
  90. package/dist/core/asset-registry.js +9 -2
  91. package/dist/core/asset-serialize.js +88 -0
  92. package/dist/core/asset-spec.js +61 -5
  93. package/dist/core/common.js +93 -5
  94. package/dist/core/concurrent.js +3 -0
  95. package/dist/core/config-io.js +347 -0
  96. package/dist/core/config-migration.js +622 -0
  97. package/dist/core/config-schema.js +558 -0
  98. package/dist/core/config-sources.js +108 -0
  99. package/dist/core/config-types.js +4 -0
  100. package/dist/core/config-walker.js +337 -0
  101. package/dist/core/config.js +366 -1077
  102. package/dist/core/errors.js +42 -20
  103. package/dist/core/events.js +31 -25
  104. package/dist/core/file-lock.js +104 -0
  105. package/dist/core/frontmatter.js +75 -10
  106. package/dist/core/lesson-lint.js +3 -0
  107. package/dist/core/markdown.js +3 -0
  108. package/dist/core/memory-belief.js +62 -0
  109. package/dist/core/memory-contradiction-detect.js +274 -0
  110. package/dist/core/memory-improve.js +142 -14
  111. package/dist/core/parse.js +3 -0
  112. package/dist/core/paths.js +218 -50
  113. package/dist/core/proposal-quality-validators.js +380 -0
  114. package/dist/core/proposal-validators.js +11 -3
  115. package/dist/core/proposals.js +464 -5
  116. package/dist/core/state-db.js +349 -56
  117. package/dist/core/text-truncation.js +107 -0
  118. package/dist/core/time.js +3 -0
  119. package/dist/core/tty.js +59 -0
  120. package/dist/core/warn.js +7 -2
  121. package/dist/core/write-source.js +12 -0
  122. package/dist/indexer/db-backup.js +391 -0
  123. package/dist/indexer/db-search.js +136 -28
  124. package/dist/indexer/db.js +661 -166
  125. package/dist/indexer/ensure-index.js +3 -0
  126. package/dist/indexer/file-context.js +3 -0
  127. package/dist/indexer/graph-boost.js +162 -40
  128. package/dist/indexer/graph-db.js +241 -51
  129. package/dist/indexer/graph-dedup.js +3 -7
  130. package/dist/indexer/graph-extraction.js +242 -149
  131. package/dist/indexer/index-context.js +3 -9
  132. package/dist/indexer/indexer.js +86 -16
  133. package/dist/indexer/llm-cache.js +24 -19
  134. package/dist/indexer/manifest.js +3 -0
  135. package/dist/indexer/matchers.js +184 -11
  136. package/dist/indexer/memory-inference.js +94 -50
  137. package/dist/indexer/metadata-contributors.js +3 -0
  138. package/dist/indexer/metadata.js +110 -50
  139. package/dist/indexer/path-resolver.js +3 -0
  140. package/dist/indexer/project-context.js +192 -0
  141. package/dist/indexer/ranking-contributors.js +134 -7
  142. package/dist/indexer/ranking.js +8 -1
  143. package/dist/indexer/search-fields.js +5 -9
  144. package/dist/indexer/search-hit-enrichers.js +91 -2
  145. package/dist/indexer/search-source.js +20 -1
  146. package/dist/indexer/semantic-status.js +4 -1
  147. package/dist/indexer/staleness-detect.js +447 -0
  148. package/dist/indexer/usage-events.js +12 -9
  149. package/dist/indexer/walker.js +3 -0
  150. package/dist/integrations/agent/builders.js +135 -0
  151. package/dist/integrations/agent/config.js +121 -401
  152. package/dist/integrations/agent/detect.js +3 -0
  153. package/dist/integrations/agent/index.js +6 -14
  154. package/dist/integrations/agent/model-aliases.js +55 -0
  155. package/dist/integrations/agent/profiles.js +3 -0
  156. package/dist/integrations/agent/prompts.js +137 -8
  157. package/dist/integrations/agent/runner.js +208 -0
  158. package/dist/integrations/agent/sdk-runner.js +8 -2
  159. package/dist/integrations/agent/spawn.js +54 -14
  160. package/dist/integrations/github.js +3 -0
  161. package/dist/integrations/lockfile.js +22 -51
  162. package/dist/integrations/session-logs/index.js +4 -0
  163. package/dist/integrations/session-logs/inline-refs.js +35 -0
  164. package/dist/integrations/session-logs/pre-filter.js +152 -0
  165. package/dist/integrations/session-logs/providers/claude-code.js +226 -0
  166. package/dist/integrations/session-logs/providers/opencode.js +231 -25
  167. package/dist/integrations/session-logs/types.js +3 -0
  168. package/dist/llm/call-ai.js +14 -26
  169. package/dist/llm/client.js +16 -2
  170. package/dist/llm/embedder.js +20 -29
  171. package/dist/llm/embedders/cache.js +3 -7
  172. package/dist/llm/embedders/local.js +42 -1
  173. package/dist/llm/embedders/remote.js +20 -8
  174. package/dist/llm/embedders/types.js +3 -7
  175. package/dist/llm/feature-gate.js +92 -56
  176. package/dist/llm/graph-extract.js +402 -31
  177. package/dist/llm/index-passes.js +44 -29
  178. package/dist/llm/memory-infer.js +30 -2
  179. package/dist/llm/metadata-enhance.js +3 -7
  180. package/dist/output/cli-hints.js +7 -4
  181. package/dist/output/context.js +60 -8
  182. package/dist/output/renderers.js +170 -194
  183. package/dist/output/shapes/curate.js +56 -0
  184. package/dist/output/shapes/distill.js +10 -0
  185. package/dist/output/shapes/env-list.js +19 -0
  186. package/dist/output/shapes/events.js +11 -0
  187. package/dist/output/shapes/helpers.js +424 -0
  188. package/dist/output/shapes/history.js +7 -0
  189. package/dist/output/shapes/passthrough.js +105 -0
  190. package/dist/output/shapes/proposal-accept.js +7 -0
  191. package/dist/output/shapes/proposal-diff.js +7 -0
  192. package/dist/output/shapes/proposal-list.js +7 -0
  193. package/dist/output/shapes/proposal-producer.js +11 -0
  194. package/dist/output/shapes/proposal-reject.js +7 -0
  195. package/dist/output/shapes/proposal-show.js +7 -0
  196. package/dist/output/shapes/registry-search.js +6 -0
  197. package/dist/output/shapes/registry.js +30 -0
  198. package/dist/output/shapes/search.js +6 -0
  199. package/dist/output/shapes/secret-list.js +19 -0
  200. package/dist/output/shapes/show.js +6 -0
  201. package/dist/output/shapes/vault-list.js +19 -0
  202. package/dist/output/shapes.js +51 -549
  203. package/dist/output/text/add.js +6 -0
  204. package/dist/output/text/clone.js +6 -0
  205. package/dist/output/text/config.js +6 -0
  206. package/dist/output/text/curate.js +6 -0
  207. package/dist/output/text/distill.js +7 -0
  208. package/dist/output/text/enable-disable.js +7 -0
  209. package/dist/output/text/events.js +10 -0
  210. package/dist/output/text/feedback.js +6 -0
  211. package/dist/output/text/helpers.js +1059 -0
  212. package/dist/output/text/history.js +7 -0
  213. package/dist/output/text/import.js +6 -0
  214. package/dist/output/text/index.js +6 -0
  215. package/dist/output/text/info.js +6 -0
  216. package/dist/output/text/init.js +6 -0
  217. package/dist/output/text/list.js +6 -0
  218. package/dist/output/text/proposal-producer.js +8 -0
  219. package/dist/output/text/proposal.js +12 -0
  220. package/dist/output/text/registry-commands.js +11 -0
  221. package/dist/output/text/registry.js +30 -0
  222. package/dist/output/text/remember.js +6 -0
  223. package/dist/output/text/remove.js +6 -0
  224. package/dist/output/text/save.js +6 -0
  225. package/dist/output/text/search.js +6 -0
  226. package/dist/output/text/show.js +6 -0
  227. package/dist/output/text/update.js +6 -0
  228. package/dist/output/text/upgrade.js +6 -0
  229. package/dist/output/text/vault.js +16 -0
  230. package/dist/output/text/wiki.js +15 -0
  231. package/dist/output/text/workflow.js +14 -0
  232. package/dist/output/text.js +44 -1329
  233. package/dist/registry/build-index.js +3 -0
  234. package/dist/registry/create-provider-registry.js +3 -0
  235. package/dist/registry/factory.js +4 -1
  236. package/dist/registry/origin-resolve.js +3 -0
  237. package/dist/registry/providers/index.js +3 -0
  238. package/dist/registry/providers/skills-sh.js +11 -2
  239. package/dist/registry/providers/static-index.js +10 -1
  240. package/dist/registry/providers/types.js +3 -24
  241. package/dist/registry/resolve.js +11 -16
  242. package/dist/registry/types.js +3 -0
  243. package/dist/scripts/migrate-storage.js +17767 -0
  244. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +9031 -0
  245. package/dist/scripts/migrations/v16-to-v17.js +141 -0
  246. package/dist/setup/detect.js +3 -0
  247. package/dist/setup/ripgrep-install.js +3 -0
  248. package/dist/setup/ripgrep-resolve.js +3 -0
  249. package/dist/setup/setup.js +306 -67
  250. package/dist/setup/steps.js +3 -15
  251. package/dist/sources/include.js +3 -0
  252. package/dist/sources/provider-factory.js +3 -11
  253. package/dist/sources/provider.js +3 -20
  254. package/dist/sources/providers/filesystem.js +19 -23
  255. package/dist/sources/providers/git.js +171 -21
  256. package/dist/sources/providers/index.js +3 -0
  257. package/dist/sources/providers/install-types.js +3 -13
  258. package/dist/sources/providers/npm.js +3 -4
  259. package/dist/sources/providers/provider-utils.js +3 -0
  260. package/dist/sources/providers/sync-from-ref.js +3 -11
  261. package/dist/sources/providers/tar-utils.js +3 -0
  262. package/dist/sources/providers/website.js +18 -22
  263. package/dist/sources/resolve.js +3 -0
  264. package/dist/sources/types.js +3 -0
  265. package/dist/sources/website-ingest.js +3 -0
  266. package/dist/tasks/backends/cron.js +3 -0
  267. package/dist/tasks/backends/exec-utils.js +3 -0
  268. package/dist/tasks/backends/index.js +3 -11
  269. package/dist/tasks/backends/launchd.js +4 -1
  270. package/dist/tasks/backends/schtasks.js +4 -1
  271. package/dist/tasks/parser.js +51 -38
  272. package/dist/tasks/resolveAkmBin.js +3 -0
  273. package/dist/tasks/runner.js +35 -9
  274. package/dist/tasks/schedule.js +20 -1
  275. package/dist/tasks/schema.js +5 -3
  276. package/dist/tasks/validator.js +6 -3
  277. package/dist/version.js +3 -0
  278. package/dist/wiki/wiki-templates.js +6 -3
  279. package/dist/wiki/wiki.js +4 -1
  280. package/dist/workflows/authoring.js +4 -1
  281. package/dist/workflows/cli.js +3 -0
  282. package/dist/workflows/db.js +140 -10
  283. package/dist/workflows/document-cache.js +3 -10
  284. package/dist/workflows/parser.js +3 -0
  285. package/dist/workflows/renderer.js +3 -0
  286. package/dist/workflows/runs.js +18 -1
  287. package/dist/workflows/schema.js +3 -0
  288. package/dist/workflows/scope-key.js +3 -0
  289. package/dist/workflows/validator.js +5 -9
  290. package/docs/README.md +7 -2
  291. package/docs/data-and-telemetry.md +225 -0
  292. package/docs/migration/release-notes/0.7.5.md +2 -2
  293. package/docs/migration/release-notes/0.8.0.md +57 -5
  294. package/docs/migration/v0.7-to-v0.8.md +1378 -0
  295. package/package.json +28 -11
  296. package/.github/LICENSE +0 -374
  297. package/dist/commands/help/help-accept.md +0 -9
  298. package/dist/commands/help/help-improve.md +0 -53
  299. package/dist/commands/help/help-reject.md +0 -8
  300. package/dist/commands/install-audit.js +0 -385
  301. package/dist/commands/vault.js +0 -310
  302. package/dist/indexer/match-contributors.js +0 -141
  303. package/dist/integrations/agent/pipeline.js +0 -39
  304. package/dist/integrations/agent/runners.js +0 -31
  305. package/dist/llm/prompts/graph-extract-user-prompt.md +0 -12
  306. /package/dist/{tasks → assets}/backends/launchd-template.xml +0 -0
  307. /package/dist/{tasks → assets}/backends/schtasks-template.xml +0 -0
  308. /package/dist/{commands → assets}/help/help-propose.md +0 -0
  309. /package/dist/{wiki → assets/wiki}/index-template.md +0 -0
  310. /package/dist/{wiki → assets/wiki}/ingest-workflow-template.md +0 -0
  311. /package/dist/{wiki → assets/wiki}/log-template.md +0 -0
  312. /package/dist/{wiki → assets/wiki}/schema-template.md +0 -0
  313. /package/dist/{workflows → assets/workflows}/workflow-template.md +0 -0
@@ -16,12 +16,13 @@ akm search "<query>" --detail full # Include scores, paths, timing
16
16
 
17
17
  | Flag | Values | Default |
18
18
  | --- | --- | --- |
19
- | `--type` | `skill`, `command`, `agent`, `knowledge`, `workflow`, `script`, `memory`, `vault`, `wiki`, `any` | `any` |
19
+ | `--type` | `skill`, `command`, `agent`, `knowledge`, `workflow`, `script`, `memory`, `env`, `secret`, `wiki`, `any` | `any` |
20
20
  | `--source` | `stash`, `registry`, `both` | `stash` |
21
21
  | `--limit` | number | `20` |
22
22
  | `--format` | `json`, `jsonl`, `text`, `yaml` | `json` |
23
- | `--detail` | `brief`, `normal`, `full`, `summary`, `agent` | `brief` |
24
- | `--for-agent` | boolean (deprecated use `--detail agent`) | `false` |
23
+ | `--detail` | `brief`, `normal`, `full` | `brief` |
24
+ | `--shape` | `human`, `agent`, `summary` (`summary` only on `show`) | `human` |
25
+ | `--for-agent` | boolean (deprecated — use `--shape agent`) | `false` |
25
26
 
26
27
  ## Curate
27
28
 
@@ -59,7 +60,7 @@ akm show knowledge:my-doc # Show content (local or remote)
59
60
  | workflow | `workflowTitle`, `workflowParameters`, `steps` |
60
61
  | memory | `content` (recalled context) |
61
62
  | vault | `keys`, `comments` |
62
- | wiki | `content` (same view modes as knowledge). For any wiki task, run `akm wiki list` then `akm wiki ingest <name>` for the workflow. |
63
+ | wiki | `content` (same view modes as knowledge). For any wiki task, run `akm wiki list`. To ingest sources, `akm wiki ingest <name>` dispatches the configured agent (defaults.agent or `--profile`) to execute the ingest workflow. |
63
64
 
64
65
  ## Capture Knowledge While You Work
65
66
 
@@ -102,13 +103,17 @@ akm wiki stash research https://example.com/paper # Fetch one URL into raw/<slug
102
103
  akm wiki stash research ./paper.md --target my-stash # Route write to a named writable stash source
103
104
  echo "..." | akm wiki stash research - # stdin form
104
105
  akm wiki lint research # Structural checks: orphans, broken xrefs, uncited raws, stale index
105
- akm wiki ingest research # Print the ingest workflow for this wiki (no action)
106
- akm wiki remove research --force # Delete pages/schema/index/log; preserves raw/
107
- akm wiki remove research --force --with-sources # Full nuke, including raw/
106
+ akm wiki ingest research # Dispatch defaults.agent to run the ingest workflow on this wiki
107
+ akm wiki ingest research --profile claude --model sonnet # Override profile and model
108
+ akm wiki ingest research --timeout-ms 600000 # Override agent CLI timeout (default: profile setting)
109
+ akm wiki remove research -y # Delete pages/schema/index/log; preserves raw/ (--force is a deprecated alias for -y)
110
+ akm wiki remove research -y --with-sources # Full nuke, including raw/
108
111
  ```
109
112
 
110
- **For any wiki task, start with `akm wiki list`, then `akm wiki ingest <name>`
111
- to get the step-by-step workflow.** Wiki pages are also addressable as
113
+ **For any wiki task, start with `akm wiki list`. Then `akm wiki ingest <name>`
114
+ dispatches the configured agent (defaults.agent or `--profile`) to execute
115
+ the wiki's ingest workflow end-to-end — schema read, source dedup, search,
116
+ page create/update, log entry, lint, reindex.** Wiki pages are also addressable as
112
117
  `wiki:<name>/<page-path>` and show up in stash-wide `akm search` as
113
118
  `type: wiki`. Files under `raw/` and the wiki root infrastructure files
114
119
  `schema.md`, `index.md`, and `log.md` are not indexed and do not appear in
@@ -163,23 +168,40 @@ akm clone "npm:@scope/pkg//script:deploy.sh" # Clone from remote package
163
168
 
164
169
  When `--dest` is provided, `akm init` is not required first.
165
170
 
166
- ## Save
171
+ ## Sync
167
172
 
168
- Commit local changes in a git-backed stash. Behaviour adapts automatically:
173
+ Commit local changes in a git-backed stash. Behaviour adapts automatically.
174
+ (`akm save` is the deprecated 0.7 spelling — it still works but warns; removed
175
+ in 0.9.0.)
169
176
 
170
- - **Not a git repo** — no-op (silent skip)
177
+ - **No `.git` directory** — no-op (silent skip)
171
178
  - **Git repo, no remote** — stage and commit only (the default stash always falls here)
172
179
  - **Git repo, has remote, not writable** — stage and commit only
173
180
  - **Git repo, has remote, `writable: true`** — stage, commit, and push
181
+ - **Any writable repo with `--no-push`** — stage and commit only
174
182
 
175
183
  ```sh
176
- akm save # Save primary stash (timestamp message)
177
- akm save -m "Add deploy skill" # Save with explicit message
178
- akm save my-skills # Save a named writable git stash
179
- akm save my-skills -m "Update patterns" # Save named stash with message
184
+ akm sync # Sync primary stash (timestamp message)
185
+ akm sync -m "Add deploy skill" # Sync with explicit message
186
+ akm sync --no-push # Commit only; never push
187
+ akm sync my-skills # Sync a named writable git stash
188
+ akm sync my-skills -m "Update patterns" # Sync named stash with message
180
189
  ```
181
190
 
182
- The `--writable` flag on `akm add` opts a remote git stash into push-on-save:
191
+ `akm improve` also performs an end-of-run batch commit for git-backed stashes.
192
+ The `--sync` / `--no-sync` and `--push` / `--no-push` flags control this:
193
+
194
+ ```sh
195
+ akm improve # auto-sync per profile default (default/thorough: on; quick/memory-focus: off)
196
+ akm improve --no-sync # skip the end-of-run commit
197
+ akm improve --no-push # commit but skip push for this run
198
+ akm improve --sync # force sync even on profiles that disable it
199
+ ```
200
+
201
+ Profile sync defaults: `default` and `thorough` auto-commit + push; `quick` and
202
+ `memory-focus` skip sync entirely. Override with `--sync` / `--no-sync` flags.
203
+
204
+ The `--writable` flag on `akm add` opts a remote git stash into push-on-sync:
183
205
 
184
206
  ```sh
185
207
  akm add git@github.com:org/skills.git --provider git --name my-skills --writable
@@ -193,8 +215,8 @@ akm add @scope/stash # From npm (managed)
193
215
  akm add owner/repo # From GitHub (managed)
194
216
  akm add ./path/to/local/stash # Local directory
195
217
  akm add git@github.com:org/repo.git --provider git --name my-skills --writable
196
- akm enable skills.sh # Enable the skills.sh registry
197
- akm disable skills.sh # Disable the skills.sh registry
218
+ akm config enable skills.sh # Enable the skills.sh registry
219
+ akm config disable skills.sh # Disable the skills.sh registry
198
220
  akm list # List all sources
199
221
  akm list --kind managed # List managed sources only
200
222
  akm remove <target> # Remove by id, ref, path, or name
@@ -244,24 +266,29 @@ akm completions --install # Install completions
244
266
  ## Proposals & Improvement (0.8.0+)
245
267
 
246
268
  ```sh
247
- akm improve <ref> # Propose improvement for an asset
248
- akm proposals # List pending proposals
249
- akm show proposal <id> # Render the proposal body
250
- akm diff <ref-or-id> # Diff by ref, UUID, or 8-char prefix (proposal positional optional)
251
- akm diff skill:akm-dream # Diff by asset ref
252
- akm accept 7c115132 # Accept by UUID prefix
253
- akm accept <id> --target team-stash # Accept to a named writable stash source
254
- akm reject skill:my-skill --reason "not ready" # Reject by asset ref
255
- akm reject <id> --reason "..." # Archive with a reason
269
+ akm improve <ref> # Propose improvement for an asset
270
+ akm proposal list # List pending proposals
271
+ akm proposal show <id> # Render the proposal body
272
+ akm proposal diff <ref-or-id> # Diff by ref, UUID, or 8-char prefix
273
+ akm proposal diff skill:akm-dream # Diff by asset ref
274
+ akm proposal accept 7c115132 # Accept by UUID prefix
275
+ akm proposal accept <id> --target team-stash # Accept to a named writable stash source
276
+ akm proposal reject skill:my-skill --reason "not ready" # Reject by asset ref
277
+ akm proposal reject <id> --reason "..." # Archive with a reason
278
+ akm proposal revert <id> # Restore the pre-promotion content
256
279
  ```
257
280
 
281
+ The flat verbs `akm proposals` / `akm show proposal` / `akm accept` /
282
+ `akm reject` / `akm diff` / `akm revert` still work as deprecated aliases
283
+ (warn on stderr; removed in 0.9.0).
284
+
258
285
  Per-task `timeoutMs`: task markdown frontmatter may set `timeoutMs: null` to
259
286
  disable the agent kill timer for long-running local-model tasks, or a number
260
287
  (milliseconds) to override `config.agent.timeoutMs` for that task only.
261
288
 
262
289
  ## Output Control
263
290
 
264
- All commands accept `--format` and `--detail` flags:
291
+ All commands accept `--format`, `--detail`, and `--shape` flags:
265
292
 
266
293
  - `--format json` (default) — structured JSON
267
294
  - `--format jsonl` — one JSON object per line (streaming-friendly)
@@ -270,8 +297,9 @@ All commands accept `--format` and `--detail` flags:
270
297
  - `--detail brief` (default) — compact output
271
298
  - `--detail normal` — adds tags, refs, origins
272
299
  - `--detail full` — includes scores, paths, timing, debug info
273
- - `--detail summary` — metadata only (no content/template/prompt), under 200 tokens
274
- - `--detail agent` — agent-optimized output: strips non-actionable fields
275
- - `--for-agent` — deprecated alias for `--detail agent`
300
+ - `--shape human` (default) standard projection
301
+ - `--shape agent` — agent-optimized output: strips non-actionable fields
302
+ - `--shape summary` — metadata only (no content/template/prompt), under 200 tokens; only valid on `akm show`
303
+ - `--for-agent` — deprecated alias for `--shape agent` (removed 0.9.0)
276
304
 
277
305
  Run `akm -h` or `akm <command> -h` for per-command help.
@@ -33,15 +33,17 @@ akm remember "note" --target my-stash # Route write to a named writable
33
33
  akm import ./notes/release-checklist.md # Import a knowledge doc into your stash
34
34
  akm import ./doc.md --target my-stash # Route import to a named writable stash source
35
35
  akm wiki list # List available wikis
36
- akm wiki ingest <name> # Print the ingest workflow for a wiki
36
+ akm wiki ingest <name> # Dispatch an agent to run the ingest workflow (uses defaults.agent or --profile)
37
37
  akm wiki stash <name> ./paper.md --target my-stash # Route wiki stash write to a named source
38
- akm diff skill:akm-dream # Diff proposal by ref, UUID, or 8-char prefix
39
- akm accept 7c115132 # Accept by UUID prefix (proposal positional optional)
40
- akm reject skill:my-skill --reason "..." # Reject by ref (proposal positional optional)
38
+ akm proposal diff skill:akm-dream # Diff proposal by ref, UUID, or 8-char prefix
39
+ akm proposal accept 7c115132 # Accept by UUID prefix
40
+ akm proposal reject skill:my-skill --reason "..." # Reject by ref
41
41
  akm feedback <ref> --positive|--negative # Record whether an asset helped
42
42
  akm add <ref> # Add a source (npm, GitHub, git, local dir)
43
43
  akm clone <ref> # Copy an asset to the working stash (optional --dest arg to clone to specific location)
44
- akm save # Commit (and push if writable remote) changes in the primary stash
44
+ akm sync # Commit (and push if writable remote) changes in the primary stash (--no-push to commit only)
45
+ akm improve --no-sync # Run improve without the end-of-run auto-commit
46
+ akm improve --no-push # Auto-commit but skip push for this run
45
47
  akm registry search "<query>" # Search all registries
46
48
  ```
47
49
 
@@ -56,8 +58,9 @@ akm registry search "<query>" # Search all registries
56
58
  | knowledge | A reference doc (use `toc` or `section "..."` to navigate) |
57
59
  | workflow | Parsed steps plus workflow-specific execution commands |
58
60
  | memory | Recalled context (read the content for background information) |
59
- | vault | Key names only; use `akm vault path` or `akm vault run` to use values safely |
60
- | wiki | A page in a multi-wiki knowledge base. For any wiki task, start with `akm wiki list`, then `akm wiki ingest <name>` for the workflow. Run `akm wiki -h` for the full surface. |
61
+ | env | A `.env` file of related CONFIGURATION (many vars; sensitive or not — all protected); key names only. Inject with `akm env run <ref> -- <cmd>` (the agent-safe path values stay on disk). `vault` is the deprecated alias. |
62
+ | secret | A single sensitive value for AUTHENTICATION (token, key, cert); name only. Use `akm secret path` / `akm secret run`. |
63
+ | wiki | A page in a multi-wiki knowledge base. For any wiki task, start with `akm wiki list`. To ingest sources, run `akm wiki ingest <name>` — it dispatches the configured agent profile to execute the ingest workflow against the wiki's `raw/` directory. Run `akm wiki -h` for the full surface. |
61
64
 
62
65
  When an asset meaningfully helps or fails, record that with `akm feedback` so
63
66
  future search ranking can learn from real usage.
@@ -0,0 +1,15 @@
1
+ {
2
+ "description": "Standard improve pass — all sub-processes, markdown asset types.",
3
+ "processes": {
4
+ "reflect": {
5
+ "enabled": true,
6
+ "allowedTypes": ["agent", "command", "knowledge", "lesson", "memory", "skill", "wiki", "workflow"]
7
+ },
8
+ "distill": { "enabled": true, "allowedTypes": ["memory"] },
9
+ "consolidate": { "enabled": true, "allowedTypes": ["memory"] },
10
+ "memoryInference": { "enabled": true },
11
+ "graphExtraction": { "enabled": true },
12
+ "triage": { "enabled": false, "applyMode": "queue", "policy": "personal-stash" }
13
+ },
14
+ "sync": { "enabled": true, "push": true }
15
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "description": "Full-corpus graph extraction pass — rebuilds graph relationships across all stash files. Disables all other sub-processes. Use via `akm improve --profile graph-refresh` or the embedded `graph-refresh-weekly` task.",
3
+ "processes": {
4
+ "reflect": { "enabled": false },
5
+ "distill": { "enabled": false },
6
+ "consolidate": { "enabled": false },
7
+ "memoryInference": { "enabled": false },
8
+ "graphExtraction": { "enabled": true, "fullScan": true },
9
+ "extract": { "enabled": false },
10
+ "triage": { "enabled": false }
11
+ },
12
+ "sync": { "enabled": true, "push": true }
13
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "description": "Memory and lesson improvement only — no distill, consolidate, or graphExtraction.",
3
+ "processes": {
4
+ "reflect": { "enabled": true, "allowedTypes": ["memory", "lesson"] },
5
+ "distill": { "enabled": false },
6
+ "consolidate": { "enabled": false },
7
+ "memoryInference": { "enabled": true },
8
+ "graphExtraction": { "enabled": false },
9
+ "triage": { "enabled": false }
10
+ },
11
+ "sync": { "enabled": false }
12
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "description": "Reflect-only pass — no distill, consolidate, memoryInference, or graphExtraction.",
3
+ "processes": {
4
+ "reflect": {
5
+ "enabled": true,
6
+ "allowedTypes": ["agent", "command", "knowledge", "lesson", "memory", "skill", "wiki", "workflow"]
7
+ },
8
+ "distill": { "enabled": false },
9
+ "consolidate": { "enabled": false },
10
+ "memoryInference": { "enabled": false },
11
+ "graphExtraction": { "enabled": false },
12
+ "triage": { "enabled": false }
13
+ },
14
+ "sync": { "enabled": false }
15
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "description": "All sub-processes enabled (currently identical to default; reserved for future divergence).",
3
+ "processes": {
4
+ "reflect": {
5
+ "enabled": true,
6
+ "allowedTypes": ["agent", "command", "knowledge", "lesson", "memory", "skill", "wiki", "workflow"]
7
+ },
8
+ "distill": { "enabled": true, "allowedTypes": ["memory"] },
9
+ "consolidate": { "enabled": true, "allowedTypes": ["memory"] },
10
+ "memoryInference": { "enabled": true },
11
+ "graphExtraction": { "enabled": true },
12
+ "triage": { "enabled": true, "applyMode": "queue" }
13
+ },
14
+ "sync": { "enabled": true, "push": true }
15
+ }
@@ -0,0 +1,80 @@
1
+ You are extracting durable engineering insights from a software session transcript. Most sessions produce zero new durable insights — the agent already captures what's worth keeping via explicit `akm remember` / `akm feedback` calls during the session. Your job is to identify learnings that slipped through.
2
+
3
+ ## What counts as "durable insight"
4
+
5
+ Things worth extracting:
6
+ - Recovery patterns ("X fails when Y; the fix is Z")
7
+ - Hidden constraints discovered mid-task ("this codebase requires X before deploy")
8
+ - Architecture observations ("module A is consumed by B and C via the D bus")
9
+ - Non-obvious workarounds for real defects
10
+ - Domain knowledge the agent learned and would benefit a future session
11
+
12
+ Things NOT to extract:
13
+ - Successful command sequences (already in git history / shell history)
14
+ - Tool counts / aggregates
15
+ - The agent's own narrative about what it was doing
16
+ - Restatements of what the user asked for
17
+ - Generic platitudes ("test your code", "be careful with X")
18
+ - Anything already preserved via the agent's explicit calls (see below)
19
+
20
+ ## Already preserved by the agent — DO NOT re-extract
21
+
22
+ {{ALREADY_PRESERVED}}
23
+
24
+ ## Session metadata
25
+
26
+ - Harness: {{HARNESS}}
27
+ - Title: {{TITLE}}
28
+ - Started: {{STARTED_AT}}
29
+ - Ended: {{ENDED_AT}}
30
+ - Project hint: {{PROJECT_HINT}}
31
+
32
+ ## Filtered session transcript
33
+
34
+ The transcript below has already had read-only `akm` meta-ops and platform boilerplate stripped. Only content that might carry signal remains.
35
+
36
+ {{TRANSCRIPT}}
37
+
38
+ ## Output contract
39
+
40
+ Respond with EXACTLY one JSON object matching this shape:
41
+
42
+ ```
43
+ {
44
+ "candidates": [
45
+ {
46
+ "type": "memory" | "lesson" | "knowledge",
47
+ "name": "<kebab-case-slug>",
48
+ "description": "<one sentence 20-400 chars>",
49
+ "when_to_use": "<one sentence 15-400 chars; REQUIRED only when type=lesson>",
50
+ "body": "<markdown body, 200-3000 chars typical>",
51
+ "confidence": <number 0.0-1.0>,
52
+ "evidence": "<one-line pointer to the moment in the session>"
53
+ }
54
+ ],
55
+ "rationale_if_empty": "<one sentence; REQUIRED when candidates is empty>"
56
+ }
57
+ ```
58
+
59
+ ## Rules
60
+
61
+ 1. **Zero candidates is a valid and frequent answer.** Most sessions yield no new durable insight. When that's the case, return `{"candidates": [], "rationale_if_empty": "..."}` explaining what you saw and why it didn't rise to durable-knowledge level. Do not fabricate.
62
+
63
+ 2. **Pick the right type per candidate:**
64
+ - `memory` — a fact or short observation. Use for "X works on this codebase", "auth uses Y library version Z".
65
+ - `lesson` — a "do X / avoid Y" pattern, ALWAYS with `when_to_use`. Use for hard-won learnings about pitfalls, recovery patterns, or non-obvious gotchas.
66
+ - `knowledge` — substantive multi-section reference doc. Rare from one session.
67
+
68
+ 3. **Calibrate `confidence` honestly:**
69
+ - `0.9+` — high certainty this is a real durable insight a reviewer would clearly accept
70
+ - `0.7-0.89` — clear improvement, but a reviewer might prefer different framing or scope
71
+ - `0.5-0.69` — marginal / judgment call
72
+ - `<0.5` — don't include; prefer fewer-but-better candidates
73
+
74
+ 4. **`evidence` must reference the session** — a brief pointer like "agent's tool failure at ts=...", "user's correction at ...", "after the recovery in the Bash sequence around ...". Without evidence the candidate is hard to validate; default to lower confidence when evidence is vague.
75
+
76
+ 5. **Do not duplicate already-preserved content.** If a candidate substantively overlaps with anything in the "Already preserved" list above, skip it.
77
+
78
+ 6. **No speculation.** Only extract things the session genuinely demonstrates. If the agent struggled and didn't resolve, that may itself be a lesson (`when_to_use: "When attempting X, expect Y to fail"`) — but only if the failure mode is concrete enough to be useful next time.
79
+
80
+ 7. Respond with the JSON object only. No prose before or after. No code fences.
@@ -0,0 +1,35 @@
1
+ Extract entities and relations from the asset body below.
2
+
3
+ Rules:
4
+ - Output ONLY a JSON object: {"entities": ["Entity One", ...], "relations": [{"from": "A", "to": "B", "type": "uses"}, ...]}.
5
+ - Entities are short, canonical noun phrases (project names, services, tools, people, file/dir names, technical concepts).
6
+ - Relations connect two entities that both appear in the entities array.
7
+ - "type" is a short verb phrase (e.g. "uses", "depends on", "owns", "documents"). Optional; omit when unsure.
8
+ - Drop pleasantries, meta-commentary, and timestamps.
9
+ - Limit to at most {{MAX_ENTITIES}} entities and {{MAX_RELATIONS}} relations per asset.
10
+ - Return {"entities": [], "relations": []} if the body has no extractable graph content.
11
+ - DO NOT return markdown code blocks, ONLY valid JSON objects.
12
+
13
+ Examples:
14
+
15
+ Input:
16
+ ## Deployment Notes
17
+ The auth-service uses PostgreSQL for user sessions. It depends on the redis-cache
18
+ for rate limiting. The terraform-provisioner deploys everything to the prod cluster.
19
+ Owner: @alice.
20
+
21
+ Output:
22
+ {"entities":["auth-service","PostgreSQL","redis-cache","terraform-provisioner","prod cluster","@alice"],"relations":[{"from":"auth-service","to":"PostgreSQL","type":"uses"},{"from":"auth-service","to":"redis-cache","type":"depends on"},{"from":"terraform-provisioner","to":"prod cluster","type":"deploys"},{"from":"terraform-provisioner","to":"auth-service","type":"deploys"},{"from":"@alice","to":"auth-service","type":"owns"}]}
23
+
24
+ Input:
25
+ ## Meeting: API Redesign
26
+ Discussed moving from REST to GraphQL. The frontend team will use Apollo Client.
27
+ Backend needs to implement resolvers. Timeline: Q2.
28
+
29
+ Output:
30
+ {"entities":["REST","GraphQL","Apollo Client","frontend team","backend","resolvers","Q2"],"relations":[{"from":"frontend team","to":"Apollo Client","type":"uses"},{"from":"backend","to":"resolvers","type":"implements"},{"from":"frontend team","to":"GraphQL","type":"migrates to"}]}
31
+
32
+ ===============
33
+
34
+ Request:
35
+
@@ -0,0 +1,10 @@
1
+ id: graph-refresh-weekly
2
+ description: >
3
+ Weekly full-corpus graph extraction pass. Rebuilds entity and relation
4
+ indexes across all stash files using the graph-refresh improve profile.
5
+ Complements the per-run improve pipeline which only extracts graph data
6
+ for files touched by actionable refs.
7
+ command: akm improve --profile graph-refresh
8
+ schedule: "0 3 * * 0"
9
+ enabled: false
10
+ timeout: 3600000
@@ -0,0 +1,144 @@
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ import fs from "node:fs";
5
+ import path from "node:path";
6
+ import { stripJsonComments } from "../core/config";
7
+ import { unifiedDiff, withConfigLock, writeConfigAtomic } from "../core/config-io";
8
+ import { migrateConfigShape } from "../core/config-migration";
9
+ import { getCacheDir, getConfigPath } from "../core/paths";
10
+ import { warn } from "../core/warn";
11
+ export { migrateConfigShape } from "../core/config-migration";
12
+ const PROJECT_CONFIG_RELATIVE_PATH = path.join(".akm", "config.json");
13
+ function backupConfigFile(configPath) {
14
+ if (!fs.existsSync(configPath))
15
+ return;
16
+ const backupDir = path.join(getCacheDir(), "config-backups");
17
+ fs.mkdirSync(backupDir, { recursive: true });
18
+ const timestamp = new Date().toISOString().replace(/[.:]/g, "-");
19
+ const backupPath = path.join(backupDir, `config-${timestamp}.json`);
20
+ fs.copyFileSync(configPath, backupPath);
21
+ const latestPath = path.join(backupDir, "config.latest.json");
22
+ fs.copyFileSync(configPath, latestPath);
23
+ }
24
+ function acquireMigrateLock(lockPath, noWait) {
25
+ const lockDir = path.dirname(lockPath);
26
+ fs.mkdirSync(lockDir, { recursive: true });
27
+ const maxAttempts = noWait ? 1 : 20;
28
+ const delayMs = 200;
29
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
30
+ try {
31
+ fs.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
32
+ return () => {
33
+ try {
34
+ fs.unlinkSync(lockPath);
35
+ }
36
+ catch {
37
+ // ignore
38
+ }
39
+ };
40
+ }
41
+ catch {
42
+ if (noWait) {
43
+ return null;
44
+ }
45
+ // Simple busy-wait — synchronous since this is a one-shot CLI action
46
+ const deadline = Date.now() + delayMs;
47
+ while (Date.now() < deadline) {
48
+ // spin
49
+ }
50
+ }
51
+ }
52
+ return null;
53
+ }
54
+ export async function migrateConfigFile(filePath, opts) {
55
+ if (!fs.existsSync(filePath)) {
56
+ return { changed: false, result: {} };
57
+ }
58
+ const text = fs.readFileSync(filePath, "utf8");
59
+ let raw;
60
+ try {
61
+ const stripped = stripJsonComments(text);
62
+ const parsed = JSON.parse(stripped);
63
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
64
+ warn(`[akm] config-migrate: ${filePath} is not a valid JSON object, skipping.`);
65
+ return { changed: false, result: {} };
66
+ }
67
+ raw = parsed;
68
+ }
69
+ catch {
70
+ warn(`[akm] config-migrate: failed to parse ${filePath}, skipping.`);
71
+ return { changed: false, result: {} };
72
+ }
73
+ const { changed, result } = migrateConfigShape(raw);
74
+ if (!changed) {
75
+ return { changed: false, result };
76
+ }
77
+ const migratedText = `${JSON.stringify(result, null, 2)}\n`;
78
+ // WS-2: compute a diff when requested (always computed for --print-diff;
79
+ // never requires a write so safe in --dry-run).
80
+ const diff = opts.printDiff ? unifiedDiff(text, migratedText, filePath) : undefined;
81
+ if (opts.dryRun) {
82
+ return { changed: true, result, diff };
83
+ }
84
+ // WS-3: acquire config write lock + use atomic write (tmp → rename).
85
+ withConfigLock(() => {
86
+ backupConfigFile(filePath);
87
+ writeConfigAtomic(filePath, result);
88
+ });
89
+ return { changed: true, result, diff };
90
+ }
91
+ function discoverProjectConfigPaths(startDir) {
92
+ const paths = [];
93
+ let currentDir = path.resolve(startDir);
94
+ while (true) {
95
+ const configPath = path.join(currentDir, PROJECT_CONFIG_RELATIVE_PATH);
96
+ if (fs.existsSync(configPath) && fs.statSync(configPath).isFile()) {
97
+ paths.unshift(configPath);
98
+ }
99
+ const parentDir = path.dirname(currentDir);
100
+ if (parentDir === currentDir)
101
+ break;
102
+ currentDir = parentDir;
103
+ }
104
+ return paths;
105
+ }
106
+ export async function runConfigMigrate(opts) {
107
+ const userConfigPath = getConfigPath();
108
+ const projectPaths = discoverProjectConfigPaths(process.cwd());
109
+ const allPaths = [userConfigPath, ...projectPaths].filter((p, i, arr) => arr.indexOf(p) === i && fs.existsSync(p));
110
+ if (allPaths.length === 0) {
111
+ console.log("No config files found to migrate.");
112
+ return;
113
+ }
114
+ const lockPath = path.join(getCacheDir(), "config-migrate.lock");
115
+ const releaseLock = acquireMigrateLock(lockPath, opts.noWait ?? false);
116
+ if (!releaseLock) {
117
+ warn("[akm] config-migrate: another migration is already in progress, skipping.");
118
+ return;
119
+ }
120
+ try {
121
+ let anyChanged = false;
122
+ for (const filePath of allPaths) {
123
+ const { changed, diff } = await migrateConfigFile(filePath, { dryRun: opts.dryRun, printDiff: opts.printDiff });
124
+ if (changed) {
125
+ const action = opts.dryRun ? "would migrate" : "migrated";
126
+ console.log(`[akm] ${action}: ${filePath}`);
127
+ // WS-2: print the unified diff to stdout when --print-diff is set.
128
+ if (diff) {
129
+ console.log(diff);
130
+ }
131
+ anyChanged = true;
132
+ }
133
+ else {
134
+ console.log(`[akm] already up to date: ${filePath}`);
135
+ }
136
+ }
137
+ if (!anyChanged) {
138
+ console.log("All config files are already at the current version.");
139
+ }
140
+ }
141
+ finally {
142
+ releaseLock();
143
+ }
144
+ }
@@ -0,0 +1,39 @@
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ /**
5
+ * `akm config validate` — verify the on-disk config matches the schema.
6
+ *
7
+ * Reads the user config file, validates against AkmConfigSchema, and either
8
+ * prints "All checks passed." or a list of structured errors (path + message).
9
+ * Exits non-zero on errors so it composes well in CI hooks.
10
+ */
11
+ import fs from "node:fs";
12
+ import { parseConfigText } from "../core/config-io";
13
+ import { validateConfigShape } from "../core/config-schema";
14
+ import { ConfigError } from "../core/errors";
15
+ import { getConfigPath } from "../core/paths";
16
+ export async function runConfigValidate() {
17
+ const configPath = getConfigPath();
18
+ if (!fs.existsSync(configPath)) {
19
+ console.log(`No config file at ${configPath} — nothing to validate.`);
20
+ return;
21
+ }
22
+ let text;
23
+ try {
24
+ text = fs.readFileSync(configPath, "utf8");
25
+ }
26
+ catch (err) {
27
+ const detail = err instanceof Error ? err.message : String(err);
28
+ throw new ConfigError(`Could not read config at ${configPath}: ${detail}`, "INVALID_CONFIG_FILE");
29
+ }
30
+ // parseConfigText throws ConfigError on malformed JSON (#458). Surface as-is.
31
+ const raw = parseConfigText(text, configPath);
32
+ const result = validateConfigShape(raw);
33
+ if (result.ok) {
34
+ console.log(`All checks passed. (${configPath})`);
35
+ return;
36
+ }
37
+ const lines = result.errors.map((e) => ` - ${e.path || "(root)"}: ${e.message}`).join("\n");
38
+ throw new ConfigError(`Config at ${configPath} has ${result.errors.length} validation error${result.errors.length === 1 ? "" : "s"}:\n${lines}`, "INVALID_CONFIG_FILE", "Fix the listed fields, or run `akm config migrate` if the errors look like legacy-shape leftovers.");
39
+ }