akm-cli 0.9.0-rc.1 → 0.9.0-rc.13

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 (587) hide show
  1. package/CHANGELOG.md +1190 -52
  2. package/README.md +62 -37
  3. package/SECURITY.md +46 -31
  4. package/dist/akm +162 -38
  5. package/dist/akm-migrate +44 -0
  6. package/dist/assets/backends/schtasks-template.xml +2 -1
  7. package/dist/assets/hints/cli-hints-full.md +268 -118
  8. package/dist/assets/hints/cli-hints-short.md +87 -24
  9. package/dist/assets/{profiles → improve-strategies}/catchup.json +3 -1
  10. package/dist/assets/{profiles → improve-strategies}/consolidate.json +3 -1
  11. package/dist/assets/{profiles → improve-strategies}/default.json +6 -7
  12. package/dist/assets/improve-strategies/frequent.json +15 -0
  13. package/dist/assets/{profiles → improve-strategies}/graph-refresh.json +4 -2
  14. package/dist/assets/{profiles → improve-strategies}/memory-focus.json +4 -1
  15. package/dist/assets/{profiles → improve-strategies}/proactive-maintenance.json +5 -5
  16. package/dist/assets/{profiles → improve-strategies}/quick.json +4 -2
  17. package/dist/assets/improve-strategies/reflect-distill.json +30 -0
  18. package/dist/assets/{profiles → improve-strategies}/thorough.json +1 -1
  19. package/dist/assets/prompts/consolidate-system.md +5 -5
  20. package/dist/assets/prompts/extract-session.md +2 -6
  21. package/dist/assets/prompts/memory-infer-user.md +2 -3
  22. package/dist/assets/prompts/reflect-llm-framed-contract.md +11 -0
  23. package/dist/assets/prompts/reflect-llm-schema-contract.md +3 -0
  24. package/dist/assets/prompts/reflect-output-repair.md +3 -0
  25. package/dist/assets/stash-skeleton/README.md +38 -10
  26. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +8 -0
  27. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +8 -0
  28. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +14 -1
  29. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +13 -1
  30. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +9 -1
  31. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +11 -0
  32. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +9 -0
  33. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +9 -0
  34. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +8 -0
  35. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +100 -0
  36. package/dist/assets/stash-skeleton/facts/conventions/domains.md +64 -0
  37. package/dist/assets/stash-skeleton/facts/conventions/organization.md +136 -0
  38. package/dist/assets/tasks/core/extract.yml +3 -2
  39. package/dist/assets/tasks/core/improve.yml +2 -1
  40. package/dist/assets/tasks/core/index-refresh.yml +1 -0
  41. package/dist/assets/tasks/core/sync.yml +1 -0
  42. package/dist/assets/tasks/core/version-check.yml +2 -1
  43. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +5 -0
  44. package/dist/assets/tasks/improve/akm-improve-catchup.yml +8 -0
  45. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +5 -0
  46. package/dist/assets/tasks/improve/akm-improve-frequent.yml +5 -0
  47. package/dist/assets/tasks/improve/akm-improve-nightly.yml +5 -0
  48. package/dist/assets/templates/html/health.html +5 -4
  49. package/dist/assets/workflows/workflow-template.md +31 -15
  50. package/dist/cli/invocation.js +279 -0
  51. package/dist/cli/parse-args.js +5 -90
  52. package/dist/cli/retired-commands.js +78 -0
  53. package/dist/cli/shared.js +158 -48
  54. package/dist/cli-node.mjs +2 -1
  55. package/dist/cli.js +747 -293
  56. package/dist/commands/agent/agent-dispatch.js +19 -18
  57. package/dist/commands/agent/agent-support.js +0 -24
  58. package/dist/commands/agent/contribute-cli.js +43 -97
  59. package/dist/commands/completions.js +80 -23
  60. package/dist/commands/config-cli.js +44 -281
  61. package/dist/commands/env/env-binding.js +13 -9
  62. package/dist/commands/env/env-cli.js +76 -159
  63. package/dist/commands/env/env.js +12 -163
  64. package/dist/commands/env/marker-path.js +6 -0
  65. package/dist/commands/env/secret-cli.js +45 -61
  66. package/dist/commands/env/secret.js +32 -62
  67. package/dist/commands/feedback-cli.js +179 -85
  68. package/dist/commands/health/accept-rate.js +58 -0
  69. package/dist/commands/health/advisories.js +7 -8
  70. package/dist/commands/health/checks.js +279 -94
  71. package/dist/commands/health/html-report.js +197 -578
  72. package/dist/commands/health/improve-metrics.js +277 -246
  73. package/dist/commands/health/llm-usage.js +19 -19
  74. package/dist/commands/health/md-report.js +16 -7
  75. package/dist/commands/health/metrics.js +67 -32
  76. package/dist/commands/health/renderers.js +47 -0
  77. package/dist/commands/health/report-view-model.js +508 -0
  78. package/dist/commands/health/stash-exposure.js +1 -1
  79. package/dist/commands/health/surfaces.js +16 -56
  80. package/dist/commands/health/task-runs.js +3 -67
  81. package/dist/{migrate-storage-node.mjs → commands/health/types-checks.js} +1 -5
  82. package/dist/commands/health/types-improve.js +29 -0
  83. package/dist/{output/text/save.js → commands/health/types-metrics.js} +1 -2
  84. package/dist/commands/health/types-result.js +7 -0
  85. package/dist/commands/health/types-runs.js +4 -0
  86. package/dist/commands/health/types-session-log.js +4 -0
  87. package/dist/commands/health/types-windows.js +4 -0
  88. package/dist/commands/health/types.js +26 -21
  89. package/dist/commands/health/windows.js +2 -3
  90. package/dist/commands/health.js +296 -167
  91. package/dist/commands/improve/anti-collapse.js +5 -5
  92. package/dist/commands/improve/autonomy-gate.js +68 -0
  93. package/dist/commands/improve/collapse-detector.js +65 -52
  94. package/dist/commands/improve/consolidate/chunking.js +9 -7
  95. package/dist/commands/improve/consolidate/eligibility.js +1 -23
  96. package/dist/commands/improve/consolidate/merge.js +4 -0
  97. package/dist/commands/improve/consolidate.js +454 -1354
  98. package/dist/commands/improve/content-hash.js +39 -0
  99. package/dist/commands/improve/distill/content-repair.js +4 -10
  100. package/dist/commands/improve/distill/promote-memory.js +89 -64
  101. package/dist/commands/improve/distill/quality-gate.js +118 -42
  102. package/dist/commands/improve/distill-guards.js +1 -1
  103. package/dist/commands/improve/distill-promotion-policy.js +33 -888
  104. package/dist/commands/improve/distill.js +607 -363
  105. package/dist/commands/improve/eligibility.js +165 -79
  106. package/dist/commands/improve/extract-cli.js +35 -126
  107. package/dist/commands/improve/extract-prompt.js +6 -35
  108. package/dist/commands/improve/extract.js +640 -391
  109. package/dist/commands/improve/feedback-valence.js +2 -12
  110. package/dist/commands/improve/improve-cli.js +134 -135
  111. package/dist/commands/improve/improve-result-file.js +30 -50
  112. package/dist/commands/improve/improve-run-types.js +4 -0
  113. package/dist/commands/improve/improve-strategies.js +135 -0
  114. package/dist/commands/improve/improve.js +904 -701
  115. package/dist/commands/improve/locks.js +64 -111
  116. package/dist/commands/improve/loop-stages.js +1110 -923
  117. package/dist/commands/improve/memory/derived-ref.js +124 -0
  118. package/dist/commands/improve/memory/memory-belief.js +79 -7
  119. package/dist/commands/improve/memory/memory-contradiction-detect.js +49 -52
  120. package/dist/commands/improve/memory/memory-improve.js +25 -37
  121. package/dist/commands/improve/outcome-loop.js +25 -88
  122. package/dist/commands/improve/preparation.js +1034 -813
  123. package/dist/commands/improve/proactive-maintenance.js +34 -9
  124. package/dist/commands/improve/proposal-envelope.js +31 -0
  125. package/dist/commands/improve/reflect.js +983 -794
  126. package/dist/commands/improve/run-context.js +119 -0
  127. package/dist/commands/improve/salience.js +24 -127
  128. package/dist/commands/improve/session-asset.js +7 -3
  129. package/dist/commands/improve/shared.js +14 -34
  130. package/dist/commands/improve/source-identity.js +28 -0
  131. package/dist/commands/improve/triage.js +20 -17
  132. package/dist/commands/lint/base-linter.js +340 -313
  133. package/dist/commands/lint/env-key-rules.js +31 -47
  134. package/dist/commands/lint/index.js +185 -30
  135. package/dist/commands/{events.js → log.js} +28 -38
  136. package/dist/commands/migrate-cli.js +54 -0
  137. package/dist/commands/migration-tool.js +55 -0
  138. package/dist/commands/observability-cli.js +70 -208
  139. package/dist/commands/proposal/diff-format.js +50 -0
  140. package/dist/commands/proposal/drain-policies.js +0 -6
  141. package/dist/commands/proposal/drain.js +91 -40
  142. package/dist/commands/proposal/proposal-cli.js +134 -132
  143. package/dist/commands/proposal/proposal-types.js +56 -0
  144. package/dist/commands/proposal/proposal.js +83 -65
  145. package/dist/commands/proposal/propose-cli.js +88 -0
  146. package/dist/commands/proposal/propose.js +105 -88
  147. package/dist/commands/proposal/repository.js +1303 -278
  148. package/dist/commands/proposal/validators/proposal-quality-validators.js +16 -6
  149. package/dist/commands/proposal/validators/proposal-validators.js +61 -12
  150. package/dist/commands/proposal/validators/proposals.js +6 -8
  151. package/dist/commands/read/curate.js +78 -73
  152. package/dist/commands/read/knowledge.js +510 -13
  153. package/dist/commands/read/registry-search.js +2 -2
  154. package/dist/commands/read/remember-cli.js +84 -15
  155. package/dist/commands/read/search-cli.js +203 -96
  156. package/dist/commands/read/search.js +126 -94
  157. package/dist/commands/read/show.js +226 -250
  158. package/dist/commands/registry-cli.js +34 -60
  159. package/dist/commands/remember.js +18 -57
  160. package/dist/commands/sources/add-cli.js +104 -49
  161. package/dist/commands/sources/bundle-cli.js +166 -0
  162. package/dist/commands/sources/bundle-config-ops.js +63 -0
  163. package/dist/commands/sources/info.js +27 -15
  164. package/dist/commands/sources/init.js +30 -40
  165. package/dist/commands/sources/installed-stashes.js +469 -172
  166. package/dist/commands/sources/schema-repair.js +10 -9
  167. package/dist/commands/sources/self-update.js +182 -121
  168. package/dist/commands/sources/source-add.js +169 -178
  169. package/dist/commands/sources/source-clone.js +144 -41
  170. package/dist/commands/sources/source-manage.js +94 -59
  171. package/dist/commands/sources/sources-cli.js +64 -205
  172. package/dist/commands/sources/stash-cli.js +91 -54
  173. package/dist/commands/sources/stash-skeleton.js +1 -1
  174. package/dist/commands/tasks/tasks-cli.js +106 -104
  175. package/dist/commands/tasks/tasks.js +445 -262
  176. package/dist/commands/workflow-cli.js +75 -228
  177. package/dist/core/action-contributors.js +1 -1
  178. package/dist/core/activation-policy.js +49 -0
  179. package/dist/core/adapter/adapters/agent-skills-adapter.js +181 -0
  180. package/dist/core/adapter/adapters/akm-adapter.js +528 -0
  181. package/dist/core/adapter/adapters/akm-lint.js +392 -0
  182. package/dist/core/adapter/adapters/akm-metadata.js +387 -0
  183. package/dist/core/adapter/adapters/akm-task-adapter.js +149 -0
  184. package/dist/core/adapter/adapters/akm-workflow-adapter.js +180 -0
  185. package/dist/core/adapter/adapters/claude-adapter.js +61 -0
  186. package/dist/core/adapter/adapters/dotenv-adapter.js +187 -0
  187. package/dist/core/adapter/adapters/generic-files-adapter.js +119 -0
  188. package/dist/core/adapter/adapters/index.js +80 -0
  189. package/dist/core/adapter/adapters/llm-wiki-adapter.js +419 -0
  190. package/dist/core/adapter/adapters/okf-adapter.js +391 -0
  191. package/dist/core/adapter/adapters/opencode-adapter.js +68 -0
  192. package/dist/core/adapter/adapters/shared.js +286 -0
  193. package/dist/core/adapter/adapters/tool-dir-shared.js +217 -0
  194. package/dist/core/adapter/adapters/website-snapshot-adapter.js +155 -0
  195. package/dist/core/adapter/bundle-adapter.js +4 -0
  196. package/dist/core/adapter/detect-adapter.js +17 -0
  197. package/dist/core/adapter/recognize-match.js +44 -0
  198. package/dist/core/adapter/registry.js +56 -0
  199. package/dist/core/adapter/types.js +4 -0
  200. package/dist/core/asset/akm-markdown.js +30 -0
  201. package/dist/core/asset/asset-placement.js +243 -0
  202. package/dist/core/asset/asset-ref.js +110 -79
  203. package/dist/core/asset/asset-serialize.js +20 -0
  204. package/dist/core/asset/frontmatter.js +28 -12
  205. package/dist/core/asset/markdown.js +40 -51
  206. package/dist/core/asset/resolve-ref.js +274 -0
  207. package/dist/core/asset/stash-meta.js +2 -2
  208. package/dist/core/bundle-id.js +51 -0
  209. package/dist/core/common.js +281 -86
  210. package/dist/core/config/config-io.js +42 -128
  211. package/dist/core/config/config-schema.js +233 -855
  212. package/dist/core/config/config-sources.js +162 -39
  213. package/dist/core/config/config-types.js +16 -11
  214. package/dist/core/config/config-version.js +29 -0
  215. package/dist/core/config/config-walker.js +126 -37
  216. package/dist/core/config/config.js +154 -331
  217. package/dist/core/config/deep-merge.js +41 -0
  218. package/dist/core/config/engine-semantics.js +28 -0
  219. package/dist/core/config/experimental.js +21 -0
  220. package/dist/core/config/schema/embedding.js +38 -0
  221. package/dist/core/config/schema/engines.js +116 -0
  222. package/dist/core/config/schema/experimental.js +47 -0
  223. package/dist/core/config/schema/feedback.js +31 -0
  224. package/dist/core/config/schema/improve-processes.js +389 -0
  225. package/dist/core/config/schema/improve.js +94 -0
  226. package/dist/core/config/schema/index-config.js +176 -0
  227. package/dist/core/config/schema/output.js +18 -0
  228. package/dist/core/config/schema/primitives.js +94 -0
  229. package/dist/core/config/schema/search.js +30 -0
  230. package/dist/core/config/schema/setup.js +18 -0
  231. package/dist/core/config/schema/sources-bundles.js +169 -0
  232. package/dist/core/config/schema/workflow.js +29 -0
  233. package/dist/core/env-secret-ref.js +155 -20
  234. package/dist/core/errors.js +17 -15
  235. package/dist/core/events-types.js +4 -0
  236. package/dist/core/events.js +46 -128
  237. package/dist/core/extra-params.js +62 -0
  238. package/dist/core/file-change.js +17 -0
  239. package/dist/core/file-lock.js +202 -57
  240. package/dist/core/fs-txn.js +392 -0
  241. package/dist/core/git-message.js +59 -0
  242. package/dist/core/improve-result.js +167 -0
  243. package/dist/core/lesson-lint.js +1 -17
  244. package/dist/core/logs-db.js +1 -1
  245. package/dist/core/maintenance-barrier.js +135 -0
  246. package/dist/core/migration-operation.js +44 -0
  247. package/dist/core/mutation-target.js +78 -0
  248. package/dist/core/paths.js +22 -25
  249. package/dist/core/platform.js +10 -0
  250. package/dist/core/recognition-util.js +128 -0
  251. package/dist/core/redaction.js +392 -0
  252. package/dist/core/standards/resolve-standards-context.js +36 -65
  253. package/dist/core/standards/resolve-stash-standards.js +2 -2
  254. package/dist/core/standards/resolve-type-conventions.js +5 -5
  255. package/dist/core/state/migrations.js +242 -11
  256. package/dist/core/state-db.js +98 -10
  257. package/dist/core/structured.js +1 -1
  258. package/dist/core/subprocess.js +303 -0
  259. package/dist/core/text-truncation.js +9 -5
  260. package/dist/core/time.js +20 -0
  261. package/dist/core/type-presentation.js +130 -0
  262. package/dist/core/warn.js +0 -3
  263. package/dist/core/write-source.js +834 -118
  264. package/dist/indexer/bundle-identity-guard.js +92 -0
  265. package/dist/indexer/db/graph-db.js +1 -25
  266. package/dist/indexer/db/llm-cache.js +1 -1
  267. package/dist/indexer/ensure-index.js +30 -9
  268. package/dist/indexer/graph/graph-boost.js +9 -30
  269. package/dist/indexer/graph/graph-extraction.js +41 -27
  270. package/dist/indexer/graph/graph-types.js +4 -0
  271. package/dist/indexer/index-writer-lock.js +93 -49
  272. package/dist/indexer/index-written-assets.js +100 -53
  273. package/dist/indexer/indexer.js +746 -329
  274. package/dist/indexer/init.js +18 -25
  275. package/dist/indexer/installations.js +142 -0
  276. package/dist/indexer/passes/dir-staleness.js +18 -10
  277. package/dist/indexer/passes/memory-inference.js +25 -15
  278. package/dist/indexer/passes/metadata.js +412 -243
  279. package/dist/indexer/scan/doc-to-entry.js +160 -0
  280. package/dist/indexer/scan/drain-dir.js +134 -0
  281. package/dist/indexer/search/db-search.js +292 -108
  282. package/dist/indexer/search/fts-query.js +64 -0
  283. package/dist/indexer/search/ranking-contributors.js +145 -25
  284. package/dist/indexer/search/ranking-types.js +4 -0
  285. package/dist/indexer/search/ranking.js +28 -71
  286. package/dist/indexer/search/search-attribution.js +67 -0
  287. package/dist/indexer/search/search-fields.js +18 -3
  288. package/dist/indexer/search/search-hit-enrichers.js +30 -40
  289. package/dist/indexer/search/search-source.js +157 -111
  290. package/dist/indexer/search/semantic-status.js +4 -1
  291. package/dist/indexer/usage/usage-events.js +10 -30
  292. package/dist/indexer/walk/file-context.js +3 -45
  293. package/dist/indexer/walk/matchers.js +42 -73
  294. package/dist/indexer/walk/path-resolver.js +11 -5
  295. package/dist/indexer/walk/walker.js +42 -14
  296. package/dist/integrations/agent/builder-shared.js +7 -0
  297. package/dist/integrations/agent/builders.js +5 -58
  298. package/dist/integrations/agent/config.js +3 -143
  299. package/dist/integrations/agent/detect.js +17 -2
  300. package/dist/integrations/agent/engine-resolution.js +231 -0
  301. package/dist/integrations/agent/index.js +1 -2
  302. package/dist/integrations/agent/model-aliases.js +8 -3
  303. package/dist/integrations/agent/profiles.js +6 -99
  304. package/dist/integrations/agent/prompts.js +46 -18
  305. package/dist/integrations/agent/runner-dispatch.js +78 -13
  306. package/dist/integrations/agent/runner.js +76 -208
  307. package/dist/integrations/agent/spawn.js +48 -279
  308. package/dist/integrations/harnesses/aider/agent-builder.js +9 -8
  309. package/dist/integrations/harnesses/aider/index.js +2 -12
  310. package/dist/integrations/harnesses/amazonq/agent-builder.js +10 -16
  311. package/dist/integrations/harnesses/amazonq/index.js +3 -17
  312. package/dist/integrations/harnesses/claude/agent-builder.js +2 -3
  313. package/dist/integrations/harnesses/claude/config-import.js +1 -3
  314. package/dist/integrations/harnesses/claude/index.js +1 -14
  315. package/dist/integrations/harnesses/claude/session-log.js +27 -75
  316. package/dist/integrations/harnesses/codex/agent-builder.js +8 -7
  317. package/dist/integrations/harnesses/codex/index.js +2 -13
  318. package/dist/integrations/harnesses/copilot/agent-builder.js +9 -9
  319. package/dist/integrations/harnesses/copilot/index.js +1 -13
  320. package/dist/integrations/harnesses/gemini/agent-builder.js +8 -9
  321. package/dist/integrations/harnesses/gemini/index.js +1 -13
  322. package/dist/integrations/harnesses/ids.js +24 -0
  323. package/dist/integrations/harnesses/index.js +31 -33
  324. package/dist/integrations/harnesses/opencode/agent-builder.js +23 -5
  325. package/dist/integrations/harnesses/opencode/config-import.js +1 -3
  326. package/dist/integrations/harnesses/opencode/index.js +1 -18
  327. package/dist/integrations/harnesses/opencode/session-log.js +67 -125
  328. package/dist/integrations/harnesses/opencode-sdk/harness.js +3 -17
  329. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +274 -191
  330. package/dist/integrations/harnesses/openhands/agent-builder.js +12 -10
  331. package/dist/integrations/harnesses/openhands/index.js +2 -12
  332. package/dist/integrations/harnesses/pi/agent-builder.js +11 -18
  333. package/dist/integrations/harnesses/pi/index.js +3 -16
  334. package/dist/integrations/harnesses/shared.js +17 -0
  335. package/dist/integrations/harnesses/types.js +38 -34
  336. package/dist/integrations/lockfile.js +211 -24
  337. package/dist/integrations/session-logs/index.js +24 -40
  338. package/dist/integrations/session-logs/provider-base.js +113 -0
  339. package/dist/llm/client.js +182 -110
  340. package/dist/llm/embedders/deterministic.js +2 -2
  341. package/dist/llm/embedders/remote.js +21 -9
  342. package/dist/llm/feature-gate.js +17 -57
  343. package/dist/llm/graph-extract.js +12 -13
  344. package/dist/llm/index-passes.js +8 -42
  345. package/dist/llm/memory-infer.js +144 -1
  346. package/dist/llm/metadata-enhance.js +45 -30
  347. package/dist/llm/structured-call.js +16 -8
  348. package/dist/llm/usage-persist.js +30 -5
  349. package/dist/llm/usage-telemetry.js +59 -6
  350. package/dist/output/cli-hints.js +1 -2
  351. package/dist/output/command-registry.js +27 -0
  352. package/dist/output/context.js +22 -7
  353. package/dist/output/format-exempt.js +80 -0
  354. package/dist/output/generic-render.js +251 -0
  355. package/dist/output/html-render.js +11 -16
  356. package/dist/output/render-registry.js +57 -0
  357. package/dist/output/renderers.js +15 -281
  358. package/dist/output/shapes/curate.js +10 -1
  359. package/dist/output/shapes/events.js +12 -7
  360. package/dist/output/shapes/helpers.js +58 -84
  361. package/dist/output/shapes/passthrough.js +8 -40
  362. package/dist/output/shapes/proposal/producer.js +15 -7
  363. package/dist/output/shapes/registry.js +12 -6
  364. package/dist/output/shapes.js +0 -9
  365. package/dist/output/text/{init.js → bundle-create.js} +3 -1
  366. package/dist/output/text/bundle-show.js +7 -0
  367. package/dist/output/text/command-format.js +562 -0
  368. package/dist/output/text/env.js +1 -3
  369. package/dist/output/text/events.js +8 -7
  370. package/dist/output/text/helpers.js +15 -1375
  371. package/dist/output/text/proposal/producer.js +4 -2
  372. package/dist/output/text/proposal-format.js +202 -0
  373. package/dist/output/text/registry-commands.js +1 -2
  374. package/dist/output/text/registry.js +12 -6
  375. package/dist/output/text/show-directives.js +117 -0
  376. package/dist/output/text/show-format.js +103 -0
  377. package/dist/output/text/sync.js +5 -0
  378. package/dist/output/text/workflow-format.js +332 -0
  379. package/dist/output/text/workflow.js +1 -2
  380. package/dist/output/text.js +10 -19
  381. package/dist/registry/factory.js +4 -6
  382. package/dist/registry/origin-resolve.js +16 -27
  383. package/dist/registry/providers/skills-sh.js +3 -3
  384. package/dist/registry/providers/static-index.js +15 -25
  385. package/dist/registry/resolve.js +43 -94
  386. package/dist/registry/semver.js +43 -0
  387. package/dist/runtime.js +81 -12
  388. package/dist/scripts/akm-migrate.js +35529 -0
  389. package/dist/setup/detect.js +5 -7
  390. package/dist/setup/detected-engines.js +136 -0
  391. package/dist/setup/engine-config.js +100 -0
  392. package/dist/setup/registry-stash-loader.js +3 -3
  393. package/dist/setup/semantic-assets.js +12 -9
  394. package/dist/setup/setup.js +444 -208
  395. package/dist/setup/steps/connection-shared.js +120 -0
  396. package/dist/setup/steps/connection.js +108 -305
  397. package/dist/setup/steps/platforms.js +13 -12
  398. package/dist/setup/steps/semantic.js +15 -3
  399. package/dist/setup/steps/sources.js +21 -15
  400. package/dist/setup/steps/stashdir.js +6 -4
  401. package/dist/setup/steps/tasks.js +236 -119
  402. package/dist/setup/steps.js +3 -2
  403. package/dist/sources/freshness.js +39 -0
  404. package/dist/sources/provider-factory.js +11 -17
  405. package/dist/sources/providers/filesystem.js +2 -3
  406. package/dist/sources/providers/git-install.js +278 -34
  407. package/dist/sources/providers/git-provider.js +54 -56
  408. package/dist/sources/providers/git-stash.js +420 -91
  409. package/dist/sources/providers/git.js +2 -2
  410. package/dist/sources/providers/npm.js +16 -19
  411. package/dist/sources/providers/provider-utils.js +47 -22
  412. package/dist/sources/providers/sync-from-ref.js +3 -9
  413. package/dist/sources/providers/website.js +2 -2
  414. package/dist/sources/resolve.js +11 -10
  415. package/dist/sources/snapshot-fetchers/types.js +4 -0
  416. package/dist/sources/{website-ingest.js → snapshot-fetchers/website-ingest.js} +110 -41
  417. package/dist/storage/database.js +60 -4
  418. package/dist/storage/engines/sqlite-migrations.js +156 -5
  419. package/dist/storage/locations.js +1 -2
  420. package/dist/storage/repositories/canaries-repository.js +1 -1
  421. package/dist/storage/repositories/events-repository.js +51 -11
  422. package/dist/storage/repositories/improve-runs-repository.js +6 -32
  423. package/dist/storage/repositories/index-connection.js +79 -0
  424. package/dist/storage/repositories/index-db.js +4 -3
  425. package/dist/storage/repositories/index-entries-repository.js +863 -0
  426. package/dist/{indexer/db/entry-mapper.js → storage/repositories/index-entry-mapper.js} +19 -2
  427. package/dist/storage/repositories/index-entry-types.js +4 -0
  428. package/dist/storage/repositories/index-fts-repository.js +167 -0
  429. package/dist/storage/repositories/index-llm-cache-repository.js +108 -0
  430. package/dist/storage/repositories/index-meta-repository.js +49 -0
  431. package/dist/{indexer/db/schema.js → storage/repositories/index-schema.js} +226 -100
  432. package/dist/storage/repositories/index-sql.js +12 -0
  433. package/dist/storage/repositories/index-utility-repository.js +356 -0
  434. package/dist/storage/repositories/index-vec-repository.js +250 -0
  435. package/dist/storage/repositories/outcome-repository.js +119 -0
  436. package/dist/storage/repositories/proposals-repository.js +317 -75
  437. package/dist/storage/repositories/registry-cache.js +1 -1
  438. package/dist/storage/repositories/salience-repository.js +172 -0
  439. package/dist/storage/repositories/task-history-repository.js +110 -3
  440. package/dist/storage/repositories/workflow-runs-repository.js +68 -35
  441. package/dist/tasks/backends/cron.js +169 -46
  442. package/dist/tasks/backends/exec-utils.js +76 -3
  443. package/dist/tasks/backends/index.js +6 -9
  444. package/dist/tasks/backends/launchd.js +292 -55
  445. package/dist/tasks/backends/schtasks.js +557 -70
  446. package/dist/tasks/backends/types.js +4 -0
  447. package/dist/tasks/command-executable.js +93 -0
  448. package/dist/tasks/embedded.js +56 -38
  449. package/dist/tasks/parser.js +156 -64
  450. package/dist/tasks/resolve-akm-bin.js +144 -51
  451. package/dist/tasks/runner.js +377 -209
  452. package/dist/tasks/schedule.js +108 -19
  453. package/dist/tasks/scheduler-invocation.js +296 -0
  454. package/dist/tasks/schema.js +1 -1
  455. package/dist/tasks/task-id.js +35 -0
  456. package/dist/tasks/validator.js +30 -16
  457. package/dist/workflows/authoring/authoring.js +96 -148
  458. package/dist/workflows/authoring/scope-key.js +1 -1
  459. package/dist/workflows/cli.js +0 -20
  460. package/dist/workflows/concurrency-policy.js +15 -0
  461. package/dist/workflows/exec/brief.js +25 -59
  462. package/dist/workflows/exec/frozen-judge.js +47 -0
  463. package/dist/workflows/exec/native-executor.js +157 -94
  464. package/dist/workflows/exec/report.js +365 -200
  465. package/dist/workflows/exec/run-workflow.js +46 -40
  466. package/dist/workflows/exec/scheduler.js +12 -41
  467. package/dist/workflows/exec/step-work.js +239 -205
  468. package/dist/workflows/exec/workflow-engine-gate.js +67 -0
  469. package/dist/workflows/ir/compile.js +141 -283
  470. package/dist/workflows/ir/freeze.js +233 -0
  471. package/dist/workflows/ir/plan-hash.js +40 -5
  472. package/dist/workflows/ir/schema.js +537 -1
  473. package/dist/workflows/parser.js +878 -306
  474. package/dist/workflows/program/expressions.js +20 -208
  475. package/dist/workflows/program/schema.js +7 -10
  476. package/dist/workflows/renderer.js +99 -121
  477. package/dist/workflows/resource-limits.js +22 -0
  478. package/dist/workflows/runtime/checkin.js +1 -1
  479. package/dist/workflows/runtime/plan-classifier.js +131 -0
  480. package/dist/workflows/runtime/runs.js +200 -113
  481. package/dist/workflows/runtime/unit-checkin.js +1 -1
  482. package/dist/workflows/runtime/unit-phases.js +20 -0
  483. package/dist/workflows/runtime/workflow-asset-loader.js +235 -97
  484. package/dist/workflows/schema.js +1 -11
  485. package/dist/workflows/validate-summary.js +4 -26
  486. package/dist/workflows/validator.js +52 -30
  487. package/docs/README.md +42 -78
  488. package/docs/migration/README.md +8 -0
  489. package/docs/migration/release-notes/0.6.0.md +1 -1
  490. package/docs/migration/release-notes/0.7.0.md +9 -8
  491. package/docs/migration/release-notes/0.9.0.md +158 -14
  492. package/docs/migration/v0.7-to-v0.8.md +46 -47
  493. package/docs/migration/v0.8-to-v0.9.md +844 -0
  494. package/docs/reference/README.md +12 -0
  495. package/docs/reference/data-and-telemetry.md +333 -0
  496. package/package.json +21 -17
  497. package/schemas/akm-asset-envelope.json +93 -0
  498. package/schemas/akm-config.json +4636 -0
  499. package/schemas/akm-task.json +87 -0
  500. package/{dist/schemas → schemas}/akm-workflow.json +127 -82
  501. package/dist/akm-migrate-storage +0 -38
  502. package/dist/assets/help/help-accept.md +0 -12
  503. package/dist/assets/help/help-improve.md +0 -84
  504. package/dist/assets/help/help-proposals.md +0 -17
  505. package/dist/assets/help/help-propose.md +0 -17
  506. package/dist/assets/help/help-reject.md +0 -11
  507. package/dist/assets/profiles/frequent.json +0 -13
  508. package/dist/assets/profiles/recombine-only.json +0 -21
  509. package/dist/assets/profiles/reflect-distill.json +0 -30
  510. package/dist/assets/profiles/synthesize.json +0 -15
  511. package/dist/assets/prompts/procedural-system.md +0 -44
  512. package/dist/assets/prompts/recombine-system.md +0 -40
  513. package/dist/assets/prompts/staleness-detect-system.md +0 -6
  514. package/dist/assets/tasks/core/backup.yml +0 -4
  515. package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
  516. package/dist/assets/templates/html/default.html +0 -78
  517. package/dist/assets/templates/html/vendor/echarts.min.js +0 -45
  518. package/dist/assets/wiki/index-template.md +0 -12
  519. package/dist/assets/wiki/ingest-workflow-template.md +0 -83
  520. package/dist/assets/wiki/log-template.md +0 -8
  521. package/dist/assets/wiki/schema-template.md +0 -61
  522. package/dist/cli/config-migrate.js +0 -150
  523. package/dist/cli/config-validate.js +0 -39
  524. package/dist/commands/graph/graph-cli.js +0 -124
  525. package/dist/commands/graph/graph.js +0 -487
  526. package/dist/commands/improve/calibration.js +0 -161
  527. package/dist/commands/improve/dedup.js +0 -482
  528. package/dist/commands/improve/extract-watch.js +0 -140
  529. package/dist/commands/improve/hot-probation.js +0 -45
  530. package/dist/commands/improve/improve-auto-accept.js +0 -276
  531. package/dist/commands/improve/improve-profiles.js +0 -168
  532. package/dist/commands/improve/procedural.js +0 -398
  533. package/dist/commands/improve/recombine.js +0 -818
  534. package/dist/commands/improve/schema-similarity-gate.js +0 -168
  535. package/dist/commands/lint/agent-linter.js +0 -44
  536. package/dist/commands/lint/command-linter.js +0 -44
  537. package/dist/commands/lint/default-linter.js +0 -16
  538. package/dist/commands/lint/fact-linter.js +0 -39
  539. package/dist/commands/lint/knowledge-linter.js +0 -16
  540. package/dist/commands/lint/memory-linter.js +0 -61
  541. package/dist/commands/lint/registry.js +0 -41
  542. package/dist/commands/lint/skill-linter.js +0 -45
  543. package/dist/commands/lint/task-linter.js +0 -50
  544. package/dist/commands/lint/workflow-linter.js +0 -81
  545. package/dist/commands/proposal/legacy-import.js +0 -115
  546. package/dist/commands/sources/history.js +0 -196
  547. package/dist/commands/tasks/default-tasks.js +0 -186
  548. package/dist/commands/wiki-cli.js +0 -292
  549. package/dist/core/asset/asset-registry.js +0 -76
  550. package/dist/core/asset/asset-spec.js +0 -316
  551. package/dist/core/config/config-migration.js +0 -602
  552. package/dist/core/deep-merge.js +0 -38
  553. package/dist/core/eval/rank-metrics.js +0 -113
  554. package/dist/core/ripgrep/install.js +0 -163
  555. package/dist/core/ripgrep/resolve.js +0 -81
  556. package/dist/indexer/db/db.js +0 -1414
  557. package/dist/indexer/manifest.js +0 -170
  558. package/dist/indexer/passes/metadata-contributors.js +0 -31
  559. package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -94
  560. package/dist/integrations/harnesses/opencode-sdk/index.js +0 -25
  561. package/dist/llm/call-ai.js +0 -62
  562. package/dist/llm/memory-infer-impl.js +0 -138
  563. package/dist/output/shapes/distill.js +0 -14
  564. package/dist/output/shapes/history.js +0 -11
  565. package/dist/output/text/distill.js +0 -6
  566. package/dist/output/text/enable-disable.js +0 -8
  567. package/dist/output/text/history.js +0 -6
  568. package/dist/output/text/wiki.js +0 -16
  569. package/dist/registry/build-index.js +0 -386
  570. package/dist/schemas/akm-config.json +0 -14225
  571. package/dist/scripts/migrate-storage.js +0 -13169
  572. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +0 -10169
  573. package/dist/scripts/migrations/v16-to-v17.js +0 -141
  574. package/dist/setup/legacy-config.js +0 -106
  575. package/dist/storage/repositories/consolidation-repository.js +0 -38
  576. package/dist/storage/repositories/recombine-repository.js +0 -213
  577. package/dist/wiki/wiki-templates.js +0 -15
  578. package/dist/wiki/wiki.js +0 -1012
  579. package/dist/workflows/authoring/workflow-program-template.yaml +0 -31
  580. package/dist/workflows/db.js +0 -350
  581. package/dist/workflows/exec/watch.js +0 -116
  582. package/dist/workflows/program/parser.js +0 -760
  583. package/dist/workflows/program/project.js +0 -105
  584. package/docs/data-and-telemetry.md +0 -227
  585. package/docs/migration/release-notes/0.9.0-beta.60.md +0 -19
  586. /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/registry.js +0 -0
  587. /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/youtube.js +0 -0
@@ -4,11 +4,20 @@
4
4
  import fs from "node:fs";
5
5
  import path from "node:path";
6
6
  import { ConfigError } from "../errors.js";
7
- import { backupExistingConfig, parseConfigText, withConfigLock, writeConfigAtomic } from "./config-io.js";
8
- import { CURRENT_CONFIG_VERSION, compareConfigVersion, migrateConfigShape } from "./config-migration.js";
9
- import { AkmConfigSchema } from "./config-schema.js";
7
+ import { assertNoPendingMigrationOperation } from "../migration-operation.js";
8
+ import { acquireConfigLock, backupExistingConfig, parseConfigText, readConfigText, withConfigLock, writeConfigAtomic, } from "./config-io.js";
9
+ import { AkmConfigSchema, CURRENT_CONFIG_VERSION } from "./config-schema.js";
10
+ import { bundlesToSourceEntries } from "./config-sources.js";
11
+ import { deepMergeConfig } from "./deep-merge.js";
10
12
  export { stripJsonComments } from "./config-io.js";
11
- import { getCacheDir, getConfigPath } from "../paths.js";
13
+ // requireLlmConfig / getDefaultLlmConfig moved to
14
+ // integrations/agent/engine-resolution.ts (WI-9.8 KILL 3, D.3 edge A) — they
15
+ // called that module's materializeLlmConnection/resolveLlmEngineUse, and that
16
+ // module imported LlmConnectionConfig back from this file, closing a 2-file
17
+ // cycle that also fused config.ts into the harness/agent-runtime SCC. This
18
+ // file can no longer re-export them (a re-export is still a graph edge to
19
+ // engine-resolution.ts) — import them from there directly.
20
+ import { getConfigPath } from "../paths.js";
12
21
  import { warn } from "../warn.js";
13
22
  // Canonical harness-id source of truth (#565) — runtime value re-export.
14
23
  export { VALID_HARNESS_IDS } from "./config-types.js";
@@ -48,7 +57,8 @@ export function resolveBatchSize(configured, contextLength) {
48
57
  }
49
58
  // ── Defaults ────────────────────────────────────────────────────────────────
50
59
  export const DEFAULT_CONFIG = {
51
- semanticSearchMode: "auto",
60
+ configVersion: "0.9.0",
61
+ semanticSearchMode: "off",
52
62
  registries: [
53
63
  { url: "https://raw.githubusercontent.com/itlackey/akm-registry/main/index.json", name: "akm-registry" },
54
64
  { url: "https://skills.sh", name: "skills.sh", provider: "skills-sh", enabled: false },
@@ -59,20 +69,23 @@ export const DEFAULT_CONFIG = {
59
69
  },
60
70
  };
61
71
  // ── Load / Save / Update ────────────────────────────────────────────────────
62
- const PROJECT_CONFIG_RELATIVE_PATH = path.join(".akm", "config.json");
63
72
  let cachedConfig;
64
73
  export function resetConfigCache() {
65
74
  cachedConfig = undefined;
66
75
  }
67
76
  export function loadUserConfig() {
77
+ assertNoPendingMigrationOperation();
68
78
  const configPath = getConfigPath();
69
79
  let stat;
70
80
  try {
71
81
  stat = fs.statSync(configPath);
72
82
  }
73
- catch {
83
+ catch (err) {
84
+ if (err.code !== "ENOENT") {
85
+ throw new ConfigError(`Unable to read config at ${configPath}: ${err instanceof Error ? err.message : String(err)}`, "INVALID_CONFIG_FILE");
86
+ }
74
87
  cachedConfig = undefined;
75
- return applyRuntimeEnvApiKeys({ ...DEFAULT_CONFIG });
88
+ return { ...DEFAULT_CONFIG };
76
89
  }
77
90
  // Cache key: mtimeMs + size. Tests that write rapidly back-to-back inside
78
91
  // the mtime resolution window MUST call resetConfigCache() between writes —
@@ -85,17 +98,16 @@ export function loadUserConfig() {
85
98
  }
86
99
  let text;
87
100
  try {
88
- text = fs.readFileSync(configPath, "utf8");
101
+ text = readConfigText(configPath);
89
102
  }
90
- catch {
103
+ catch (err) {
104
+ throw new ConfigError(`Unable to read config at ${configPath}: ${err instanceof Error ? err.message : String(err)}`, "INVALID_CONFIG_FILE");
105
+ }
106
+ if (text === undefined) {
91
107
  cachedConfig = undefined;
92
- return applyRuntimeEnvApiKeys({ ...DEFAULT_CONFIG });
108
+ return { ...DEFAULT_CONFIG };
93
109
  }
94
- // Auto-migration: rewrite legacy shapes to disk on cache miss so the schema
95
- // sees canonical input. AKM_NO_AUTO_MIGRATE=1 skips the disk rewrite (still
96
- // applies in-memory).
97
- text = maybeAutoMigrateConfigFile(configPath, text);
98
- const finalConfig = applyRuntimeEnvApiKeys(parseAndValidate(text, configPath));
110
+ const finalConfig = parseAndValidateConfigText(text, configPath);
99
111
  // Re-stat after potential write-back so the cache key reflects the new mtime.
100
112
  let finalStat = stat;
101
113
  try {
@@ -113,222 +125,141 @@ export function loadUserConfig() {
113
125
  return finalConfig;
114
126
  }
115
127
  /**
116
- * Parse raw config text, run the legacy-shape migration
117
- * ({@link migrateConfigShape}), then validate via Zod
128
+ * Parse raw config text and validate via Zod.
118
129
  * ({@link AkmConfigSchema}). Returns the merged-with-defaults AkmConfig.
119
130
  *
120
- * The migration handles all one-time 0.7→0.8 transforms (legacy keys,
121
- * boolean→string coercions, openviking rename); the schema then validates
122
- * the canonical shape and throws on anything it doesn't recognise.
131
+ * The schema accepts only the current config version and validates the
132
+ * canonical shape before defaults are merged.
123
133
  */
124
- function parseAndValidate(text, sourcePath) {
125
- // Migration absorbs 0.7→0.8 input transforms (semanticSearchMode bool→string,
126
- // stashes[] → sources[], openviking removal); the schema then sees a
127
- // canonical shape. Migration is idempotent on already-migrated input.
128
- const migrated = migrateConfigShape(parseConfigText(text, sourcePath)).result;
129
- const parsed = AkmConfigSchema.safeParse(migrated);
134
+ export function parseAndValidateConfigText(text, sourcePath) {
135
+ const raw = parseConfigText(text, sourcePath);
136
+ if (raw.configVersion !== CURRENT_CONFIG_VERSION) {
137
+ throw new ConfigError(`Unsupported configVersion${sourcePath ? ` at ${sourcePath}` : ""}: expected "${CURRENT_CONFIG_VERSION}".`, "UNSUPPORTED_CONFIG_VERSION", "Recreate engines and improve.strategies manually for AKM 0.9.0; profile-based configuration is not translated automatically.");
138
+ }
139
+ const parsed = AkmConfigSchema.safeParse(raw);
130
140
  if (!parsed.success) {
131
141
  const lines = parsed.error.issues.map((i) => ` - ${i.path.join(".") || "(root)"}: ${i.message}`).join("\n");
132
142
  const where = sourcePath ? ` at ${sourcePath}` : "";
133
143
  throw new ConfigError(`Invalid config${where}:\n${lines}`, "INVALID_CONFIG_FILE");
134
144
  }
135
- return mergeLoadedConfig(DEFAULT_CONFIG, parsed.data);
136
- }
137
- export function getSources(config) {
138
- return config.sources ?? [];
139
- }
140
- export function getEffectiveRegistries(config) {
141
- return config.registries ?? DEFAULT_CONFIG.registries ?? [];
142
- }
143
- /**
144
- * Resolve the name of the default LLM profile.
145
- *
146
- * Resolution order:
147
- * 1. `defaults.llm` — explicit pointer set by the user.
148
- * 2. A profile literally named `default` under `profiles.llm` — implicit
149
- * fallback. The convention "name your default profile `default`" is
150
- * what `akm setup` produces, so an unset `defaults.llm` next to a
151
- * `profiles.llm.default` block is overwhelmingly a config-rewrite
152
- * casualty (see [[project_akm_config_clobber_trap]]) rather than
153
- * intent. Treating that shape as configured avoids the silent
154
- * `getDefaultLlmConfig → undefined → pass-returns-zero` failure mode
155
- * that produced 18h of no-op memory-inference runs on 2026-05-23.
156
- *
157
- * Returns `undefined` when neither path resolves to a profile name.
158
- */
159
- function resolveDefaultLlmProfileName(config) {
160
- const explicit = config.defaults?.llm;
161
- if (explicit)
162
- return explicit;
163
- if (config.profiles?.llm?.default)
164
- return "default";
165
- return undefined;
166
- }
167
- /**
168
- * Resolve the default LLM connection from `profiles.llm[defaults.llm]`.
169
- *
170
- * Throws {@link ConfigError} when no default profile can be resolved (neither
171
- * `defaults.llm` nor an implicit `profiles.llm.default`) or when the named
172
- * profile does not exist under `profiles.llm`. Use this in code paths that
173
- * must have an LLM configured (per-pass index calls, distill, consolidate,
174
- * etc).
175
- */
176
- export function requireLlmConfig(config) {
177
- const defaultName = resolveDefaultLlmProfileName(config);
178
- if (!defaultName) {
179
- throw new ConfigError("LLM is not configured. Run `akm setup` or set `defaults.llm` to a profile defined in `profiles.llm`.", "LLM_NOT_CONFIGURED");
180
- }
181
- const profile = config.profiles?.llm?.[defaultName];
182
- if (!profile) {
183
- throw new ConfigError(`LLM default profile "${defaultName}" not found in profiles.llm.`, "LLM_NOT_CONFIGURED", `Available profiles: ${Object.keys(config.profiles?.llm ?? {}).join(", ") || "none"}. Run \`akm setup\` to configure.`);
145
+ const merged = deepMergeConfig(DEFAULT_CONFIG, parsed.data);
146
+ const finalResult = AkmConfigSchema.safeParse(merged);
147
+ if (!finalResult.success) {
148
+ const lines = finalResult.error.issues.map((i) => ` - ${i.path.join(".") || "(root)"}: ${i.message}`).join("\n");
149
+ throw new ConfigError(`Invalid merged config${sourcePath ? ` at ${sourcePath}` : ""}:\n${lines}`, "INVALID_CONFIG_FILE");
184
150
  }
185
- return profile;
151
+ return finalResult.data;
186
152
  }
187
153
  /**
188
- * Like {@link requireLlmConfig} but returns `undefined` instead of throwing
189
- * when no LLM is configured. Use in code paths where the LLM is optional.
190
- */
191
- export function getDefaultLlmConfig(config) {
192
- const defaultName = resolveDefaultLlmProfileName(config);
193
- if (!defaultName)
194
- return undefined;
195
- return config.profiles?.llm?.[defaultName];
196
- }
197
- /**
198
- * Resolve the per-process config section for an improve process,
199
- * centralizing the deeply-nested lookup
200
- * `profile?.processes?.<name>` that was previously copy-pasted across the
201
- * improve command family (20+ call sites).
154
+ * The configured stash sources as an ordered {@link SourceConfigEntry} list.
202
155
  *
203
- * When an `activeProfile` is supplied (the profile resolved for the current
204
- * `akm improve --profile <name>` run), its per-process override wins; otherwise
205
- * — and as a fallback when the active profile does not define the section — the
206
- * lookup falls back to the `"default"` improve profile from the on-disk config.
207
- * Callers that have not yet threaded the active profile pass only `config` and
208
- * get the historical default-profile behavior unchanged.
156
+ * 0.9.0 (spec §10.1): the retired `stashDir`/`sources[]`/`installed[]` trio is
157
+ * gone — every source is a `bundles.<slug>` entry. This derives the unified
158
+ * source list from `bundles` (defaultBundle first, then map order), so callers
159
+ * that iterated `sources[]` keep working over the new shape. Returns `[]` when
160
+ * no bundles are configured.
209
161
  */
210
- export function getImproveProcessConfig(config, processName, activeProfile) {
211
- const fromActiveProfile = activeProfile?.processes?.[processName];
212
- if (fromActiveProfile !== undefined)
213
- return fromActiveProfile;
214
- return config.profiles?.improve?.default?.processes?.[processName];
162
+ export function getSources(config) {
163
+ return bundlesToSourceEntries(config) ?? [];
164
+ }
165
+ export function getEffectiveRegistries(config) {
166
+ return config.registries ?? DEFAULT_CONFIG.registries ?? [];
215
167
  }
216
168
  /**
217
- * Run `migrateConfigShape` on the raw text and — unless `AKM_NO_AUTO_MIGRATE=1`
218
- * is set — persist the migrated result. Returns the (possibly migrated) text
219
- * for the caller to feed into `parseAndValidate`.
220
- *
221
- * If the on-disk config is newer than this binary's known version, the bytes
222
- * are left untouched (we won't silently strip fields on downgrade).
169
+ * Transitional internal accessor. It deliberately never consults a configured
170
+ * default strategy; callers must pass their already selected strategy.
223
171
  */
224
- function maybeAutoMigrateConfigFile(configPath, text) {
225
- let obj;
226
- try {
227
- obj = parseConfigText(text);
228
- }
229
- catch {
230
- return text; // Malformed JSON — let parseAndValidate surface the error.
231
- }
232
- // Downgrade protection. Skip migration when the on-disk config is NEWER than
233
- // this binary (=== 1), OR when it carries a configVersion we cannot order
234
- // against ours (compareConfigVersion returns undefined for an unparseable
235
- // value — e.g. one written by a newer/foreign akm). Migrating such a config
236
- // could strip fields a newer binary added, a cross-version data-loss path.
237
- //
238
- // A MISSING configVersion (absent → undefined) is a legacy pre-versioning
239
- // config that MUST still migrate, so the unparseable-skip is gated on the
240
- // field being PRESENT (`onDiskVersion !== undefined`).
241
- const onDiskVersion = obj.configVersion;
242
- const versionOrder = compareConfigVersion(onDiskVersion, CURRENT_CONFIG_VERSION);
243
- if (versionOrder === 1 || (onDiskVersion !== undefined && versionOrder === undefined)) {
244
- return text;
245
- }
246
- const { changed, result } = migrateConfigShape(obj);
247
- if (!changed)
248
- return text;
249
- const migratedText = `${JSON.stringify(result, null, 2)}\n`;
250
- if (process.env.AKM_NO_AUTO_MIGRATE === "1")
251
- return migratedText;
252
- try {
253
- withConfigLock(() => {
254
- backupExistingConfig(configPath);
255
- writeConfigAtomic(configPath, result);
256
- });
257
- const newVersion = typeof result.configVersion === "string" ? result.configVersion : "0.8.0";
258
- const backupDir = `${getCacheDir()}/config-backups`;
259
- // WS-2: emit a loud banner to BOTH stderr and stdout so pipelines and
260
- // interactive terminals both see it. Include the backup path (resolved,
261
- // not ~/...), opt-out env var, and preview diff command.
262
- const banner = [
263
- "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━",
264
- ` akm: auto-migrated config → ${newVersion} format`,
265
- ` file: ${configPath}`,
266
- ` backup: ${backupDir}/config-<timestamp>.json`,
267
- " to opt out of future auto-migration: AKM_NO_AUTO_MIGRATE=1",
268
- " to preview a dry-run diff: akm config migrate --dry-run --print-diff",
269
- "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━",
270
- ].join("\n");
271
- process.stderr?.write?.(`${banner}\n`);
272
- process.stdout?.write?.(`${banner}\n`);
273
- }
274
- catch (err) {
275
- // #461: never return migrated bytes when disk write fails — that triggers
276
- // an infinite re-migrate loop on every load. Hard-error so the user
277
- // notices and either fixes the disk issue or sets AKM_NO_AUTO_MIGRATE=1.
278
- throw new ConfigError(`Failed to write migrated config to ${configPath}: ${err instanceof Error ? err.message : String(err)}`, "INVALID_CONFIG_FILE", "Check filesystem permissions, free space, and disk health. To skip auto-migration, set AKM_NO_AUTO_MIGRATE=1.");
279
- }
280
- return migratedText;
172
+ export function getImproveProcessConfig(processName, selected) {
173
+ return selected?.processes?.[processName];
281
174
  }
282
175
  export function loadConfig() {
283
- // Single-layer load: only the user-level config file is read. Project-level
284
- // .akm/config.json files discovered under cwd-ancestors emit a one-time
285
- // deprecation warning (#457) but are NOT merged.
286
- warnIfProjectConfigPresent(process.cwd());
287
176
  return loadUserConfig();
288
177
  }
289
- let saveConfigOverride;
290
- /** TEST-ONLY. Swap the implementation of `saveConfig`; pass undefined to restore. */
291
- export function _setSaveConfigForTests(fake) {
292
- saveConfigOverride = fake;
293
- }
294
178
  export function saveConfig(config) {
295
- if (saveConfigOverride) {
296
- saveConfigOverride(config);
297
- return;
298
- }
299
- saveConfigReal(config);
179
+ // Every lifecycle write produces the only config version this binary can load.
180
+ const currentConfig = { ...config, configVersion: CURRENT_CONFIG_VERSION };
181
+ saveConfigReal(currentConfig);
300
182
  }
301
183
  function saveConfigReal(config) {
184
+ assertNoPendingMigrationOperation();
302
185
  cachedConfig = undefined;
303
186
  const configPath = getConfigPath();
304
187
  const dir = path.dirname(configPath);
305
188
  fs.mkdirSync(dir, { recursive: true });
306
- const sanitized = sanitizeConfigForWrite(config);
307
- // Final validation gate before bytes hit disk. Runs the FULL schema —
308
- // including the cross-field superRefine guards (removed `feedbackDistillation`
309
- // process key, `defaultWriteTarget` resolution, writable npm/website sources)
310
- // and all type/enum/range checks — so an `akm config set` (leaf OR object
311
- // form) cannot persist a guard-violating or mistyped value. NOTE: unknown
312
- // keys are intentionally NOT rejected here — object schemas are `.passthrough()`
313
- // so cross-version skew round-trips (see config-schema.ts header); the lenient
314
- // tolerance is by design, not an oversight.
315
- const parseResult = AkmConfigSchema.safeParse(sanitized);
316
- if (!parseResult.success) {
317
- const lines = parseResult.error.issues.map((i) => ` - ${i.path.join(".") || "(root)"}: ${i.message}`).join("\n");
318
- throw new ConfigError(`Refusing to save invalid config:\n${lines}`, "INVALID_CONFIG_FILE", "Fix the listed fields, or undo the offending `akm config set`. " +
319
- "If this looks like an akm bug, re-run with --debug to attach the traceback.");
320
- }
321
- // WS-3: acquire the config write lock so concurrent `akm config set`
322
- // invocations do not interleave their backup+atomic-write cycles.
323
189
  withConfigLock(() => {
190
+ assertNoPendingMigrationOperation();
191
+ const validated = validateCompleteConfig(config);
324
192
  backupExistingConfig(configPath);
325
- writeConfigAtomic(configPath, sanitized);
193
+ writeConfigAtomic(configPath, sanitizeConfigForWrite(validated));
326
194
  });
327
195
  }
196
+ export function validateCompleteConfig(config) {
197
+ const parseResult = AkmConfigSchema.safeParse(config);
198
+ if (parseResult.success)
199
+ return parseResult.data;
200
+ const lines = parseResult.error.issues.map((i) => ` - ${i.path.join(".") || "(root)"}: ${i.message}`).join("\n");
201
+ throw new ConfigError(`Refusing to save invalid config:\n${lines}`, "INVALID_CONFIG_FILE", "Fix the listed fields, or undo the offending `akm config set`. " +
202
+ "If this looks like an akm bug, re-run with --debug to attach the traceback.");
203
+ }
204
+ /**
205
+ * Mutate config under one fail-closed lock spanning read, merge, validation,
206
+ * ordinary backup, and atomic write.
207
+ */
208
+ export function mutateConfig(mutate, options) {
209
+ assertNoPendingMigrationOperation();
210
+ cachedConfig = undefined;
211
+ const configPath = getConfigPath();
212
+ return withConfigLock(() => {
213
+ assertNoPendingMigrationOperation();
214
+ const text = readConfigText(configPath);
215
+ if (text === undefined && options?.absentNoop) {
216
+ return { config: { ...DEFAULT_CONFIG }, written: false };
217
+ }
218
+ const current = text === undefined ? { ...DEFAULT_CONFIG } : parseAndValidateConfigText(text, configPath);
219
+ const mutated = mutate(current);
220
+ if (mutated === current)
221
+ return { config: current, written: false };
222
+ const next = validateCompleteConfig({ ...mutated, configVersion: CURRENT_CONFIG_VERSION });
223
+ if (text !== undefined)
224
+ backupExistingConfig(configPath);
225
+ fs.mkdirSync(path.dirname(configPath), { recursive: true });
226
+ writeConfigAtomic(configPath, sanitizeConfigForWrite(next));
227
+ return { config: next, written: true };
228
+ });
229
+ }
230
+ /**
231
+ * Mutate config while holding the write lock across one validated pre-commit
232
+ * side effect. Setup uses this to reject a three-way conflict before creating
233
+ * its stash, while preventing another config writer from racing the final save.
234
+ */
235
+ export async function mutateConfigWithPrecommit(mutate, precommit) {
236
+ assertNoPendingMigrationOperation();
237
+ cachedConfig = undefined;
238
+ const configPath = getConfigPath();
239
+ const release = acquireConfigLock();
240
+ try {
241
+ assertNoPendingMigrationOperation();
242
+ const text = readConfigText(configPath);
243
+ const current = text === undefined ? { ...DEFAULT_CONFIG } : parseAndValidateConfigText(text, configPath);
244
+ const mutated = mutate(current);
245
+ const next = validateCompleteConfig({ ...mutated, configVersion: CURRENT_CONFIG_VERSION });
246
+ if (text !== undefined)
247
+ backupExistingConfig(configPath);
248
+ const precommitResult = await precommit(next);
249
+ if (mutated === current)
250
+ return { config: current, written: false, precommit: precommitResult };
251
+ fs.mkdirSync(path.dirname(configPath), { recursive: true });
252
+ writeConfigAtomic(configPath, sanitizeConfigForWrite(next));
253
+ return { config: next, written: true, precommit: precommitResult };
254
+ }
255
+ finally {
256
+ release();
257
+ }
258
+ }
328
259
  /**
329
260
  * Strip literal apiKey fields before writing config to disk.
330
261
  * API keys are expected to come from environment variables
331
- * (AKM_EMBED_API_KEY, AKM_LLM_API_KEY, AKM_PROFILE_<NAME>_API_KEY).
262
+ * (AKM_EMBED_API_KEY, AKM_LLM_API_KEY, AKM_ENGINE_<NAME>_API_KEY).
332
263
  *
333
264
  * `${VAR}` / `$VAR` references are preserved — they are not secrets, they
334
265
  * are deferred lookups resolved at consumption by `resolveSecret`. Dropping
@@ -340,7 +271,7 @@ function saveConfigReal(config) {
340
271
  * --yes` with an `apiKey` field expected persistence and got a wiped config
341
272
  * with no feedback.
342
273
  */
343
- function sanitizeConfigForWrite(config) {
274
+ export function sanitizeConfigForWrite(config) {
344
275
  const sanitized = { ...config };
345
276
  const stripped = [];
346
277
  if (config.embedding?.apiKey !== undefined) {
@@ -359,50 +290,36 @@ function sanitizeConfigForWrite(config) {
359
290
  else if (config.embedding) {
360
291
  sanitized.embedding = { ...config.embedding };
361
292
  }
362
- if (config.profiles?.llm) {
363
- const llmProfiles = {};
364
- for (const [name, profile] of Object.entries(config.profiles.llm)) {
365
- if (profile.apiKey !== undefined) {
366
- if (isEnvReference(profile.apiKey)) {
367
- llmProfiles[name] = { ...profile };
368
- }
369
- else {
370
- const { apiKey: _drop, ...rest } = profile;
371
- llmProfiles[name] = rest;
372
- if (profile.apiKey) {
373
- const envVar = `AKM_PROFILE_${name.toUpperCase().replace(/-/g, "_")}_API_KEY`;
374
- stripped.push(`profiles.llm.${name}.apiKey (set ${envVar} to provide at runtime)`);
375
- }
376
- }
377
- }
378
- else {
379
- llmProfiles[name] = { ...profile };
293
+ if (config.engines) {
294
+ const engines = {};
295
+ for (const [name, engine] of Object.entries(config.engines)) {
296
+ if (engine.kind !== "llm" || engine.apiKey === undefined || isEnvReference(engine.apiKey)) {
297
+ engines[name] = { ...engine };
298
+ continue;
380
299
  }
300
+ const { apiKey: _drop, ...rest } = engine;
301
+ engines[name] = rest;
302
+ if (engine.apiKey)
303
+ stripped.push(`engines.${name}.apiKey`);
381
304
  }
382
- sanitized.profiles = {
383
- ...(sanitized.profiles ?? {}),
384
- llm: llmProfiles,
385
- };
305
+ sanitized.engines = engines;
386
306
  }
387
307
  if (stripped.length > 0) {
388
- warn(`Config sanitizer dropped API key(s) before writing to disk:\n - ${stripped.join("\n - ")}\n\nakm does not persist API keys to config.json. Set the listed environment variables to provide them at runtime, or use \`\${VAR}\` references in your config to defer lookup. See docs/data-and-telemetry.md.`);
308
+ warn(`Config sanitizer dropped API key(s) before writing to disk:\n - ${stripped.join("\n - ")}\n\nakm does not persist API keys to config.json. Set the listed environment variables to provide them at runtime, or use \`\${VAR}\` references in your config to defer lookup. See docs/reference/data-and-telemetry.md.`);
389
309
  }
390
310
  return sanitized;
391
311
  }
392
- /** Matches `${VAR}`, `${VAR:-default}`, or `$VAR`. */
312
+ /** Matches the only 0.9 symbolic secret forms: `${VAR}` or `$VAR`. */
393
313
  function isEnvReference(value) {
394
- return /^\$\{[^}]+\}$|^\$[A-Za-z_][A-Za-z0-9_]*$/.test(value);
314
+ return /^\$\{[A-Za-z_][A-Za-z0-9_]*\}$|^\$[A-Za-z_][A-Za-z0-9_]*$/.test(value);
395
315
  }
396
316
  export function updateConfig(partial) {
397
- const current = loadUserConfig();
398
- const merged = mergeLoadedConfig(current, partial);
399
- saveConfig(merged);
400
- return merged;
317
+ return mutateConfig((current) => deepMergeConfig(current, partial)).config;
401
318
  }
402
319
  // ── Helpers ─────────────────────────────────────────────────────────────────
403
320
  /**
404
- * Resolve a single secret value by expanding `${VAR}` / `$VAR` /
405
- * `${VAR:-default}` references against `process.env`. Use this at apiKey /
321
+ * Resolve a single secret value by expanding `${VAR}` / `$VAR` references
322
+ * against `process.env`. Use this at apiKey /
406
323
  * authorization-header consumption sites (LLM client, embedder, agent SDK
407
324
  * runner) — NOT on the load path. Non-string inputs pass through unchanged.
408
325
  *
@@ -420,26 +337,21 @@ export function resolveSecret(value) {
420
337
  return value;
421
338
  if (!value.includes("$"))
422
339
  return value;
423
- return value.replace(/\$\{([^}]+)\}|\$([A-Za-z_][A-Za-z0-9_]*)/g, (_match, braced, bare) => {
424
- if (braced) {
425
- const [name, ...rest] = braced.split(":-");
426
- const fallback = rest.join(":-");
427
- return process.env[name] ?? fallback ?? "";
428
- }
429
- return process.env[bare] ?? "";
340
+ return value.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}|\$([A-Za-z_][A-Za-z0-9_]*)/g, (_match, braced, bare) => {
341
+ return process.env[(braced ?? bare)] ?? "";
430
342
  });
431
343
  }
432
344
  /**
433
345
  * Read a per-pass {@link IndexPassConfig} entry from {@link IndexConfig},
434
346
  * filtering out the reserved feature-section keys so callers don't mistake
435
- * `metadataEnhance` / `stalenessDetection` for a pass.
347
+ * `metadataEnhance` for a pass.
436
348
  */
437
349
  /**
438
350
  * Reserved well-known keys on IndexConfig that are NOT per-pass entries.
439
- * `stalenessDetection` is retired (10-Q3) but stays reserved so a leftover
440
- * config section is never misread as a pass entry.
351
+ * `indexBodyOpening` (stash-conventions SPEC-8) is a boolean feature flag, not
352
+ * a pass section.
441
353
  */
442
- const INDEX_RESERVED_KEYS = new Set(["metadataEnhance", "stalenessDetection"]);
354
+ const INDEX_RESERVED_KEYS = new Set(["metadataEnhance", "indexBodyOpening"]);
443
355
  export function getIndexPassConfig(config, passName) {
444
356
  if (!config)
445
357
  return undefined;
@@ -451,99 +363,10 @@ export function getIndexPassConfig(config, passName) {
451
363
  return entry;
452
364
  }
453
365
  // Re-export source runtime helpers — implementation lives in config-sources.ts.
454
- export { parseSourceSpec, resolveConfiguredSources } from "./config-sources.js";
366
+ export { bundleComponentConfig, bundleEntryToSourceEntry, bundlesToSourceEntries, installedSourceDescriptor, parseSourceSpec, primaryBundlePath, resolveConfiguredSources, } from "./config-sources.js";
455
367
  /**
456
368
  * Merge a partial user-config override onto a base config. Used by
457
369
  * {@link loadUserConfig} (DEFAULT_CONFIG + on-disk) and {@link updateConfig}
458
370
  * (current config + partial patch). Sub-objects with named records (profiles,
459
371
  * defaults, etc.) shallow-merge; arrays override wholesale.
460
372
  */
461
- function mergeLoadedConfig(base, override) {
462
- if (!override)
463
- return { ...base };
464
- const merged = { ...base, ...override };
465
- // Shallow-merge sub-objects so a partial update to e.g. `output.format`
466
- // doesn't drop the existing `output.detail`.
467
- for (const key of ["output", "embedding", "index", "defaults"]) {
468
- if (base[key] && override[key]) {
469
- // biome-ignore lint/suspicious/noExplicitAny: heterogeneous structural merge
470
- merged[key] = { ...base[key], ...override[key] };
471
- }
472
- }
473
- if (base.profiles && override.profiles) {
474
- const next = { ...base.profiles };
475
- for (const k of ["llm", "agent", "improve"]) {
476
- const ovr = override.profiles[k];
477
- if (ovr)
478
- next[k] = { ...(next[k] ?? {}), ...ovr };
479
- }
480
- merged.profiles = next;
481
- }
482
- return merged;
483
- }
484
- function applyRuntimeEnvApiKeys(config) {
485
- const next = { ...config };
486
- if (next.embedding && !next.embedding.apiKey) {
487
- const envKey = process.env.AKM_EMBED_API_KEY?.trim();
488
- if (envKey)
489
- next.embedding = { ...next.embedding, apiKey: envKey };
490
- }
491
- // LLM profile keys: AKM_LLM_API_KEY for the default profile, then
492
- // AKM_PROFILE_<UPPER>_API_KEY for any profile (per-profile wins).
493
- // Resolve the default profile the SAME way the rest of the config layer does
494
- // (resolveDefaultLlmProfileName), so the implicit `profiles.llm.default`
495
- // fallback is honored. Keying off the raw `defaults.llm` field alone silently
496
- // dropped AKM_LLM_API_KEY for configs that rely on the implicit default —
497
- // the same no-op-run class as the 2026-05-23 incident.
498
- const defaultProfile = resolveDefaultLlmProfileName(next);
499
- if (next.profiles?.llm) {
500
- const updated = { ...next.profiles.llm };
501
- let changed = false;
502
- for (const [name, profile] of Object.entries(updated)) {
503
- if (profile.apiKey)
504
- continue;
505
- const perProfile = process.env[`AKM_PROFILE_${name.toUpperCase().replace(/-/g, "_")}_API_KEY`]?.trim();
506
- const fallback = name === defaultProfile ? process.env.AKM_LLM_API_KEY?.trim() : undefined;
507
- const envKey = perProfile || fallback;
508
- if (envKey) {
509
- updated[name] = { ...profile, apiKey: envKey };
510
- changed = true;
511
- }
512
- }
513
- if (changed)
514
- next.profiles = { ...next.profiles, llm: updated };
515
- }
516
- return next;
517
- }
518
- /**
519
- * Walk cwd-ancestors looking for `.akm/config.json`. If one is found, emit a
520
- * one-time deprecation warning per path. The file's contents are NOT read —
521
- * multi-layer project config was removed in this release; the warning stays
522
- * for one cycle so users notice they have a now-dead file on disk and can
523
- * migrate its settings to the user-level config.
524
- */
525
- const PROJECT_CONFIG_DEPRECATION_WARNED = new Set();
526
- function warnIfProjectConfigPresent(startDir) {
527
- let currentDir = path.resolve(startDir);
528
- while (true) {
529
- const configPath = path.join(currentDir, PROJECT_CONFIG_RELATIVE_PATH);
530
- if (isFile(configPath) && !PROJECT_CONFIG_DEPRECATION_WARNED.has(configPath)) {
531
- PROJECT_CONFIG_DEPRECATION_WARNED.add(configPath);
532
- warn(`[akm] DEPRECATED: project-level config file found at ${configPath}. ` +
533
- "Project-level config files are no longer merged (removed after 0.8.x deprecation). " +
534
- "Move any needed settings to ~/.config/akm/config.json; this file is ignored.");
535
- }
536
- const parentDir = path.dirname(currentDir);
537
- if (parentDir === currentDir)
538
- break;
539
- currentDir = parentDir;
540
- }
541
- }
542
- function isFile(filePath) {
543
- try {
544
- return fs.statSync(filePath).isFile();
545
- }
546
- catch {
547
- return false;
548
- }
549
- }
@@ -0,0 +1,41 @@
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
+ const UNSAFE_KEYS = new Set(["__proto__", "constructor", "prototype"]);
5
+ export function isPlainObject(value) {
6
+ if (value === null || typeof value !== "object" || Array.isArray(value))
7
+ return false;
8
+ const prototype = Object.getPrototypeOf(value);
9
+ return prototype === Object.prototype || prototype === null;
10
+ }
11
+ function copyConfigValue(value) {
12
+ if (Array.isArray(value))
13
+ return value.map(copyConfigValue);
14
+ if (!isPlainObject(value))
15
+ return value;
16
+ const copy = {};
17
+ for (const key of Object.keys(value)) {
18
+ if (UNSAFE_KEYS.has(key))
19
+ throw new TypeError(`Unsafe configuration key: ${key}`);
20
+ copy[key] = copyConfigValue(value[key]);
21
+ }
22
+ return copy;
23
+ }
24
+ /**
25
+ * Merge configuration values without mutating either input. Plain objects merge
26
+ * recursively; arrays and scalar values replace the less-specific value.
27
+ */
28
+ export function deepMergeConfig(base, override) {
29
+ const result = copyConfigValue(base);
30
+ for (const key of Object.keys(override)) {
31
+ if (UNSAFE_KEYS.has(key))
32
+ throw new TypeError(`Unsafe configuration key: ${key}`);
33
+ const next = override[key];
34
+ if (next === undefined)
35
+ continue;
36
+ const current = result[key];
37
+ result[key] =
38
+ isPlainObject(current) && isPlainObject(next) ? deepMergeConfig(current, next) : copyConfigValue(next);
39
+ }
40
+ return result;
41
+ }