akm-cli 0.9.0-rc.8 → 0.9.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 (450) hide show
  1. package/CHANGELOG.md +1063 -44
  2. package/README.md +51 -25
  3. package/SECURITY.md +14 -1
  4. package/STABILITY.md +497 -0
  5. package/dist/akm +148 -35
  6. package/dist/{akm-migrate-storage → akm-migrate} +6 -9
  7. package/dist/assets/hints/cli-hints-full.md +223 -95
  8. package/dist/assets/hints/cli-hints-short.md +85 -22
  9. package/dist/assets/improve-strategies/default.json +1 -1
  10. package/dist/assets/improve-strategies/reflect-distill.json +1 -1
  11. package/dist/assets/prompts/memory-infer-user.md +2 -3
  12. package/dist/assets/stash-skeleton/README.md +6 -5
  13. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +2 -0
  14. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +2 -0
  15. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +2 -0
  16. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +2 -0
  17. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +2 -0
  18. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +2 -0
  19. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +2 -0
  20. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +2 -0
  21. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +2 -0
  22. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +2 -0
  23. package/dist/assets/stash-skeleton/facts/conventions/domains.md +2 -0
  24. package/dist/assets/stash-skeleton/facts/conventions/organization.md +20 -9
  25. package/dist/assets/tasks/core/extract.yml +1 -1
  26. package/dist/assets/tasks/core/version-check.yml +1 -1
  27. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +5 -0
  28. package/dist/assets/tasks/improve/akm-improve-catchup.yml +8 -0
  29. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +5 -0
  30. package/dist/assets/tasks/improve/akm-improve-frequent.yml +5 -0
  31. package/dist/assets/tasks/improve/akm-improve-nightly.yml +5 -0
  32. package/dist/assets/templates/html/health.html +1 -3
  33. package/dist/assets/workflows/workflow-template.md +32 -15
  34. package/dist/cli/invocation.js +40 -15
  35. package/dist/cli/parse-args.js +0 -22
  36. package/dist/cli/retired-commands.js +121 -0
  37. package/dist/cli/shared.js +154 -22
  38. package/dist/cli/unknown-flags.js +236 -0
  39. package/dist/cli-node.mjs +2 -1
  40. package/dist/cli.js +696 -258
  41. package/dist/commands/agent/agent-dispatch.js +14 -3
  42. package/dist/commands/agent/contribute-cli.js +73 -88
  43. package/dist/commands/completions.js +79 -22
  44. package/dist/commands/config-cli.js +17 -150
  45. package/dist/commands/env/env-cli.js +59 -143
  46. package/dist/commands/env/env.js +12 -163
  47. package/dist/commands/env/marker-path.js +6 -0
  48. package/dist/commands/env/secret-cli.js +36 -66
  49. package/dist/commands/env/secret.js +24 -57
  50. package/dist/commands/feedback-cli.js +141 -87
  51. package/dist/commands/health/accept-rate.js +58 -0
  52. package/dist/commands/health/advisories.js +3 -4
  53. package/dist/commands/health/checks.js +85 -23
  54. package/dist/commands/health/html-report.js +7 -10
  55. package/dist/commands/health/improve-metrics.js +25 -83
  56. package/dist/commands/health/md-report.js +5 -9
  57. package/dist/commands/health/metrics.js +62 -20
  58. package/dist/commands/health/renderers.js +47 -0
  59. package/dist/commands/health/report-view-model.js +4 -5
  60. package/dist/commands/health/stash-exposure.js +1 -1
  61. package/dist/commands/health/surfaces.js +3 -48
  62. package/dist/commands/health/task-runs.js +3 -67
  63. package/dist/commands/health/types-improve.js +7 -0
  64. package/dist/commands/health.js +99 -28
  65. package/dist/commands/improve/anti-collapse.js +2 -2
  66. package/dist/commands/improve/autonomy-gate.js +68 -0
  67. package/dist/commands/improve/collapse-detector.js +41 -40
  68. package/dist/commands/improve/consolidate/eligibility.js +1 -23
  69. package/dist/commands/improve/consolidate/merge.js +4 -0
  70. package/dist/commands/improve/consolidate.js +140 -1000
  71. package/dist/commands/improve/distill/promote-memory.js +12 -12
  72. package/dist/commands/improve/distill/quality-gate.js +6 -6
  73. package/dist/commands/improve/distill.js +58 -69
  74. package/dist/commands/improve/eligibility.js +105 -57
  75. package/dist/commands/improve/extract-cli.js +14 -133
  76. package/dist/commands/improve/improve-cli.js +98 -114
  77. package/dist/commands/improve/improve-result-file.js +1 -28
  78. package/dist/commands/improve/improve-strategies.js +8 -5
  79. package/dist/commands/improve/improve.js +128 -91
  80. package/dist/commands/improve/loop-stages.js +182 -20
  81. package/dist/commands/improve/memory/derived-ref.js +45 -43
  82. package/dist/commands/improve/memory/memory-belief.js +1 -1
  83. package/dist/commands/improve/memory/memory-contradiction-detect.js +4 -12
  84. package/dist/commands/improve/memory/memory-improve.js +6 -5
  85. package/dist/commands/improve/outcome-loop.js +22 -65
  86. package/dist/commands/improve/preparation.js +114 -123
  87. package/dist/commands/improve/proactive-maintenance.js +2 -5
  88. package/dist/commands/improve/reflect.js +56 -160
  89. package/dist/commands/improve/salience.js +11 -122
  90. package/dist/commands/improve/source-identity.js +10 -38
  91. package/dist/commands/lint/base-linter.js +20 -124
  92. package/dist/commands/lint/env-key-rules.js +31 -47
  93. package/dist/commands/lint/index.js +249 -43
  94. package/dist/commands/{events.js → log.js} +33 -38
  95. package/dist/commands/migrate-cli.js +92 -12
  96. package/dist/commands/migration-tool.js +46 -0
  97. package/dist/commands/observability-cli.js +70 -209
  98. package/dist/commands/proposal/drain.js +101 -29
  99. package/dist/commands/proposal/proposal-cli.js +76 -48
  100. package/dist/commands/proposal/proposal.js +54 -18
  101. package/dist/commands/proposal/propose-cli.js +88 -0
  102. package/dist/commands/proposal/propose.js +23 -15
  103. package/dist/commands/proposal/repository.js +701 -278
  104. package/dist/commands/proposal/validators/proposal-quality-validators.js +2 -8
  105. package/dist/commands/proposal/validators/proposal-validators.js +55 -7
  106. package/dist/commands/proposal/validators/proposals.js +4 -7
  107. package/dist/commands/read/curate.js +34 -53
  108. package/dist/commands/read/knowledge.js +150 -95
  109. package/dist/commands/read/registry-search.js +2 -2
  110. package/dist/commands/read/remember-cli.js +42 -15
  111. package/dist/commands/read/search-cli.js +180 -78
  112. package/dist/commands/read/search.js +58 -43
  113. package/dist/commands/read/show.js +197 -141
  114. package/dist/commands/registry-cli.js +12 -51
  115. package/dist/commands/remember.js +14 -57
  116. package/dist/commands/sources/add-cli.js +100 -31
  117. package/dist/commands/sources/bundle-cli.js +166 -0
  118. package/dist/commands/sources/bundle-config-ops.js +7 -2
  119. package/dist/commands/sources/info.js +18 -5
  120. package/dist/commands/sources/init.js +12 -12
  121. package/dist/commands/sources/installed-stashes.js +382 -98
  122. package/dist/commands/sources/schema-repair.js +3 -2
  123. package/dist/commands/sources/self-update.js +131 -38
  124. package/dist/commands/sources/source-add.js +72 -17
  125. package/dist/commands/sources/source-clone.js +129 -45
  126. package/dist/commands/sources/source-manage.js +43 -23
  127. package/dist/commands/sources/sources-cli.js +57 -208
  128. package/dist/commands/sources/stash-cli.js +46 -53
  129. package/dist/commands/tasks/tasks-cli.js +91 -97
  130. package/dist/commands/tasks/tasks.js +276 -421
  131. package/dist/commands/workflow-cli.js +175 -450
  132. package/dist/core/adapter/adapters/akm-adapter.js +47 -28
  133. package/dist/core/adapter/adapters/akm-lint.js +42 -27
  134. package/dist/core/adapter/adapters/akm-metadata.js +15 -44
  135. package/dist/core/adapter/adapters/akm-task-adapter.js +15 -13
  136. package/dist/core/adapter/adapters/akm-workflow-adapter.js +55 -71
  137. package/dist/core/adapter/adapters/dotenv-adapter.js +1 -1
  138. package/dist/core/adapter/adapters/generic-files-adapter.js +2 -0
  139. package/dist/core/adapter/adapters/index.js +6 -6
  140. package/dist/core/adapter/adapters/llm-wiki-adapter.js +14 -8
  141. package/dist/core/adapter/adapters/okf-adapter.js +187 -19
  142. package/dist/core/adapter/adapters/shared.js +3 -19
  143. package/dist/core/adapter/adapters/tool-dir-shared.js +8 -3
  144. package/dist/core/adapter/adapters/website-snapshot-adapter.js +1 -0
  145. package/dist/core/adapter/detect-adapter.js +17 -0
  146. package/dist/core/adapter/recognize-match.js +6 -4
  147. package/dist/core/adapter/validate-context.js +214 -0
  148. package/dist/core/asset/akm-markdown.js +63 -0
  149. package/dist/core/asset/asset-placement.js +20 -6
  150. package/dist/core/asset/asset-ref.js +11 -9
  151. package/dist/core/asset/frontmatter-lint.js +30 -0
  152. package/dist/core/asset/frontmatter.js +37 -9
  153. package/dist/core/asset/markdown.js +40 -51
  154. package/dist/core/asset/resolve-ref.js +89 -18
  155. package/dist/core/asset/stash-meta.js +1 -1
  156. package/dist/core/bundle-id.js +51 -0
  157. package/dist/core/common.js +152 -38
  158. package/dist/core/config/config-io.js +12 -1
  159. package/dist/core/config/config-schema.js +35 -8
  160. package/dist/core/config/config-sources.js +55 -11
  161. package/dist/core/config/config-walker.js +25 -9
  162. package/dist/core/config/config.js +9 -48
  163. package/dist/core/config/experimental.js +21 -0
  164. package/dist/core/config/schema/embedding.js +5 -1
  165. package/dist/core/config/schema/experimental.js +30 -0
  166. package/dist/core/config/schema/improve-processes.js +0 -6
  167. package/dist/core/config/schema/improve.js +21 -3
  168. package/dist/core/config/schema/index-config.js +8 -15
  169. package/dist/core/config/schema/output.js +4 -1
  170. package/dist/core/config/schema/setup.js +9 -18
  171. package/dist/core/config/schema/sources-bundles.js +49 -33
  172. package/dist/core/config/schema/workflow.js +3 -3
  173. package/dist/core/env-secret-ref.js +76 -46
  174. package/dist/core/errors.js +18 -12
  175. package/dist/core/events.js +46 -128
  176. package/dist/core/file-change.js +6 -5
  177. package/dist/core/fs-txn.js +83 -7
  178. package/dist/core/git-message.js +2 -2
  179. package/dist/core/improve-result.js +1 -100
  180. package/dist/core/lesson-lint.js +1 -17
  181. package/dist/core/logs-db.js +2 -1
  182. package/dist/core/migration-operation.js +16 -0
  183. package/dist/core/mutation-target.js +78 -0
  184. package/dist/core/parse.js +4 -1
  185. package/dist/core/paths.js +17 -20
  186. package/dist/core/recognition-util.js +12 -14
  187. package/dist/core/redaction.js +34 -0
  188. package/dist/core/standards/resolve-standards-context.js +2 -14
  189. package/dist/core/standards/resolve-stash-standards.js +2 -2
  190. package/dist/core/standards/resolve-type-conventions.js +2 -2
  191. package/dist/core/state/migrations.js +41 -18
  192. package/dist/core/state-db.js +5 -14
  193. package/dist/core/structured.js +1 -1
  194. package/dist/core/subprocess.js +6 -4
  195. package/dist/core/text-truncation.js +9 -5
  196. package/dist/core/type-presentation.js +3 -3
  197. package/dist/core/warn.js +0 -3
  198. package/dist/core/write-source.js +771 -95
  199. package/dist/indexer/bundle-identity-guard.js +3 -2
  200. package/dist/indexer/db/graph-db.js +0 -24
  201. package/dist/indexer/ensure-index.js +1 -0
  202. package/dist/indexer/graph/graph-boost.js +9 -34
  203. package/dist/indexer/graph/graph-extraction.js +8 -5
  204. package/dist/indexer/index-writer-lock.js +53 -17
  205. package/dist/indexer/index-written-assets.js +16 -22
  206. package/dist/indexer/indexer.js +497 -239
  207. package/dist/indexer/installations.js +14 -96
  208. package/dist/indexer/passes/dir-staleness.js +16 -9
  209. package/dist/indexer/passes/memory-inference.js +11 -9
  210. package/dist/indexer/passes/metadata.js +113 -47
  211. package/dist/indexer/scan/doc-to-entry.js +38 -1
  212. package/dist/indexer/scan/drain-dir.js +13 -23
  213. package/dist/indexer/search/db-search.js +99 -54
  214. package/dist/indexer/search/fts-query.js +47 -24
  215. package/dist/indexer/search/ranking-contributors.js +42 -20
  216. package/dist/indexer/search/ranking.js +18 -99
  217. package/dist/indexer/search/search-fields.js +7 -2
  218. package/dist/indexer/search/search-source.js +82 -93
  219. package/dist/indexer/usage/usage-events.js +0 -89
  220. package/dist/indexer/walk/file-context.js +2 -1
  221. package/dist/indexer/walk/matchers.js +30 -43
  222. package/dist/indexer/walk/path-resolver.js +7 -2
  223. package/dist/indexer/walk/walker.js +38 -12
  224. package/dist/integrations/agent/builders.js +0 -6
  225. package/dist/integrations/agent/config.js +2 -2
  226. package/dist/integrations/agent/detect.js +49 -19
  227. package/dist/integrations/agent/engine-fallback.js +76 -0
  228. package/dist/integrations/agent/profiles.js +14 -0
  229. package/dist/integrations/agent/prompts.js +12 -8
  230. package/dist/integrations/agent/runner-dispatch.js +4 -2
  231. package/dist/integrations/agent/runner.js +0 -1
  232. package/dist/integrations/agent/spawn.js +5 -6
  233. package/dist/integrations/github.js +1 -1
  234. package/dist/integrations/harnesses/aider/agent-builder.js +6 -4
  235. package/dist/integrations/harnesses/amazonq/agent-builder.js +7 -4
  236. package/dist/integrations/harnesses/claude/session-log.js +0 -10
  237. package/dist/integrations/harnesses/codex/agent-builder.js +5 -2
  238. package/dist/integrations/harnesses/copilot/agent-builder.js +5 -3
  239. package/dist/integrations/harnesses/gemini/agent-builder.js +5 -3
  240. package/dist/integrations/harnesses/index.js +3 -7
  241. package/dist/integrations/harnesses/opencode/agent-builder.js +21 -2
  242. package/dist/integrations/harnesses/opencode/session-log.js +0 -15
  243. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +13 -4
  244. package/dist/integrations/harnesses/openhands/agent-builder.js +9 -6
  245. package/dist/integrations/harnesses/pi/agent-builder.js +6 -4
  246. package/dist/integrations/lockfile.js +101 -6
  247. package/dist/integrations/session-logs/index.js +3 -28
  248. package/dist/llm/client.js +136 -100
  249. package/dist/llm/embedders/remote.js +13 -5
  250. package/dist/llm/feature-gate.js +4 -12
  251. package/dist/llm/graph-extract.js +5 -11
  252. package/dist/llm/memory-infer.js +144 -1
  253. package/dist/llm/metadata-enhance.js +5 -7
  254. package/dist/llm/structured-call.js +1 -1
  255. package/dist/llm/usage-persist.js +26 -5
  256. package/dist/llm/usage-telemetry.js +25 -2
  257. package/dist/output/cli-hints.js +1 -2
  258. package/dist/output/context.js +22 -7
  259. package/dist/output/format-exempt.js +80 -0
  260. package/dist/output/generic-render.js +259 -0
  261. package/dist/output/render-registry.js +57 -0
  262. package/dist/output/renderers.js +14 -36
  263. package/dist/output/shapes/curate.js +10 -1
  264. package/dist/output/shapes/events.js +12 -7
  265. package/dist/output/shapes/helpers.js +56 -83
  266. package/dist/output/shapes/migrate.js +8 -0
  267. package/dist/output/shapes/passthrough.js +7 -41
  268. package/dist/output/shapes/proposal/producer.js +15 -7
  269. package/dist/output/shapes.js +2 -9
  270. package/dist/output/text/{init.js → bundle-create.js} +3 -1
  271. package/dist/output/text/bundle-show.js +7 -0
  272. package/dist/output/text/command-format.js +164 -96
  273. package/dist/output/text/env.js +1 -3
  274. package/dist/output/text/events.js +8 -7
  275. package/dist/output/text/health-format.js +103 -0
  276. package/dist/output/text/health.js +7 -0
  277. package/dist/output/text/helpers.js +10 -8
  278. package/dist/output/text/lint-format.js +43 -0
  279. package/dist/output/text/{save.js → lint.js} +2 -2
  280. package/dist/output/text/migrate.js +88 -0
  281. package/dist/output/text/proposal/producer.js +4 -2
  282. package/dist/output/text/proposal-format.js +44 -72
  283. package/dist/output/text/registry-commands.js +1 -2
  284. package/dist/output/text/show-directives.js +15 -7
  285. package/dist/output/text/status-list.js +32 -0
  286. package/dist/output/text/sync.js +5 -0
  287. package/dist/output/text/workflow-format.js +24 -203
  288. package/dist/output/text/workflow.js +1 -7
  289. package/dist/output/text.js +16 -17
  290. package/dist/registry/factory.js +4 -6
  291. package/dist/registry/origin-resolve.js +16 -27
  292. package/dist/registry/providers/skills-sh.js +3 -3
  293. package/dist/registry/providers/static-index.js +13 -23
  294. package/dist/registry/resolve.js +42 -7
  295. package/dist/registry/semver.js +34 -84
  296. package/dist/runtime.js +2 -23
  297. package/dist/scripts/akm-migrate-node.js +60290 -0
  298. package/dist/scripts/akm-migrate.js +59628 -0
  299. package/dist/setup/detect.js +42 -15
  300. package/dist/setup/registry-stash-loader.js +2 -2
  301. package/dist/setup/setup.js +236 -136
  302. package/dist/setup/steps/connection.js +7 -9
  303. package/dist/setup/steps/platforms.js +9 -9
  304. package/dist/setup/steps/semantic.js +15 -3
  305. package/dist/setup/steps/sources.js +12 -13
  306. package/dist/setup/steps/stashdir.js +2 -3
  307. package/dist/setup/steps/tasks.js +237 -120
  308. package/dist/sources/freshness.js +1 -1
  309. package/dist/sources/provider-factory.js +11 -17
  310. package/dist/sources/providers/filesystem.js +2 -3
  311. package/dist/sources/providers/git-install.js +278 -34
  312. package/dist/sources/providers/git-provider.js +25 -23
  313. package/dist/sources/providers/git-stash.js +395 -106
  314. package/dist/sources/providers/git.js +2 -2
  315. package/dist/sources/providers/npm.js +16 -19
  316. package/dist/sources/providers/provider-utils.js +7 -4
  317. package/dist/sources/providers/sync-from-ref.js +3 -9
  318. package/dist/sources/providers/website.js +6 -1
  319. package/dist/sources/resolve.js +6 -5
  320. package/dist/sources/snapshot-fetchers/bluesky.js +146 -0
  321. package/dist/sources/snapshot-fetchers/content-extract.js +566 -0
  322. package/dist/sources/snapshot-fetchers/fetcher-util.js +41 -0
  323. package/dist/sources/snapshot-fetchers/github.js +100 -0
  324. package/dist/sources/snapshot-fetchers/host-guard.js +291 -0
  325. package/dist/sources/snapshot-fetchers/registry.js +17 -1
  326. package/dist/sources/snapshot-fetchers/robots.js +348 -0
  327. package/dist/sources/snapshot-fetchers/rss.js +282 -0
  328. package/dist/sources/snapshot-fetchers/secret-seam.js +42 -0
  329. package/dist/sources/snapshot-fetchers/website-ingest.js +566 -268
  330. package/dist/sources/snapshot-fetchers/x.js +910 -0
  331. package/dist/storage/database.js +7 -0
  332. package/dist/storage/engines/sqlite-migrations.js +23 -111
  333. package/dist/storage/managed-db.js +2 -2
  334. package/dist/storage/repositories/canaries-repository.js +1 -1
  335. package/dist/storage/repositories/events-repository.js +27 -11
  336. package/dist/storage/repositories/improve-runs-repository.js +6 -12
  337. package/dist/storage/repositories/index-connection.js +17 -6
  338. package/dist/storage/repositories/index-entries-repository.js +151 -240
  339. package/dist/storage/repositories/index-entry-mapper.js +15 -11
  340. package/dist/storage/repositories/index-fts-repository.js +5 -2
  341. package/dist/storage/repositories/index-llm-cache-repository.js +0 -1
  342. package/dist/storage/repositories/index-meta-repository.js +2 -3
  343. package/dist/storage/repositories/index-schema.js +10 -25
  344. package/dist/storage/repositories/index-utility-repository.js +15 -28
  345. package/dist/storage/repositories/index-vec-repository.js +6 -1
  346. package/dist/storage/repositories/outcome-repository.js +119 -0
  347. package/dist/storage/repositories/proposals-repository.js +296 -59
  348. package/dist/storage/repositories/registry-cache.js +19 -0
  349. package/dist/storage/repositories/salience-repository.js +172 -0
  350. package/dist/storage/repositories/task-history-repository.js +15 -13
  351. package/dist/storage/repositories/workflow-runs-repository.js +52 -40
  352. package/dist/tasks/backends/cron.js +105 -15
  353. package/dist/tasks/backends/index.js +1 -1
  354. package/dist/tasks/backends/launchd.js +85 -38
  355. package/dist/tasks/backends/schtasks.js +135 -15
  356. package/dist/tasks/embedded.js +56 -40
  357. package/dist/tasks/parser.js +7 -157
  358. package/dist/tasks/resolve-akm-bin.js +137 -59
  359. package/dist/tasks/runner.js +79 -42
  360. package/dist/tasks/scheduler-invocation.js +220 -10
  361. package/dist/tasks/schema.js +24 -1
  362. package/dist/tasks/task-id.js +1 -3
  363. package/dist/tasks/validator.js +20 -6
  364. package/dist/workflows/authoring/authoring.js +94 -143
  365. package/dist/workflows/authoring/scope-key.js +1 -1
  366. package/dist/workflows/exec/frozen-judge.js +28 -2
  367. package/dist/workflows/exec/native-executor.js +77 -57
  368. package/dist/workflows/exec/param-secrets.js +9 -9
  369. package/dist/workflows/exec/run-workflow.js +133 -79
  370. package/dist/workflows/exec/step-work.js +219 -346
  371. package/dist/{migrate-storage-node.mjs → workflows/exec/unit-dispatch.js} +1 -5
  372. package/dist/workflows/ir/compile.js +141 -270
  373. package/dist/workflows/ir/freeze.js +40 -30
  374. package/dist/workflows/ir/params.js +135 -11
  375. package/dist/workflows/ir/plan-hash.js +1 -1
  376. package/dist/workflows/ir/schema.js +25 -26
  377. package/dist/workflows/parser.js +872 -307
  378. package/dist/workflows/program/expressions.js +20 -208
  379. package/dist/workflows/program/schema.js +7 -10
  380. package/dist/workflows/renderer.js +95 -68
  381. package/dist/workflows/resource-limits.js +2 -0
  382. package/dist/workflows/runtime/checkin.js +3 -3
  383. package/dist/workflows/runtime/plan-classifier.js +16 -75
  384. package/dist/workflows/runtime/runs.js +186 -127
  385. package/dist/workflows/runtime/unit-checkin.js +1 -1
  386. package/dist/workflows/runtime/unit-phases.js +2 -2
  387. package/dist/workflows/runtime/workflow-asset-loader.js +232 -83
  388. package/dist/workflows/schema.js +1 -11
  389. package/dist/workflows/validate-summary.js +30 -36
  390. package/dist/workflows/validator.js +21 -62
  391. package/docs/README.md +68 -0
  392. package/docs/migration/README.md +8 -0
  393. package/docs/migration/release-notes/0.7.0.md +11 -11
  394. package/docs/migration/release-notes/0.9.0.md +208 -27
  395. package/docs/migration/v0.7-to-v0.8.md +46 -47
  396. package/docs/migration/v0.8-to-v0.9.md +564 -208
  397. package/docs/migration/v0.9.0-troubleshooting.md +561 -0
  398. package/docs/reference/README.md +12 -0
  399. package/docs/reference/cli.md +2253 -0
  400. package/docs/reference/configuration.md +358 -0
  401. package/docs/reference/data-and-telemetry.md +105 -42
  402. package/docs/reference/workflows.md +647 -0
  403. package/package.json +22 -11
  404. package/schemas/akm-asset-envelope.json +93 -0
  405. package/schemas/akm-config.json +81 -128
  406. package/schemas/akm-workflow.json +74 -73
  407. package/dist/assets/tasks/core/backup.yml +0 -5
  408. package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
  409. package/dist/cli/config-migrate.js +0 -1806
  410. package/dist/cli/config-validate.js +0 -41
  411. package/dist/commands/backup-cli.js +0 -56
  412. package/dist/commands/bundle/bundle-cli.js +0 -68
  413. package/dist/commands/bundle/bundle.js +0 -219
  414. package/dist/commands/graph/graph-cli.js +0 -124
  415. package/dist/commands/graph/graph.js +0 -489
  416. package/dist/commands/improve/extract-watch.js +0 -140
  417. package/dist/commands/mv-cli.js +0 -1221
  418. package/dist/commands/sources/history.js +0 -201
  419. package/dist/commands/tasks/default-tasks.js +0 -186
  420. package/dist/core/migration-backup.js +0 -1234
  421. package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -95
  422. package/dist/llm/memory-infer-impl.js +0 -138
  423. package/dist/migrate/legacy/config-source-migration.js +0 -223
  424. package/dist/migrate/legacy/content-migration.js +0 -305
  425. package/dist/migrate/legacy/legacy-layout.js +0 -779
  426. package/dist/migrate/legacy/legacy-paths.js +0 -25
  427. package/dist/migrate/legacy/legacy-stash-json.js +0 -72
  428. package/dist/migrate/legacy/proposal-fs-import.js +0 -168
  429. package/dist/migrate/legacy/task-target-ref-migration.js +0 -272
  430. package/dist/migrate/legacy/three-db-cutover.js +0 -841
  431. package/dist/migrate/legacy/workflow-migrations-bodies.js +0 -52
  432. package/dist/migrate/legacy/workflow-migrations-frozen.js +0 -21
  433. package/dist/migrate/legacy-ref-grammar.js +0 -214
  434. package/dist/output/shapes/distill.js +0 -14
  435. package/dist/output/shapes/history.js +0 -11
  436. package/dist/output/text/distill.js +0 -6
  437. package/dist/output/text/enable-disable.js +0 -8
  438. package/dist/output/text/history.js +0 -6
  439. package/dist/registry/build-index.js +0 -382
  440. package/dist/schemas/akm-config.json +0 -4704
  441. package/dist/schemas/akm-task.json +0 -87
  442. package/dist/schemas/akm-workflow.json +0 -372
  443. package/dist/scripts/migrate-storage.js +0 -3816
  444. package/dist/workflows/authoring/workflow-program-template.yaml +0 -31
  445. package/dist/workflows/cli.js +0 -53
  446. package/dist/workflows/exec/brief.js +0 -481
  447. package/dist/workflows/exec/report.js +0 -1460
  448. package/dist/workflows/exec/watch.js +0 -116
  449. package/dist/workflows/program/parser.js +0 -813
  450. package/dist/workflows/program/project.js +0 -104
@@ -20,8 +20,10 @@ import path from "node:path";
20
20
  import { isDeepStrictEqual } from "node:util";
21
21
  import * as p from "../cli/clack.js";
22
22
  import { akmInit } from "../commands/sources/init.js";
23
- import { DEFAULT_CONFIG, loadUserConfig, mutateConfigWithPrecommit, parseAndValidateConfigText, primaryBundlePath, validateCompleteConfig, } from "../core/config/config.js";
23
+ import { deriveBundleIds } from "../core/bundle-id.js";
24
+ import { bundleEntryToSourceEntry, DEFAULT_CONFIG, loadUserConfig, mutateConfigWithPrecommit, parseAndValidateConfigText, primaryBundlePath, validateCompleteConfig, } from "../core/config/config.js";
24
25
  import { readConfigText } from "../core/config/config-io.js";
26
+ import { listTopLevelConfigKeys } from "../core/config/config-schema.js";
25
27
  import { deepMergeConfig } from "../core/config/deep-merge.js";
26
28
  import { ConfigError, UsageError } from "../core/errors.js";
27
29
  import { getConfigPath, getDefaultStashDir, isTransientStashPath } from "../core/paths.js";
@@ -32,7 +34,6 @@ import { detectAgentCliProfiles, pickDefaultAgentProfile } from "../integrations
32
34
  import { defaultProfileName } from "../integrations/harnesses/index.js";
33
35
  import { readLockfile } from "../integrations/lockfile.js";
34
36
  import { probeLlmCapabilities } from "../llm/client.js";
35
- import { migrateConfigSourcesToBundles } from "../migrate/legacy/config-source-migration.js";
36
37
  import { detectEnvironment, detectLMStudio, renderDetectionSummary, } from "./detect.js";
37
38
  import { upsertDetectedAgentEngine, upsertDetectedLlmEngine, verifyOpenAiCompatibleEndpoint } from "./detected-engines.js";
38
39
  import { readCurrentLlmEngine, writeAgentEngines, writeLlmEngine } from "./engine-config.js";
@@ -47,13 +48,13 @@ import { printCapabilitySummary, stepAgentCliDetection, stepAgentPlatforms, step
47
48
  import { stepSemanticSearch } from "./steps/semantic.js";
48
49
  import { stepAdditionalSources, stepAddSources, stepRegistries } from "./steps/sources.js";
49
50
  import { stepStashDir } from "./steps/stashdir.js";
50
- import { stepDefaultImproveTasks, stepScheduledTasks } from "./steps/tasks.js";
51
+ import { stepScheduledTasks } from "./steps/tasks.js";
51
52
  // ── Setup sandbox guard ─────────────────────────────────────────────────────
52
53
  /**
53
- * Refuse to persist an explicit `--dir /tmp/...` stashDir to the user's
54
- * config. The OS may reap the directory at any time, and the next run will
55
- * see a `stashDir` that points at a deleted path (falling back to ~/akm
56
- * silently). Mirrors the `assertInitSandbox` check in commands/init.ts, but
54
+ * Refuse to persist an explicit `--dir /tmp/...` as the default bundle path.
55
+ * The OS may reap the directory at any time, leaving the next run with a
56
+ * default bundle that points at a deleted path. Mirrors the
57
+ * `assertInitSandbox` check in commands/init.ts, but
57
58
  * fires under all runtimes (not just `bun test`) because `akm setup --dir
58
59
  * /tmp/X` is a documented isolation pattern that has been observed to
59
60
  * silently clobber the host config (the 2026-05-23 setup-clobbers-user-config
@@ -61,7 +62,7 @@ import { stepDefaultImproveTasks, stepScheduledTasks } from "./steps/tasks.js";
61
62
  *
62
63
  * Escape hatch: set `AKM_FORCE_SETUP_TMP_STASH=1` to override. When the
63
64
  * escape hatch is on, `applyStashIsolationToEnv` below also pre-sets
64
- * `AKM_STASH_DIR` so that the `getConfigDir` / `getCacheDir` isolation
65
+ * `AKM_BUNDLE_DIR` so that the `getConfigDir` / `getCacheDir` isolation
65
66
  * rules fire and config + cache writes route into `$stashDir/.akm/`
66
67
  * instead of the user's host `~/.config/akm`.
67
68
  */
@@ -73,7 +74,7 @@ function assertSetupSandbox(stashDir, dirExplicitlyProvided) {
73
74
  if (!isTransientStashPath(stashDir))
74
75
  return;
75
76
  throw new ConfigError(`refusing to run \`akm setup --dir ${stashDir}\`: the path is in a transient/sandbox directory family the OS may reap. ` +
76
- "Persisting it as the user's stashDir would leave the next run pointing at a deleted path (silently falling back to ~/akm). " +
77
+ "Persisting it as the default bundle would leave the next run pointing at a deleted path. " +
77
78
  "Use a persistent directory, OR set AKM_FORCE_SETUP_TMP_STASH=1 if you intentionally want a sandbox setup " +
78
79
  "(setup will also auto-isolate config + cache writes into $stashDir/.akm/ so the host config is preserved).", "SETUP_TMP_STASH_REFUSED");
79
80
  }
@@ -81,11 +82,11 @@ function assertSetupSandbox(stashDir, dirExplicitlyProvided) {
81
82
  * Propagate the explicit `--dir <stashDir>` choice to the env so that the
82
83
  * `getConfigDir` / `getCacheDir` isolation rules in `src/core/paths.ts`
83
84
  * actually fire for the duration of this setup run. Without this, a CLI
84
- * caller who passes `--dir /tmp/X` but doesn't pre-export `AKM_STASH_DIR`
85
+ * caller who passes `--dir /tmp/X` but doesn't pre-export `AKM_BUNDLE_DIR`
85
86
  * would still write config to the host `~/.config/akm/config.json`. We
86
87
  * only set the env var when:
87
88
  * - `--dir` was explicitly provided (we have an operator-stated stash), AND
88
- * - `AKM_STASH_DIR` is not already set (caller's explicit env wins).
89
+ * - `AKM_BUNDLE_DIR` is not already set (caller's explicit env wins).
89
90
  * The set is process-wide; for the CLI that's the right scope (the process
90
91
  * is about to do all its work against this stash). For tests, each test
91
92
  * already isolates env via beforeEach/afterEach so there is no leak.
@@ -93,12 +94,37 @@ function assertSetupSandbox(stashDir, dirExplicitlyProvided) {
93
94
  function applyStashIsolationToEnv(stashDir, dirExplicitlyProvided) {
94
95
  if (!dirExplicitlyProvided)
95
96
  return;
96
- if (process.env.AKM_STASH_DIR?.trim())
97
+ if (process.env.AKM_BUNDLE_DIR?.trim())
97
98
  return;
98
- process.env.AKM_STASH_DIR = stashDir;
99
+ process.env.AKM_BUNDLE_DIR = stashDir;
99
100
  }
100
101
  // ── Helpers ─────────────────────────────────────────────────────────────────
101
- /** Raw preflight used before setup performs prompts, initialization, or writes. */
102
+ /**
103
+ * Raw preflight used before setup performs prompts, initialization, or writes.
104
+ *
105
+ * `setup` is allowlisted in `shouldBypassConfigStartup` (src/cli.ts) so it
106
+ * never dies on the CLI's own startup config load — but every entry point
107
+ * below (`runSetupWizard`, `runSetupWithDefaults`, `runSetupFromConfig`,
108
+ * `runResetRecommended`) still needs the CURRENT config as its merge base, so
109
+ * this is the first thing each one calls. When the on-disk config predates
110
+ * 0.9 (or is otherwise unparseable/invalid), `parseAndValidateConfigText`
111
+ * throws `ConfigError`; deliberately let that propagate rather than falling
112
+ * back to a fresh default config here.
113
+ *
114
+ * That fallback was considered and rejected: `mutateConfigWithPrecommit`
115
+ * (src/core/config/config.ts) re-reads and re-validates this SAME file
116
+ * inside its write lock before saving, so a wizard that "survived" this
117
+ * preflight by substituting `DEFAULT_CONFIG` would still crash with the
118
+ * identical error at the final save — after prompting the user through the
119
+ * entire wizard. Worse, making the save path itself tolerate an old config
120
+ * would mean writing a freshly-generated 0.9 config over the live 0.8 file
121
+ * `akm migrate apply` needs to read as ITS OWN input — replacing the user's
122
+ * real engine/source settings with the wizard's placeholder ones, silently,
123
+ * on the one path (`akm migrate ...`) the upgrade docs tell users to run
124
+ * first. Refusing here — before any prompt, detection, or write — is strictly
125
+ * safer: the file is left byte-for-byte untouched and the error message
126
+ * below points straight at the real recovery command.
127
+ */
102
128
  export function assertSetupConfigPreflight() {
103
129
  const configPath = getConfigPath();
104
130
  let text;
@@ -108,8 +134,21 @@ export function assertSetupConfigPreflight() {
108
134
  catch (error) {
109
135
  throw new ConfigError(`Could not read config at ${configPath}: ${error instanceof Error ? error.message : String(error)}`, "INVALID_CONFIG_FILE");
110
136
  }
111
- if (text !== undefined)
137
+ if (text === undefined)
138
+ return;
139
+ try {
112
140
  parseAndValidateConfigText(text, configPath);
141
+ }
142
+ catch (error) {
143
+ if (!(error instanceof ConfigError))
144
+ throw error;
145
+ if (error.code !== "UNSUPPORTED_CONFIG_VERSION" && error.code !== "INVALID_CONFIG_FILE")
146
+ throw error;
147
+ throw new ConfigError(`\`akm setup\` cannot run: the config at ${configPath} did not load (${error.message}). ` +
148
+ "It was left untouched — setup never writes over a config it cannot first read cleanly.", error.code, "Run `akm migrate status` to check what this config needs before continuing, or `akm help migrate 0.9.0` " +
149
+ "for the full upgrade checklist. To abandon it and start fresh instead, move it aside " +
150
+ `(e.g. \`mv ${configPath} ${configPath}.bak\`) and re-run \`akm setup\`.`);
151
+ }
113
152
  }
114
153
  function isPlainRecord(value) {
115
154
  if (!value || typeof value !== "object" || Array.isArray(value))
@@ -202,56 +241,104 @@ async function saveSetupConfig(original, desired, precommit) {
202
241
  const result = await mutateConfigWithPrecommit((latest) => rebaseSetupChanges(original, desired, latest), precommit);
203
242
  return { config: result.config, precommit: result.precommit };
204
243
  }
205
- /** The registry-managed (lock-backed) bundles of a config, preserved through setup. */
206
- function managedBundles(bundles) {
207
- if (!bundles)
208
- return {};
209
- const lockIds = new Set(readLockfile().map((entry) => entry.id));
210
- const out = {};
211
- for (const [key, bundle] of Object.entries(bundles)) {
212
- if (lockIds.has(key))
213
- out[key] = bundle;
214
- }
215
- return out;
244
+ /** Registry-managed bundles are not editable through setup's source picker. */
245
+ function managedBundleIds() {
246
+ return new Set(readLockfile().map((entry) => entry.id));
216
247
  }
217
- /**
218
- * Fold the wizard's flat scratch model (`stashDir` primary + `sources[]`) into
219
- * the persisted 0.9.0 `bundles` + `defaultBundle` shape (spec §10.1), reusing the
220
- * shared migrator mapping (D-R5 / Decision E). The registry-managed (lock-backed)
221
- * bundles from the loaded config are preserved verbatim — the wizard only
222
- * re-specifies the primary and the plain sources. Returns a config that is never
223
- * half-migrated (no `stashDir`/`sources`/`installed` leak through).
224
- */
248
+ function setupSourceDescriptor(source) {
249
+ switch (source.type) {
250
+ case "filesystem":
251
+ return source.path ? { path: source.path } : undefined;
252
+ case "git":
253
+ return source.url ? { git: source.url } : undefined;
254
+ case "website":
255
+ return source.url ? { website: { url: source.url, ...(source.options ?? {}) } } : undefined;
256
+ case "npm":
257
+ return source.path ? { npm: source.path } : undefined;
258
+ default:
259
+ return undefined;
260
+ }
261
+ }
262
+ function selectsExistingBundle(source, id, bundle) {
263
+ const configured = bundleEntryToSourceEntry(id, bundle);
264
+ return (configured !== undefined &&
265
+ configured.type === source.type &&
266
+ configured.path === source.path &&
267
+ configured.url === source.url);
268
+ }
269
+ /** Convert only the wizard's private drafts, leaving current bundle entries intact. */
225
270
  function finalizeSetupDraft(draft) {
226
- const raw = { ...draft };
227
- const hasScratch = raw.stashDir !== undefined || raw.sources !== undefined || raw.installed !== undefined;
228
- if (!hasScratch)
229
- return draft;
230
- const preservedManaged = managedBundles(draft.bundles);
231
- // When the sources step never ran (non-interactive --yes/--from paths set
232
- // only the scratch stashDir), the user made no choice about existing PLAIN
233
- // secondary bundles preserve them rather than silently dropping config.
234
- // The interactive flow re-specifies them via the toggle list into scratch
235
- // `sources`, so this branch stays empty there.
236
- const preservedPlain = {};
237
- if (raw.sources === undefined && draft.bundles) {
238
- const managedKeys = new Set(Object.keys(preservedManaged));
239
- for (const [key, bundle] of Object.entries(draft.bundles)) {
240
- if (!managedKeys.has(key) && key !== draft.defaultBundle)
241
- preservedPlain[key] = bundle;
271
+ const { primaryPath, additionalSources, ...canonical } = draft;
272
+ const finalized = canonical;
273
+ if (primaryPath === undefined && additionalSources === undefined)
274
+ return finalized;
275
+ const bundles = { ...(draft.bundles ?? {}) };
276
+ const managedIds = managedBundleIds();
277
+ const newBundles = [];
278
+ if (additionalSources !== undefined) {
279
+ const selectedExistingIds = new Set();
280
+ for (const source of additionalSources) {
281
+ const existing = source.name ? bundles[source.name] : undefined;
282
+ if (source.name &&
283
+ source.name !== draft.defaultBundle &&
284
+ existing &&
285
+ selectsExistingBundle(source, source.name, existing)) {
286
+ selectedExistingIds.add(source.name);
287
+ continue;
288
+ }
289
+ const descriptor = setupSourceDescriptor(source);
290
+ if (!descriptor)
291
+ continue;
292
+ newBundles.push({
293
+ descriptor,
294
+ derivationPath: source.path ? path.resolve(source.path) : (source.url ?? ""),
295
+ preferredId: source.name,
296
+ primary: false,
297
+ writable: source.writable,
298
+ });
299
+ }
300
+ for (const id of Object.keys(bundles)) {
301
+ if (id === draft.defaultBundle || managedIds.has(id) || selectedExistingIds.has(id))
302
+ continue;
303
+ delete bundles[id];
242
304
  }
243
305
  }
244
- // Drop the stale bundles so the migrator re-derives the primary + plain
245
- // sources cleanly from the scratch fields; then merge the managed bundles back.
246
- delete raw.bundles;
247
- delete raw.defaultBundle;
248
- const derived = migrateConfigSourcesToBundles(raw);
249
- const bundles = { ...preservedManaged, ...(derived.bundles ?? {}) };
250
- const finalized = { ...derived };
306
+ let defaultBundle = draft.defaultBundle;
307
+ const configuredPrimaryPath = primaryBundlePath(draft);
308
+ if (primaryPath !== undefined &&
309
+ (configuredPrimaryPath === undefined || path.resolve(configuredPrimaryPath) !== path.resolve(primaryPath))) {
310
+ if (defaultBundle && !managedIds.has(defaultBundle))
311
+ delete bundles[defaultBundle];
312
+ defaultBundle = undefined;
313
+ newBundles.unshift({
314
+ descriptor: { path: primaryPath },
315
+ derivationPath: path.resolve(primaryPath),
316
+ primary: true,
317
+ writable: true,
318
+ });
319
+ }
320
+ const reserved = Object.keys(bundles).map((id) => ({ path: id, registryId: id }));
321
+ const ids = deriveBundleIds([
322
+ ...reserved,
323
+ ...newBundles.map((bundle) => ({ path: bundle.derivationPath, registryId: bundle.preferredId })),
324
+ ]).slice(reserved.length);
325
+ for (const [index, bundle] of newBundles.entries()) {
326
+ const id = ids[index];
327
+ bundles[id] = {
328
+ ...bundle.descriptor,
329
+ ...(bundle.writable !== undefined ? { writable: bundle.writable } : {}),
330
+ };
331
+ if (bundle.primary)
332
+ defaultBundle = id;
333
+ }
251
334
  if (Object.keys(bundles).length > 0)
252
335
  finalized.bundles = bundles;
253
336
  else
254
337
  delete finalized.bundles;
338
+ if (defaultBundle !== undefined)
339
+ finalized.defaultBundle = defaultBundle;
340
+ else
341
+ delete finalized.defaultBundle;
255
342
  return finalized;
256
343
  }
257
344
  /**
@@ -304,14 +391,14 @@ export function buildSetupSteps(options) {
304
391
  const steps = [
305
392
  {
306
393
  id: "stash-dir",
307
- label: "Stash Directory",
394
+ label: "Bundle Directory",
308
395
  nonInteractive: true,
309
396
  async run(ctx) {
310
397
  const stashDir = await stepStashDir(ctx.config, {
311
398
  nonInteractive: ctx.nonInteractive,
312
399
  preferredDir: options.preferredStashDir,
313
400
  });
314
- ctx.apply({ stashDir });
401
+ ctx.apply({ primaryPath: stashDir });
315
402
  },
316
403
  },
317
404
  {
@@ -359,17 +446,17 @@ export function buildSetupSteps(options) {
359
446
  },
360
447
  {
361
448
  id: "stash-sources",
362
- label: "Stash Sources",
449
+ label: "Bundle Sources",
363
450
  async run(ctx) {
364
451
  const stashes = await stepAddSources(ctx.config, { promptForAdditional: false });
365
- const platforms = await stepAgentPlatforms({ ...ctx.config, sources: stashes });
452
+ const platforms = await stepAgentPlatforms({ ...ctx.config, additionalSources: stashes });
366
453
  const merged = [...stashes];
367
454
  for (const ps of platforms) {
368
455
  if (!merged.some((s) => s.path === ps.path))
369
456
  merged.push(ps);
370
457
  }
371
458
  const withAdditional = await stepAdditionalSources(merged);
372
- ctx.apply({ sources: withAdditional.length > 0 ? withAdditional : undefined });
459
+ ctx.apply({ additionalSources: withAdditional });
373
460
  },
374
461
  },
375
462
  {
@@ -431,7 +518,7 @@ export async function runSetupWizard(opts) {
431
518
  // API key values.
432
519
  const detection = await detectEnvironment({ existingStashDir: primaryBundlePath(current) });
433
520
  p.note(renderDetectionSummary(detection), "Detected environment");
434
- // Interactive entry point for `--reset-recommended`: offer to apply the
521
+ // Interactive entry point for `runResetRecommended`: offer to apply the
435
522
  // opinionated, detection-derived defaults and skip the step-by-step wizard.
436
523
  const useRecommended = await prompt(() => p.confirm({
437
524
  message: "Apply recommended defaults from the detected environment (merged into your existing config)?",
@@ -469,26 +556,25 @@ export async function runSetupWizard(opts) {
469
556
  // Step 2/2: Agent connection (for agentic features)
470
557
  const agentConfig = await stepAgentConnection(ctx.config, smallModelResult);
471
558
  ctx.apply(writeAgentEngines(ctx.config, agentConfig));
472
- // Registry-managed (lock-backed) bundles are preserved by finalizeSetupDraft.
473
559
  const newConfig = { ...ctx.config };
474
560
  const semanticSearchMode = outcome.semantic;
475
- const stashDir = newConfig.stashDir ?? primaryBundlePath(current) ?? getDefaultStashDir();
561
+ const stashDir = newConfig.primaryPath ?? primaryBundlePath(current) ?? getDefaultStashDir();
476
562
  const embedding = newConfig.embedding;
477
563
  const llm = readCurrentLlmEngine(newConfig);
478
564
  const registries = newConfig.registries;
479
- const allStashes = newConfig.sources ?? [];
565
+ const allStashes = newConfig.additionalSources ?? [];
480
566
  // Feature capability summary
481
567
  const agentConfigured = Boolean(agentConfig && !agentConfig.disabled);
482
568
  printCapabilitySummary(smallModelResult.skipped, agentConfigured);
483
569
  // Confirm before saving
484
570
  const effectiveRegistries = registries ?? DEFAULT_CONFIG.registries ?? [];
485
571
  p.note([
486
- `Stash directory: ${stashDir}`,
572
+ `Bundle directory: ${stashDir}`,
487
573
  `Embedding: ${embedding ? `${embedding.provider ?? "remote"} / ${embedding.model}` : "built-in local"}`,
488
574
  `LLM: ${llm ? `${llm.provider ?? "remote"} / ${llm.model}` : "disabled"}`,
489
575
  `Semantic search: ${semanticSearchMode.mode}`,
490
576
  `Registries: ${effectiveRegistries.filter((r) => r.enabled !== false).length} enabled`,
491
- `Stash sources: ${allStashes.length}`,
577
+ `Bundle sources: ${allStashes.length}`,
492
578
  `Agent default: ${newConfig.defaults?.engine ?? "disabled"}`,
493
579
  `Output: ${newConfig.output?.format ?? "json"} / ${newConfig.output?.detail ?? "brief"}`,
494
580
  ].join("\n"), "Configuration Summary");
@@ -502,19 +588,12 @@ export async function runSetupWizard(opts) {
502
588
  validateCompleteConfig(finalConfig);
503
589
  const { config: savedConfig } = await saveSetupConfig(current, finalConfig, async () => {
504
590
  if (!opts?.noInit)
505
- await akmInit({ dir: resolvedStashDir, setDefault: true, persistConfig: false });
591
+ await akmInit({ dir: stashDir, setDefault: true, persistConfig: false });
506
592
  });
507
- // Scheduled tasks are the wizard's only externally-visible side effect
508
- // (task files + OS scheduler entries). Run them ONLY now that the config is
509
- // confirmed and persisted, so cancelling at the final confirm above leaves
510
- // nothing behind. The task-setup path re-reads config via `loadConfig()`,
511
- // which now returns the just-saved config (the cache is invalidated on
512
- // write) — so tasks register against the confirmed engine/connection rather
513
- // than the stale pre-wizard config. This is interactive-only: `akm init` /
514
- // `--yes` go through the non-interactive entry points, which never reach
515
- // here (issue #512).
593
+ // After config persistence, the task step reviews the plan and asks one
594
+ // explicit confirmation before changing task files or scheduler state.
595
+ // Non-interactive setup paths never reach this interactive-only step.
516
596
  p.log.step("Scheduled Tasks");
517
- await stepDefaultImproveTasks();
518
597
  await stepScheduledTasks();
519
598
  if (semanticSearchMode.mode === "off") {
520
599
  clearSemanticStatus();
@@ -595,7 +674,8 @@ export async function runSetupWizard(opts) {
595
674
  p.log.info("Reminder: Set your LLM API key via the AKM_LLM_API_KEY environment variable.");
596
675
  }
597
676
  }
598
- p.outro(`Configuration saved to ${configPath}`);
677
+ p.outro(`Configuration saved to ${configPath}\n` +
678
+ 'Next: `akm bundle add <source>`, `akm index`, `akm search "<query>"`, `akm help agents`');
599
679
  }
600
680
  // ── Non-interactive / scripting entry points ─────────────────────────────────
601
681
  /**
@@ -605,14 +685,21 @@ export async function runSetupWizard(opts) {
605
685
  export async function runSetupWithDefaults(opts) {
606
686
  assertSetupConfigPreflight();
607
687
  const explicitStashDir = opts.dir != null ? path.resolve(opts.dir) : undefined;
688
+ // R-066 #4: a second `assertSetupSandbox(stashDir, explicitStashDir != null)`
689
+ // + `applyStashIsolationToEnv(stashDir, explicitStashDir != null)` pair used
690
+ // to follow `stashDir`'s resolution below. It was strictly redundant: unlike
691
+ // `runSetupFromConfig` (which can also derive an explicit stash dir from an
692
+ // incoming config file's bundle path, a case the early block below can't
693
+ // see yet), `runSetupWithDefaults` has only one source of an explicit dir —
694
+ // `opts.dir` — so `stashDir` always equals `explicitStashDir` whenever the
695
+ // `if` below ran, and always fails `dirExplicitlyProvided` (an immediate
696
+ // no-op in both helpers) whenever it didn't. Removed rather than duplicated.
608
697
  if (explicitStashDir) {
609
698
  assertSetupSandbox(explicitStashDir, true);
610
699
  applyStashIsolationToEnv(explicitStashDir, true);
611
700
  }
612
701
  const current = loadUserConfig();
613
702
  const stashDir = explicitStashDir ?? primaryBundlePath(current) ?? getDefaultStashDir();
614
- assertSetupSandbox(stashDir, explicitStashDir != null);
615
- applyStashIsolationToEnv(stashDir, explicitStashDir != null);
616
703
  // Run steps in non-interactive mode (applies defaults, skips prompts)
617
704
  const ctx = createSetupContext(current, { nonInteractive: true });
618
705
  const { steps } = buildSetupSteps({
@@ -621,11 +708,10 @@ export async function runSetupWithDefaults(opts) {
621
708
  preferredStashDir: stashDir,
622
709
  });
623
710
  await runSetupSteps(steps, ctx);
624
- // Ensure stashDir is set
625
- if (!ctx.config.stashDir)
626
- ctx.apply({ stashDir });
711
+ if (!ctx.config.primaryPath)
712
+ ctx.apply({ primaryPath: stashDir });
627
713
  // Aggregate environment detection — apply detected values directly.
628
- const env = await detectEnvironment({ existingStashDir: ctx.config.stashDir });
714
+ const env = await detectEnvironment({ existingStashDir: ctx.config.primaryPath });
629
715
  // Apply a detected LLM (live local server) when the config has none yet.
630
716
  if (!readCurrentLlmEngine(ctx.config) && opts.probe) {
631
717
  const liveLocal = env.localServers.find((s) => s.available && s.defaultModel);
@@ -677,7 +763,7 @@ export async function runSetupWithDefaults(opts) {
677
763
  });
678
764
  return {
679
765
  configPath: getConfigPath(),
680
- stashDir,
766
+ bundleDir: stashDir,
681
767
  stashCreated: initResult?.created ?? false,
682
768
  written: true,
683
769
  fields: Object.keys(finalConfig).filter((k) => finalConfig[k] !== undefined),
@@ -685,7 +771,7 @@ export async function runSetupWithDefaults(opts) {
685
771
  }
686
772
  /**
687
773
  * Run ONLY environment detection and return the typed result. Performs no
688
- * config writes and shows no prompts. Backs `akm setup --detect-only`.
774
+ * config writes and shows no prompts.
689
775
  *
690
776
  * SAFETY: The returned object carries env var NAMES only — never any API key
691
777
  * value.
@@ -699,9 +785,8 @@ export async function runDetectOnly() {
699
785
  * - Best harness → agent default (when a profile maps to it).
700
786
  * - Fastest live local model, else the first detected cloud key's provider.
701
787
  * - `nomic-embed-text` embeddings when a local LLM is live.
702
- * - improve task `0 2 * * *`, index task `0 4 * * *`.
703
788
  *
704
- * Returns a partial `AkmConfig`-shaped object plus a legacy `llm` block, ready
789
+ * Returns a partial `AkmConfig`-shaped object plus an LLM connection block, ready
705
790
  * to merge. Never includes an API key value.
706
791
  */
707
792
  export function deriveRecommendedConfig(env) {
@@ -736,14 +821,13 @@ export function deriveRecommendedConfig(env) {
736
821
  }
737
822
  }
738
823
  }
739
- result.taskSchedules = { improve: "0 2 * * *", index: "0 4 * * *" };
740
824
  return result;
741
825
  }
742
826
  /**
743
- * `akm setup --reset-recommended`: merge opinionated, detection-derived
744
- * defaults into the existing config WITHOUT removing pre-existing custom keys.
745
- * Uses the same merge path as {@link runSetupFromConfig} so custom keys survive
746
- * (follows #511 semantics).
827
+ * Merge opinionated, detection-derived defaults into the existing config
828
+ * WITHOUT removing pre-existing custom keys. Backs the interactive wizard's
829
+ * "apply recommended defaults" prompt. Uses the same merge path as
830
+ * {@link runSetupFromConfig} so custom keys survive (follows #511 semantics).
747
831
  */
748
832
  export async function runResetRecommended(opts) {
749
833
  assertSetupConfigPreflight();
@@ -787,9 +871,6 @@ export async function runResetRecommended(opts) {
787
871
  if (recommended.agentDefault) {
788
872
  incoming = deepMergeConfig(incoming, writeAgentEngines(incoming, { default: recommended.agentDefault }));
789
873
  }
790
- if (recommended.taskSchedules) {
791
- incoming.setup = { taskSchedules: recommended.taskSchedules };
792
- }
793
874
  return runSetupFromConfig({
794
875
  configJson: JSON.stringify(incoming),
795
876
  dir: opts.dir,
@@ -804,35 +885,36 @@ export async function runResetRecommended(opts) {
804
885
  export async function runSetupFromConfig(opts) {
805
886
  assertSetupConfigPreflight();
806
887
  // Phase 1: Parse JSON
807
- let incoming;
888
+ let parsed;
808
889
  try {
809
- incoming = JSON.parse(opts.configJson);
890
+ parsed = JSON.parse(opts.configJson);
810
891
  }
811
892
  catch (e) {
812
- throw new Error(`Invalid JSON in --config: ${e.message}`);
893
+ throw new UsageError(`Invalid JSON in --config: ${e.message}`, "INVALID_FLAG_VALUE");
894
+ }
895
+ if (!isPlainRecord(parsed)) {
896
+ throw new ConfigError("Setup config must contain a top-level object.", "INVALID_CONFIG_FILE");
813
897
  }
898
+ const incoming = parsed;
814
899
  // Phase 2: Validate — only allow safe top-level keys
815
- const ALLOWED_KEYS = new Set([
816
- "configVersion",
817
- "engines",
818
- "defaults",
819
- "improve",
820
- "modelAliases",
821
- "stashDir",
822
- "embedding",
823
- "semanticSearchMode",
824
- "output",
825
- "sources",
826
- // 0.9.0 (spec §10.1): the persisted source shape. Old-shape input files
827
- // (stashDir/sources) stay accepted and are normalized by finalizeSetupDraft
828
- // (Decision E); new-shape input files pass through their bundles directly.
829
- "bundles",
830
- "defaultBundle",
831
- "registries",
832
- "defaultWriteTarget",
833
- "defaults",
834
- "setup",
835
- ]);
900
+ for (const key of ["stashDir", "sources", "installed"]) {
901
+ if (key in incoming) {
902
+ throw new ConfigError(`${key} is not supported by the current config schema; run the standalone akm-migrate tool first.`, "INVALID_CONFIG_FILE");
903
+ }
904
+ }
905
+ // Derived from AkmConfigShape (via `listTopLevelConfigKeys`) rather than a
906
+ // hand-copied set: a hand-copied allowlist silently fell out of sync as the
907
+ // schema grew (R-017 — `index`, `search`, `feedback`, `archiveRetentionDays`,
908
+ // `workflow`, and `experimental` were all valid schema keys that this list
909
+ // forgot, each dropped with only a warning while the command exited 0).
910
+ // Every key the schema recognizes is a legitimate thing to set via
911
+ // `--config`/`--from`; there is no key a user can set here that they
912
+ // couldn't set by hand-editing config.json and letting `akm config set`
913
+ // validate it. Keys that remain genuinely retired (`profiles`, `llm`,
914
+ // `agent`, `features`, `stashes`, `bindings`, `writable`) are not part of
915
+ // the schema shape, so they still fall into the warn-and-drop branch below;
916
+ // `stashDir`/`sources`/`installed` get the more specific migration error above.
917
+ const ALLOWED_KEYS = new Set(listTopLevelConfigKeys());
836
918
  for (const key of Object.keys(incoming)) {
837
919
  if (!ALLOWED_KEYS.has(key)) {
838
920
  warn(`[akm setup] Ignoring unknown or restricted config key: "${key}"`);
@@ -847,21 +929,41 @@ export async function runSetupFromConfig(opts) {
847
929
  throw new Error("embedding.model is required when embedding is provided");
848
930
  }
849
931
  // Phase 3: Merge with existing config
850
- const incomingStashDir = incoming.stashDir;
851
- const explicitStashDir = opts.dir != null ? path.resolve(opts.dir) : incomingStashDir != null ? path.resolve(incomingStashDir) : undefined;
932
+ //
933
+ // R-066 #4 (verified, NOT reduced unlike the `runSetupWithDefaults` pair
934
+ // above, this one is NOT strictly redundant): the `assertSetupSandbox` +
935
+ // `applyStashIsolationToEnv` pair below looks like a duplicate of the pair
936
+ // here, but each guards a case the other cannot:
937
+ // - THIS early pair must run before `loadUserConfig()` so an explicit
938
+ // `--dir` pointed at a force-escaped transient sandbox isolates the
939
+ // config READ too, not just the later write (the isolation env var has
940
+ // to be set before `loadUserConfig()` executes, or `current` below is
941
+ // read from the host config instead of the sandboxed one — the exact
942
+ // failure class the "2026-05-23 setup-clobbers-user-config incident"
943
+ // comment on `assertSetupSandbox` describes).
944
+ // - The LATER pair below additionally covers a case this one can't see
945
+ // yet: an incoming `--config`/`--file` JSON blob that names its own
946
+ // bundle path (`incomingPrimaryPath`) with no `--dir` at all — that
947
+ // path is only known after the merge, so it cannot be checked early.
948
+ // When `explicitStashDir` is set both pairs do end up asserting the same
949
+ // (stashDir, true), but removing either one changes behavior in the case
950
+ // it uniquely covers, so both stay.
951
+ const explicitStashDir = opts.dir != null ? path.resolve(opts.dir) : undefined;
852
952
  if (explicitStashDir) {
853
953
  assertSetupSandbox(explicitStashDir, true);
854
954
  applyStashIsolationToEnv(explicitStashDir, true);
855
955
  }
856
956
  const current = loadUserConfig();
857
- const stashDir = explicitStashDir ?? primaryBundlePath(current) ?? getDefaultStashDir();
858
- const stashDirExplicit = explicitStashDir != null;
957
+ let merged = deepMergeConfig(current, incoming);
958
+ const incomingPrimaryPath = primaryBundlePath(incoming);
959
+ const configuredPrimaryPath = primaryBundlePath(merged);
960
+ const stashDir = explicitStashDir ?? configuredPrimaryPath ?? getDefaultStashDir();
961
+ const stashDirExplicit = explicitStashDir != null || incomingPrimaryPath !== undefined;
859
962
  assertSetupSandbox(stashDir, stashDirExplicit);
860
963
  applyStashIsolationToEnv(stashDir, stashDirExplicit);
861
- let merged = deepMergeConfig(current, {
862
- ...incoming,
863
- stashDir,
864
- });
964
+ if (explicitStashDir !== undefined || configuredPrimaryPath === undefined) {
965
+ merged = deepMergeConfig(merged, { primaryPath: stashDir });
966
+ }
865
967
  // Deep-merge canonical keys: nested objects merge key-by-key so a
866
968
  // partial `--file` only updates the keys it carries and never drops sibling
867
969
  // subkeys (e.g. output.detail survives an output.format-only file). Arrays
@@ -878,8 +980,8 @@ export async function runSetupFromConfig(opts) {
878
980
  preferredStashDir: stashDir,
879
981
  });
880
982
  await runSetupSteps(steps, ctx);
881
- if (!ctx.config.stashDir)
882
- ctx.apply({ stashDir });
983
+ if (!ctx.config.primaryPath)
984
+ ctx.apply({ primaryPath: stashDir });
883
985
  if (!ctx.config.defaults?.engine) {
884
986
  const detected = detectAgentCliProfiles(undefined);
885
987
  const defaultProfile = pickDefaultAgentProfile(detected, undefined);
@@ -889,9 +991,7 @@ export async function runSetupFromConfig(opts) {
889
991
  }
890
992
  merged = ctx.config;
891
993
  }
892
- // Fold the flat scratch model (stashDir + sources) into the persisted 0.9.0
893
- // bundles shape (Decision E) so old-shape and new-shape input files both land
894
- // as bundles; nothing half-migrated is ever validated or written.
994
+ // Convert the private setup draft to the persisted bundle shape.
895
995
  let finalizedMerged = finalizeSetupDraft(merged);
896
996
  // Reject an invalid merged engine graph before probing or touching the stash.
897
997
  validateCompleteConfig(finalizedMerged);
@@ -922,7 +1022,7 @@ export async function runSetupFromConfig(opts) {
922
1022
  });
923
1023
  return {
924
1024
  configPath: getConfigPath(),
925
- stashDir,
1025
+ bundleDir: stashDir,
926
1026
  stashCreated: initResult?.created ?? false,
927
1027
  written: true,
928
1028
  fields: Object.keys(incoming).filter((k) => incoming[k] !== undefined),