akm-cli 0.9.0-rc.0 → 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 (598) hide show
  1. package/CHANGELOG.md +1283 -22
  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/prompts/workflow-unit-preamble.md +26 -0
  26. package/dist/assets/stash-skeleton/README.md +38 -10
  27. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +8 -0
  28. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +8 -0
  29. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +14 -1
  30. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +13 -1
  31. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +9 -1
  32. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +11 -0
  33. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +9 -0
  34. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +9 -0
  35. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +8 -0
  36. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +100 -0
  37. package/dist/assets/stash-skeleton/facts/conventions/domains.md +64 -0
  38. package/dist/assets/stash-skeleton/facts/conventions/organization.md +136 -0
  39. package/dist/assets/tasks/core/extract.yml +3 -2
  40. package/dist/assets/tasks/core/improve.yml +2 -1
  41. package/dist/assets/tasks/core/index-refresh.yml +1 -0
  42. package/dist/assets/tasks/core/sync.yml +1 -0
  43. package/dist/assets/tasks/core/version-check.yml +2 -1
  44. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +5 -0
  45. package/dist/assets/tasks/improve/akm-improve-catchup.yml +8 -0
  46. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +5 -0
  47. package/dist/assets/tasks/improve/akm-improve-frequent.yml +5 -0
  48. package/dist/assets/tasks/improve/akm-improve-nightly.yml +5 -0
  49. package/dist/assets/templates/html/health.html +5 -4
  50. package/dist/assets/workflows/workflow-template.md +31 -15
  51. package/dist/cli/invocation.js +279 -0
  52. package/dist/cli/parse-args.js +5 -90
  53. package/dist/cli/retired-commands.js +78 -0
  54. package/dist/cli/shared.js +158 -48
  55. package/dist/cli-node.mjs +2 -1
  56. package/dist/cli.js +747 -293
  57. package/dist/commands/agent/agent-dispatch.js +19 -18
  58. package/dist/commands/agent/agent-support.js +0 -24
  59. package/dist/commands/agent/contribute-cli.js +43 -97
  60. package/dist/commands/completions.js +80 -23
  61. package/dist/commands/config-cli.js +44 -281
  62. package/dist/commands/env/env-binding.js +99 -0
  63. package/dist/commands/env/env-cli.js +84 -224
  64. package/dist/commands/env/env.js +12 -163
  65. package/dist/commands/env/marker-path.js +6 -0
  66. package/dist/commands/env/secret-cli.js +45 -61
  67. package/dist/commands/env/secret.js +32 -62
  68. package/dist/commands/feedback-cli.js +179 -85
  69. package/dist/commands/health/accept-rate.js +58 -0
  70. package/dist/commands/health/advisories.js +7 -8
  71. package/dist/commands/health/checks.js +279 -94
  72. package/dist/commands/health/html-report.js +197 -578
  73. package/dist/commands/health/improve-metrics.js +277 -246
  74. package/dist/commands/health/llm-usage.js +19 -19
  75. package/dist/commands/health/md-report.js +16 -7
  76. package/dist/commands/health/metrics.js +67 -32
  77. package/dist/commands/health/renderers.js +47 -0
  78. package/dist/commands/health/report-view-model.js +508 -0
  79. package/dist/commands/health/stash-exposure.js +1 -1
  80. package/dist/commands/health/surfaces.js +16 -56
  81. package/dist/commands/health/task-runs.js +3 -67
  82. package/dist/{migrate-storage-node.mjs → commands/health/types-checks.js} +1 -5
  83. package/dist/commands/health/types-improve.js +29 -0
  84. package/dist/{output/text/save.js → commands/health/types-metrics.js} +1 -2
  85. package/dist/commands/health/types-result.js +7 -0
  86. package/dist/commands/health/types-runs.js +4 -0
  87. package/dist/commands/health/types-session-log.js +4 -0
  88. package/dist/commands/health/types-windows.js +4 -0
  89. package/dist/commands/health/types.js +26 -21
  90. package/dist/commands/health/windows.js +2 -3
  91. package/dist/commands/health.js +296 -167
  92. package/dist/commands/improve/anti-collapse.js +5 -5
  93. package/dist/commands/improve/autonomy-gate.js +68 -0
  94. package/dist/commands/improve/collapse-detector.js +65 -52
  95. package/dist/commands/improve/consolidate/chunking.js +9 -7
  96. package/dist/commands/improve/consolidate/eligibility.js +1 -23
  97. package/dist/commands/improve/consolidate/merge.js +4 -0
  98. package/dist/commands/improve/consolidate.js +454 -1354
  99. package/dist/commands/improve/content-hash.js +39 -0
  100. package/dist/commands/improve/distill/content-repair.js +4 -10
  101. package/dist/commands/improve/distill/promote-memory.js +89 -64
  102. package/dist/commands/improve/distill/quality-gate.js +118 -42
  103. package/dist/commands/improve/distill-guards.js +1 -1
  104. package/dist/commands/improve/distill-promotion-policy.js +33 -888
  105. package/dist/commands/improve/distill.js +607 -363
  106. package/dist/commands/improve/eligibility.js +165 -79
  107. package/dist/commands/improve/extract-cli.js +35 -126
  108. package/dist/commands/improve/extract-prompt.js +6 -35
  109. package/dist/commands/improve/extract.js +640 -391
  110. package/dist/commands/improve/feedback-valence.js +2 -12
  111. package/dist/commands/improve/improve-cli.js +134 -135
  112. package/dist/commands/improve/improve-result-file.js +30 -50
  113. package/dist/commands/improve/improve-run-types.js +4 -0
  114. package/dist/commands/improve/improve-strategies.js +135 -0
  115. package/dist/commands/improve/improve.js +904 -701
  116. package/dist/commands/improve/locks.js +64 -111
  117. package/dist/commands/improve/loop-stages.js +1110 -923
  118. package/dist/commands/improve/memory/derived-ref.js +124 -0
  119. package/dist/commands/improve/memory/memory-belief.js +79 -7
  120. package/dist/commands/improve/memory/memory-contradiction-detect.js +49 -52
  121. package/dist/commands/improve/memory/memory-improve.js +25 -37
  122. package/dist/commands/improve/outcome-loop.js +25 -88
  123. package/dist/commands/improve/preparation.js +1034 -813
  124. package/dist/commands/improve/proactive-maintenance.js +34 -9
  125. package/dist/commands/improve/proposal-envelope.js +31 -0
  126. package/dist/commands/improve/reflect.js +983 -794
  127. package/dist/commands/improve/run-context.js +119 -0
  128. package/dist/commands/improve/salience.js +24 -127
  129. package/dist/commands/improve/session-asset.js +7 -3
  130. package/dist/commands/improve/shared.js +14 -34
  131. package/dist/commands/improve/source-identity.js +28 -0
  132. package/dist/commands/improve/triage.js +20 -17
  133. package/dist/commands/lint/base-linter.js +340 -313
  134. package/dist/commands/lint/env-key-rules.js +31 -47
  135. package/dist/commands/lint/index.js +185 -30
  136. package/dist/commands/{events.js → log.js} +28 -38
  137. package/dist/commands/migrate-cli.js +54 -0
  138. package/dist/commands/migration-tool.js +55 -0
  139. package/dist/commands/observability-cli.js +70 -208
  140. package/dist/commands/proposal/diff-format.js +50 -0
  141. package/dist/commands/proposal/drain-policies.js +0 -6
  142. package/dist/commands/proposal/drain.js +91 -40
  143. package/dist/commands/proposal/proposal-cli.js +134 -132
  144. package/dist/commands/proposal/proposal-types.js +56 -0
  145. package/dist/commands/proposal/proposal.js +83 -65
  146. package/dist/commands/proposal/propose-cli.js +88 -0
  147. package/dist/commands/proposal/propose.js +105 -88
  148. package/dist/commands/proposal/repository.js +1303 -278
  149. package/dist/commands/proposal/validators/proposal-quality-validators.js +16 -6
  150. package/dist/commands/proposal/validators/proposal-validators.js +61 -12
  151. package/dist/commands/proposal/validators/proposals.js +6 -8
  152. package/dist/commands/read/curate.js +78 -73
  153. package/dist/commands/read/knowledge.js +510 -13
  154. package/dist/commands/read/registry-search.js +2 -2
  155. package/dist/commands/read/remember-cli.js +84 -15
  156. package/dist/commands/read/search-cli.js +203 -96
  157. package/dist/commands/read/search.js +126 -94
  158. package/dist/commands/read/show.js +226 -250
  159. package/dist/commands/registry-cli.js +34 -60
  160. package/dist/commands/remember.js +18 -57
  161. package/dist/commands/sources/add-cli.js +104 -49
  162. package/dist/commands/sources/bundle-cli.js +166 -0
  163. package/dist/commands/sources/bundle-config-ops.js +63 -0
  164. package/dist/commands/sources/info.js +27 -15
  165. package/dist/commands/sources/init.js +30 -40
  166. package/dist/commands/sources/installed-stashes.js +469 -172
  167. package/dist/commands/sources/migration-help.js +7 -4
  168. package/dist/commands/sources/schema-repair.js +10 -9
  169. package/dist/commands/sources/self-update.js +182 -121
  170. package/dist/commands/sources/source-add.js +169 -178
  171. package/dist/commands/sources/source-clone.js +144 -41
  172. package/dist/commands/sources/source-manage.js +94 -59
  173. package/dist/commands/sources/sources-cli.js +64 -205
  174. package/dist/commands/sources/stash-cli.js +91 -54
  175. package/dist/commands/sources/stash-skeleton.js +1 -1
  176. package/dist/commands/tasks/tasks-cli.js +106 -104
  177. package/dist/commands/tasks/tasks.js +445 -262
  178. package/dist/commands/workflow-cli.js +232 -121
  179. package/dist/core/action-contributors.js +1 -1
  180. package/dist/core/activation-policy.js +49 -0
  181. package/dist/core/adapter/adapters/agent-skills-adapter.js +181 -0
  182. package/dist/core/adapter/adapters/akm-adapter.js +528 -0
  183. package/dist/core/adapter/adapters/akm-lint.js +392 -0
  184. package/dist/core/adapter/adapters/akm-metadata.js +387 -0
  185. package/dist/core/adapter/adapters/akm-task-adapter.js +149 -0
  186. package/dist/core/adapter/adapters/akm-workflow-adapter.js +180 -0
  187. package/dist/core/adapter/adapters/claude-adapter.js +61 -0
  188. package/dist/core/adapter/adapters/dotenv-adapter.js +187 -0
  189. package/dist/core/adapter/adapters/generic-files-adapter.js +119 -0
  190. package/dist/core/adapter/adapters/index.js +80 -0
  191. package/dist/core/adapter/adapters/llm-wiki-adapter.js +419 -0
  192. package/dist/core/adapter/adapters/okf-adapter.js +391 -0
  193. package/dist/core/adapter/adapters/opencode-adapter.js +68 -0
  194. package/dist/core/adapter/adapters/shared.js +286 -0
  195. package/dist/core/adapter/adapters/tool-dir-shared.js +217 -0
  196. package/dist/core/adapter/adapters/website-snapshot-adapter.js +155 -0
  197. package/dist/core/adapter/bundle-adapter.js +4 -0
  198. package/dist/core/adapter/detect-adapter.js +17 -0
  199. package/dist/core/adapter/recognize-match.js +44 -0
  200. package/dist/core/adapter/registry.js +56 -0
  201. package/dist/core/adapter/types.js +4 -0
  202. package/dist/core/asset/akm-markdown.js +30 -0
  203. package/dist/core/asset/asset-placement.js +243 -0
  204. package/dist/core/asset/asset-ref.js +110 -79
  205. package/dist/core/asset/asset-serialize.js +20 -0
  206. package/dist/core/asset/frontmatter.js +28 -12
  207. package/dist/core/asset/markdown.js +40 -51
  208. package/dist/core/asset/resolve-ref.js +274 -0
  209. package/dist/core/asset/stash-meta.js +2 -2
  210. package/dist/core/bundle-id.js +51 -0
  211. package/dist/core/common.js +281 -86
  212. package/dist/core/config/config-io.js +42 -128
  213. package/dist/core/config/config-schema.js +233 -834
  214. package/dist/core/config/config-sources.js +162 -39
  215. package/dist/core/config/config-types.js +16 -11
  216. package/dist/core/config/config-version.js +29 -0
  217. package/dist/core/config/config-walker.js +126 -37
  218. package/dist/core/config/config.js +154 -331
  219. package/dist/core/config/deep-merge.js +41 -0
  220. package/dist/core/config/engine-semantics.js +28 -0
  221. package/dist/core/config/experimental.js +21 -0
  222. package/dist/core/config/schema/embedding.js +38 -0
  223. package/dist/core/config/schema/engines.js +116 -0
  224. package/dist/core/config/schema/experimental.js +47 -0
  225. package/dist/core/config/schema/feedback.js +31 -0
  226. package/dist/core/config/schema/improve-processes.js +389 -0
  227. package/dist/core/config/schema/improve.js +94 -0
  228. package/dist/core/config/schema/index-config.js +176 -0
  229. package/dist/core/config/schema/output.js +18 -0
  230. package/dist/core/config/schema/primitives.js +94 -0
  231. package/dist/core/config/schema/search.js +30 -0
  232. package/dist/core/config/schema/setup.js +18 -0
  233. package/dist/core/config/schema/sources-bundles.js +169 -0
  234. package/dist/core/config/schema/workflow.js +29 -0
  235. package/dist/core/env-secret-ref.js +155 -20
  236. package/dist/core/errors.js +17 -15
  237. package/dist/core/events-types.js +4 -0
  238. package/dist/core/events.js +46 -128
  239. package/dist/core/extra-params.js +62 -0
  240. package/dist/core/file-change.js +17 -0
  241. package/dist/core/file-lock.js +202 -57
  242. package/dist/core/fs-txn.js +392 -0
  243. package/dist/core/git-message.js +59 -0
  244. package/dist/core/improve-result.js +167 -0
  245. package/dist/core/json-schema.js +142 -0
  246. package/dist/core/lesson-lint.js +1 -17
  247. package/dist/core/logs-db.js +1 -1
  248. package/dist/core/maintenance-barrier.js +135 -0
  249. package/dist/core/migration-operation.js +44 -0
  250. package/dist/core/mutation-target.js +78 -0
  251. package/dist/core/paths.js +22 -25
  252. package/dist/core/platform.js +10 -0
  253. package/dist/core/recognition-util.js +128 -0
  254. package/dist/core/redaction.js +392 -0
  255. package/dist/core/standards/resolve-standards-context.js +36 -65
  256. package/dist/core/standards/resolve-stash-standards.js +2 -2
  257. package/dist/core/standards/resolve-type-conventions.js +5 -5
  258. package/dist/core/state/migrations.js +242 -11
  259. package/dist/core/state-db.js +98 -10
  260. package/dist/core/structured.js +1 -1
  261. package/dist/core/subprocess.js +303 -0
  262. package/dist/core/text-truncation.js +9 -5
  263. package/dist/core/time.js +20 -0
  264. package/dist/core/type-presentation.js +130 -0
  265. package/dist/core/warn.js +0 -3
  266. package/dist/core/write-source.js +834 -118
  267. package/dist/indexer/bundle-identity-guard.js +92 -0
  268. package/dist/indexer/db/graph-db.js +1 -25
  269. package/dist/indexer/db/llm-cache.js +1 -1
  270. package/dist/indexer/ensure-index.js +30 -9
  271. package/dist/indexer/graph/graph-boost.js +9 -30
  272. package/dist/indexer/graph/graph-extraction.js +41 -27
  273. package/dist/indexer/graph/graph-types.js +4 -0
  274. package/dist/indexer/index-writer-lock.js +93 -49
  275. package/dist/indexer/index-written-assets.js +100 -53
  276. package/dist/indexer/indexer.js +746 -329
  277. package/dist/indexer/init.js +18 -25
  278. package/dist/indexer/installations.js +142 -0
  279. package/dist/indexer/passes/dir-staleness.js +18 -10
  280. package/dist/indexer/passes/memory-inference.js +25 -15
  281. package/dist/indexer/passes/metadata.js +412 -243
  282. package/dist/indexer/scan/doc-to-entry.js +160 -0
  283. package/dist/indexer/scan/drain-dir.js +134 -0
  284. package/dist/indexer/search/db-search.js +292 -108
  285. package/dist/indexer/search/fts-query.js +64 -0
  286. package/dist/indexer/search/ranking-contributors.js +145 -25
  287. package/dist/indexer/search/ranking-types.js +4 -0
  288. package/dist/indexer/search/ranking.js +28 -71
  289. package/dist/indexer/search/search-attribution.js +67 -0
  290. package/dist/indexer/search/search-fields.js +18 -3
  291. package/dist/indexer/search/search-hit-enrichers.js +30 -40
  292. package/dist/indexer/search/search-source.js +157 -111
  293. package/dist/indexer/search/semantic-status.js +4 -1
  294. package/dist/indexer/usage/usage-events.js +10 -30
  295. package/dist/indexer/walk/file-context.js +3 -45
  296. package/dist/indexer/walk/matchers.js +42 -34
  297. package/dist/indexer/walk/path-resolver.js +11 -5
  298. package/dist/indexer/walk/walker.js +42 -14
  299. package/dist/integrations/agent/builder-shared.js +7 -0
  300. package/dist/integrations/agent/builders.js +5 -56
  301. package/dist/integrations/agent/config.js +3 -143
  302. package/dist/integrations/agent/detect.js +17 -2
  303. package/dist/integrations/agent/engine-resolution.js +231 -0
  304. package/dist/integrations/agent/index.js +1 -2
  305. package/dist/integrations/agent/model-aliases.js +16 -2
  306. package/dist/integrations/agent/profiles.js +36 -62
  307. package/dist/integrations/agent/prompts.js +46 -18
  308. package/dist/integrations/agent/runner-dispatch.js +93 -4
  309. package/dist/integrations/agent/runner.js +76 -208
  310. package/dist/integrations/agent/spawn.js +88 -196
  311. package/dist/integrations/harnesses/aider/agent-builder.js +114 -0
  312. package/dist/integrations/harnesses/aider/index.js +48 -0
  313. package/dist/integrations/harnesses/aider/result-extractor.js +53 -0
  314. package/dist/integrations/harnesses/amazonq/agent-builder.js +147 -0
  315. package/dist/integrations/harnesses/amazonq/index.js +45 -0
  316. package/dist/integrations/harnesses/amazonq/result-extractor.js +48 -0
  317. package/dist/integrations/harnesses/claude/agent-builder.js +46 -8
  318. package/dist/integrations/harnesses/claude/config-import.js +1 -3
  319. package/dist/integrations/harnesses/claude/index.js +24 -35
  320. package/dist/integrations/harnesses/claude/result-extractor.js +52 -0
  321. package/dist/integrations/harnesses/claude/session-log.js +27 -75
  322. package/dist/integrations/harnesses/codex/agent-builder.js +138 -0
  323. package/dist/integrations/harnesses/codex/index.js +52 -0
  324. package/dist/integrations/harnesses/codex/result-extractor.js +73 -0
  325. package/dist/integrations/harnesses/copilot/agent-builder.js +122 -0
  326. package/dist/integrations/harnesses/copilot/index.js +48 -0
  327. package/dist/integrations/harnesses/copilot/result-extractor.js +151 -0
  328. package/dist/integrations/harnesses/gemini/agent-builder.js +120 -0
  329. package/dist/integrations/harnesses/gemini/index.js +48 -0
  330. package/dist/integrations/harnesses/gemini/result-extractor.js +121 -0
  331. package/dist/integrations/harnesses/ids.js +24 -0
  332. package/dist/integrations/harnesses/index.js +54 -34
  333. package/dist/integrations/harnesses/opencode/agent-builder.js +23 -5
  334. package/dist/integrations/harnesses/opencode/config-import.js +1 -3
  335. package/dist/integrations/harnesses/opencode/index.js +14 -32
  336. package/dist/integrations/harnesses/opencode/session-log.js +67 -125
  337. package/dist/integrations/harnesses/opencode-sdk/harness.js +51 -0
  338. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +681 -108
  339. package/dist/integrations/harnesses/openhands/agent-builder.js +128 -0
  340. package/dist/integrations/harnesses/openhands/index.js +48 -0
  341. package/dist/integrations/harnesses/openhands/result-extractor.js +103 -0
  342. package/dist/integrations/harnesses/pi/agent-builder.js +97 -0
  343. package/dist/integrations/harnesses/pi/index.js +45 -0
  344. package/dist/integrations/harnesses/pi/result-extractor.js +135 -0
  345. package/dist/integrations/harnesses/shared.js +17 -0
  346. package/dist/integrations/harnesses/types.js +43 -32
  347. package/dist/integrations/lockfile.js +211 -24
  348. package/dist/integrations/session-logs/index.js +36 -39
  349. package/dist/integrations/session-logs/provider-base.js +113 -0
  350. package/dist/llm/client.js +182 -110
  351. package/dist/llm/embedders/deterministic.js +2 -2
  352. package/dist/llm/embedders/remote.js +21 -9
  353. package/dist/llm/feature-gate.js +17 -57
  354. package/dist/llm/graph-extract.js +12 -13
  355. package/dist/llm/index-passes.js +8 -42
  356. package/dist/llm/memory-infer.js +144 -1
  357. package/dist/llm/metadata-enhance.js +45 -30
  358. package/dist/llm/structured-call.js +16 -8
  359. package/dist/llm/usage-persist.js +30 -5
  360. package/dist/llm/usage-telemetry.js +59 -6
  361. package/dist/output/cli-hints.js +1 -2
  362. package/dist/output/command-registry.js +27 -0
  363. package/dist/output/context.js +22 -7
  364. package/dist/output/format-exempt.js +80 -0
  365. package/dist/output/generic-render.js +251 -0
  366. package/dist/output/html-render.js +11 -16
  367. package/dist/output/render-registry.js +57 -0
  368. package/dist/output/renderers.js +14 -279
  369. package/dist/output/shapes/curate.js +10 -1
  370. package/dist/output/shapes/events.js +12 -7
  371. package/dist/output/shapes/helpers.js +58 -84
  372. package/dist/output/shapes/passthrough.js +11 -39
  373. package/dist/output/shapes/proposal/producer.js +15 -7
  374. package/dist/output/shapes/registry.js +12 -6
  375. package/dist/output/shapes.js +0 -9
  376. package/dist/output/text/{init.js → bundle-create.js} +3 -1
  377. package/dist/output/text/bundle-show.js +7 -0
  378. package/dist/output/text/command-format.js +562 -0
  379. package/dist/output/text/env.js +1 -3
  380. package/dist/output/text/events.js +8 -7
  381. package/dist/output/text/helpers.js +15 -1164
  382. package/dist/output/text/proposal/producer.js +4 -2
  383. package/dist/output/text/proposal-format.js +202 -0
  384. package/dist/output/text/registry-commands.js +1 -2
  385. package/dist/output/text/registry.js +12 -6
  386. package/dist/output/text/show-directives.js +117 -0
  387. package/dist/output/text/show-format.js +103 -0
  388. package/dist/output/text/sync.js +5 -0
  389. package/dist/output/text/workflow-format.js +332 -0
  390. package/dist/output/text/workflow.js +3 -2
  391. package/dist/output/text.js +10 -19
  392. package/dist/registry/factory.js +4 -6
  393. package/dist/registry/origin-resolve.js +16 -27
  394. package/dist/registry/providers/skills-sh.js +3 -3
  395. package/dist/registry/providers/static-index.js +15 -25
  396. package/dist/registry/resolve.js +43 -94
  397. package/dist/registry/semver.js +43 -0
  398. package/dist/runtime.js +81 -12
  399. package/dist/scripts/akm-migrate.js +35529 -0
  400. package/dist/setup/detect.js +5 -7
  401. package/dist/setup/detected-engines.js +136 -0
  402. package/dist/setup/engine-config.js +100 -0
  403. package/dist/setup/registry-stash-loader.js +3 -3
  404. package/dist/setup/semantic-assets.js +12 -9
  405. package/dist/setup/setup.js +444 -208
  406. package/dist/setup/steps/connection-shared.js +120 -0
  407. package/dist/setup/steps/connection.js +108 -305
  408. package/dist/setup/steps/platforms.js +13 -12
  409. package/dist/setup/steps/semantic.js +15 -3
  410. package/dist/setup/steps/sources.js +21 -15
  411. package/dist/setup/steps/stashdir.js +6 -4
  412. package/dist/setup/steps/tasks.js +236 -119
  413. package/dist/setup/steps.js +3 -2
  414. package/dist/sources/freshness.js +39 -0
  415. package/dist/sources/provider-factory.js +11 -17
  416. package/dist/sources/providers/filesystem.js +2 -3
  417. package/dist/sources/providers/git-install.js +278 -34
  418. package/dist/sources/providers/git-provider.js +54 -56
  419. package/dist/sources/providers/git-stash.js +420 -91
  420. package/dist/sources/providers/git.js +2 -2
  421. package/dist/sources/providers/npm.js +16 -19
  422. package/dist/sources/providers/provider-utils.js +47 -22
  423. package/dist/sources/providers/sync-from-ref.js +3 -9
  424. package/dist/sources/providers/website.js +2 -2
  425. package/dist/sources/resolve.js +11 -10
  426. package/dist/sources/snapshot-fetchers/types.js +4 -0
  427. package/dist/sources/{website-ingest.js → snapshot-fetchers/website-ingest.js} +110 -41
  428. package/dist/storage/database.js +60 -4
  429. package/dist/storage/engines/sqlite-migrations.js +156 -5
  430. package/dist/storage/locations.js +1 -2
  431. package/dist/storage/repositories/canaries-repository.js +1 -1
  432. package/dist/storage/repositories/events-repository.js +51 -11
  433. package/dist/storage/repositories/improve-runs-repository.js +6 -32
  434. package/dist/storage/repositories/index-connection.js +79 -0
  435. package/dist/storage/repositories/index-db.js +4 -3
  436. package/dist/storage/repositories/index-entries-repository.js +863 -0
  437. package/dist/{indexer/db/entry-mapper.js → storage/repositories/index-entry-mapper.js} +19 -2
  438. package/dist/storage/repositories/index-entry-types.js +4 -0
  439. package/dist/storage/repositories/index-fts-repository.js +167 -0
  440. package/dist/storage/repositories/index-llm-cache-repository.js +108 -0
  441. package/dist/storage/repositories/index-meta-repository.js +49 -0
  442. package/dist/{indexer/db/schema.js → storage/repositories/index-schema.js} +226 -100
  443. package/dist/storage/repositories/index-sql.js +12 -0
  444. package/dist/storage/repositories/index-utility-repository.js +356 -0
  445. package/dist/storage/repositories/index-vec-repository.js +250 -0
  446. package/dist/storage/repositories/outcome-repository.js +119 -0
  447. package/dist/storage/repositories/proposals-repository.js +317 -75
  448. package/dist/storage/repositories/registry-cache.js +1 -1
  449. package/dist/storage/repositories/salience-repository.js +172 -0
  450. package/dist/storage/repositories/task-history-repository.js +110 -3
  451. package/dist/storage/repositories/workflow-runs-repository.js +240 -19
  452. package/dist/tasks/backends/cron.js +169 -46
  453. package/dist/tasks/backends/exec-utils.js +76 -3
  454. package/dist/tasks/backends/index.js +6 -9
  455. package/dist/tasks/backends/launchd.js +292 -55
  456. package/dist/tasks/backends/schtasks.js +557 -70
  457. package/dist/tasks/backends/types.js +4 -0
  458. package/dist/tasks/command-executable.js +93 -0
  459. package/dist/tasks/embedded.js +56 -38
  460. package/dist/tasks/parser.js +156 -64
  461. package/dist/tasks/resolve-akm-bin.js +144 -51
  462. package/dist/tasks/runner.js +377 -209
  463. package/dist/tasks/schedule.js +108 -19
  464. package/dist/tasks/scheduler-invocation.js +296 -0
  465. package/dist/tasks/schema.js +1 -1
  466. package/dist/tasks/task-id.js +35 -0
  467. package/dist/tasks/validator.js +30 -16
  468. package/dist/text-import-hook.mjs +1 -1
  469. package/dist/workflows/authoring/authoring.js +104 -43
  470. package/dist/workflows/authoring/scope-key.js +1 -1
  471. package/dist/workflows/cli.js +0 -16
  472. package/dist/workflows/concurrency-policy.js +15 -0
  473. package/dist/workflows/exec/brief.js +450 -0
  474. package/dist/workflows/exec/frozen-judge.js +47 -0
  475. package/dist/workflows/exec/native-executor.js +1038 -0
  476. package/dist/workflows/exec/param-secrets.js +115 -0
  477. package/dist/workflows/exec/report.js +1460 -0
  478. package/dist/workflows/exec/run-workflow.js +602 -0
  479. package/dist/workflows/exec/scheduler.js +71 -0
  480. package/dist/workflows/exec/step-work.js +1190 -0
  481. package/dist/workflows/exec/unit-writer.js +23 -0
  482. package/dist/workflows/exec/workflow-engine-gate.js +67 -0
  483. package/dist/workflows/exec/worktree.js +171 -0
  484. package/dist/workflows/ir/compile.js +246 -0
  485. package/dist/workflows/ir/freeze.js +233 -0
  486. package/dist/workflows/ir/params.js +54 -0
  487. package/dist/workflows/ir/plan-hash.js +68 -0
  488. package/dist/workflows/ir/schema.js +540 -0
  489. package/dist/workflows/parser.js +878 -304
  490. package/dist/workflows/program/expressions.js +181 -0
  491. package/dist/workflows/program/schema.js +51 -0
  492. package/dist/workflows/renderer.js +100 -45
  493. package/dist/workflows/resource-limits.js +22 -0
  494. package/dist/workflows/runtime/agent-identity.js +59 -14
  495. package/dist/workflows/runtime/checkin.js +1 -1
  496. package/dist/workflows/runtime/plan-classifier.js +131 -0
  497. package/dist/workflows/runtime/runs.js +376 -119
  498. package/dist/workflows/runtime/unit-checkin.js +45 -0
  499. package/dist/workflows/runtime/unit-phases.js +20 -0
  500. package/dist/workflows/runtime/workflow-asset-loader.js +241 -40
  501. package/dist/workflows/schema.js +1 -11
  502. package/dist/workflows/validate-summary.js +2 -3
  503. package/dist/workflows/validator.js +52 -30
  504. package/docs/README.md +42 -78
  505. package/docs/migration/README.md +8 -0
  506. package/docs/migration/release-notes/0.6.0.md +1 -1
  507. package/docs/migration/release-notes/0.7.0.md +9 -8
  508. package/docs/migration/release-notes/0.9.0.md +158 -14
  509. package/docs/migration/v0.7-to-v0.8.md +46 -47
  510. package/docs/migration/v0.8-to-v0.9.md +844 -0
  511. package/docs/reference/README.md +12 -0
  512. package/docs/reference/data-and-telemetry.md +333 -0
  513. package/package.json +21 -17
  514. package/schemas/akm-asset-envelope.json +93 -0
  515. package/schemas/akm-config.json +4636 -0
  516. package/schemas/akm-task.json +87 -0
  517. package/schemas/akm-workflow.json +373 -0
  518. package/dist/akm-migrate-storage +0 -38
  519. package/dist/assets/help/help-accept.md +0 -12
  520. package/dist/assets/help/help-improve.md +0 -84
  521. package/dist/assets/help/help-proposals.md +0 -17
  522. package/dist/assets/help/help-propose.md +0 -17
  523. package/dist/assets/help/help-reject.md +0 -11
  524. package/dist/assets/profiles/frequent.json +0 -13
  525. package/dist/assets/profiles/recombine-only.json +0 -21
  526. package/dist/assets/profiles/reflect-distill.json +0 -30
  527. package/dist/assets/profiles/synthesize.json +0 -15
  528. package/dist/assets/prompts/procedural-system.md +0 -44
  529. package/dist/assets/prompts/recombine-system.md +0 -40
  530. package/dist/assets/prompts/staleness-detect-system.md +0 -6
  531. package/dist/assets/tasks/core/backup.yml +0 -4
  532. package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
  533. package/dist/assets/templates/html/default.html +0 -78
  534. package/dist/assets/templates/html/vendor/echarts.min.js +0 -45
  535. package/dist/assets/wiki/index-template.md +0 -12
  536. package/dist/assets/wiki/ingest-workflow-template.md +0 -83
  537. package/dist/assets/wiki/log-template.md +0 -8
  538. package/dist/assets/wiki/schema-template.md +0 -61
  539. package/dist/cli/config-migrate.js +0 -150
  540. package/dist/cli/config-validate.js +0 -39
  541. package/dist/commands/graph/graph-cli.js +0 -124
  542. package/dist/commands/graph/graph.js +0 -487
  543. package/dist/commands/improve/calibration.js +0 -161
  544. package/dist/commands/improve/dedup.js +0 -482
  545. package/dist/commands/improve/extract-watch.js +0 -140
  546. package/dist/commands/improve/hot-probation.js +0 -45
  547. package/dist/commands/improve/improve-auto-accept.js +0 -276
  548. package/dist/commands/improve/improve-profiles.js +0 -168
  549. package/dist/commands/improve/procedural.js +0 -398
  550. package/dist/commands/improve/recombine.js +0 -818
  551. package/dist/commands/improve/schema-similarity-gate.js +0 -168
  552. package/dist/commands/lint/agent-linter.js +0 -44
  553. package/dist/commands/lint/command-linter.js +0 -44
  554. package/dist/commands/lint/default-linter.js +0 -16
  555. package/dist/commands/lint/fact-linter.js +0 -39
  556. package/dist/commands/lint/knowledge-linter.js +0 -16
  557. package/dist/commands/lint/memory-linter.js +0 -61
  558. package/dist/commands/lint/registry.js +0 -41
  559. package/dist/commands/lint/skill-linter.js +0 -45
  560. package/dist/commands/lint/task-linter.js +0 -50
  561. package/dist/commands/lint/workflow-linter.js +0 -81
  562. package/dist/commands/proposal/legacy-import.js +0 -115
  563. package/dist/commands/sources/history.js +0 -196
  564. package/dist/commands/tasks/default-tasks.js +0 -186
  565. package/dist/commands/wiki-cli.js +0 -292
  566. package/dist/core/asset/asset-registry.js +0 -76
  567. package/dist/core/asset/asset-spec.js +0 -259
  568. package/dist/core/config/config-migration.js +0 -602
  569. package/dist/core/deep-merge.js +0 -38
  570. package/dist/core/eval/rank-metrics.js +0 -113
  571. package/dist/core/ripgrep/install.js +0 -163
  572. package/dist/core/ripgrep/resolve.js +0 -81
  573. package/dist/indexer/db/db.js +0 -1413
  574. package/dist/indexer/manifest.js +0 -170
  575. package/dist/indexer/passes/metadata-contributors.js +0 -31
  576. package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -94
  577. package/dist/integrations/harnesses/opencode-sdk/index.js +0 -49
  578. package/dist/llm/call-ai.js +0 -62
  579. package/dist/llm/memory-infer-impl.js +0 -138
  580. package/dist/output/shapes/distill.js +0 -14
  581. package/dist/output/shapes/history.js +0 -11
  582. package/dist/output/text/distill.js +0 -6
  583. package/dist/output/text/enable-disable.js +0 -8
  584. package/dist/output/text/history.js +0 -6
  585. package/dist/output/text/wiki.js +0 -16
  586. package/dist/registry/build-index.js +0 -386
  587. package/dist/scripts/migrate-storage.js +0 -19108
  588. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +0 -9411
  589. package/dist/scripts/migrations/v16-to-v17.js +0 -141
  590. package/dist/setup/legacy-config.js +0 -106
  591. package/dist/storage/repositories/consolidation-repository.js +0 -38
  592. package/dist/storage/repositories/recombine-repository.js +0 -213
  593. package/dist/wiki/wiki-templates.js +0 -15
  594. package/dist/wiki/wiki.js +0 -1012
  595. package/dist/workflows/db.js +0 -215
  596. package/docs/data-and-telemetry.md +0 -226
  597. /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/registry.js +0 -0
  598. /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/youtube.js +0 -0
@@ -4,29 +4,15 @@
4
4
  import crypto from "node:crypto";
5
5
  import fs from "node:fs";
6
6
  import path from "node:path";
7
- import { getAssetTypes, TYPE_DIRS } from "./asset/asset-spec.js";
8
7
  import { ConfigError } from "./errors.js";
9
8
  import { getConfigPath, getDefaultStashDir } from "./paths.js";
10
- // ── Types ───────────────────────────────────────────────────────────────────
11
- /**
12
- * The canonical catalog of built-in asset types.
13
- *
14
- * SINGLE SOURCE OF TRUTH: derived from the {@link ASSET_SPECS} registry in
15
- * `asset-spec.ts` rather than hand-maintained here. Before #490/WS7 this was a
16
- * hand-written literal array that had DRIFTED from the registry (it omitted
17
- * `task`, which the registry has always carried). Deriving from the registry
18
- * kills that drift — see `tests/asset-type-union-source.test.ts` for the
19
- * intentional-`task`-delta guard.
20
- *
21
- * Note: `AkmAssetType` stays a static literal union of the BUILT-IN types
22
- * (those present at module-eval time). Dynamically `registerAssetType`-d types
23
- * are accepted at runtime via {@link isAssetType} but are not part of the
24
- * static union — identical to the pre-WS7 contract.
25
- */
26
- export const ASSET_TYPES = Object.freeze([...getAssetTypes()]);
27
- export const ASSET_TYPE_SET = new Set(ASSET_TYPES);
28
9
  // ── Constants ───────────────────────────────────────────────────────────────
29
- export const IS_WINDOWS = process.platform === "win32";
10
+ // Moved to the platform leaf so paths.ts can use it without a common↔paths
11
+ // cycle (chunk-8 WI-8.6, DoD 11); re-exported here for the existing surface.
12
+ export { IS_WINDOWS } from "./platform.js";
13
+ export const MAX_CONFIG_FILE_BYTES = 1024 * 1024;
14
+ export const MAX_LOCAL_METADATA_BYTES = 1024 * 1024;
15
+ export const MAX_LOCK_METADATA_BYTES = 64 * 1024;
30
16
  export function isHttpUrl(value) {
31
17
  return !!value && /^https?:\/\//.test(value);
32
18
  }
@@ -44,20 +30,36 @@ export function isRemoteUrl(value) {
44
30
  value.startsWith("ssh://") ||
45
31
  value.startsWith("git://"));
46
32
  }
47
- // ── Validators ──────────────────────────────────────────────────────────────
48
- /**
49
- * Returns true if `type` is a known asset type — either a built-in from
50
- * {@link ASSET_TYPES} or one dynamically registered via `registerAssetType`.
51
- *
52
- * The type guard narrows to `AkmAssetType` for all built-in types. Dynamic
53
- * types (e.g. registered by plugins) are also accepted at runtime, but the
54
- * type system treats them as `AkmAssetType` via assertion since they are not
55
- * part of the static union.
56
- */
57
- export function isAssetType(type) {
58
- return Object.hasOwn(TYPE_DIRS, type);
59
- }
60
33
  // ── Utilities ───────────────────────────────────────────────────────────────
34
+ export function readTextFileDescriptorWithLimit(fd, maxBytes, label = "File", displayPath = "(open file)") {
35
+ const stat = fs.fstatSync(fd);
36
+ if (!stat.isFile())
37
+ throw new ConfigError(`${label} is not a regular file: ${displayPath}.`, "INVALID_CONFIG_FILE");
38
+ if (stat.size > maxBytes) {
39
+ throw new ConfigError(`${label} exceeds the ${maxBytes}-byte limit: ${displayPath}.`, "INVALID_CONFIG_FILE");
40
+ }
41
+ const buffer = Buffer.allocUnsafe(maxBytes + 1);
42
+ let total = 0;
43
+ while (total <= maxBytes) {
44
+ const bytesRead = fs.readSync(fd, buffer, total, maxBytes + 1 - total, null);
45
+ if (bytesRead === 0)
46
+ break;
47
+ total += bytesRead;
48
+ }
49
+ if (total > maxBytes) {
50
+ throw new ConfigError(`${label} exceeds the ${maxBytes}-byte limit: ${displayPath}.`, "INVALID_CONFIG_FILE");
51
+ }
52
+ return buffer.subarray(0, total).toString("utf8");
53
+ }
54
+ export function readTextFileWithLimit(filePath, maxBytes, label = "File") {
55
+ const fd = fs.openSync(filePath, "r");
56
+ try {
57
+ return readTextFileDescriptorWithLimit(fd, maxBytes, label, filePath);
58
+ }
59
+ finally {
60
+ fs.closeSync(fd);
61
+ }
62
+ }
61
63
  /**
62
64
  * Write content to a file atomically via a temp file + rename.
63
65
  * Prevents partial-write corruption on crash.
@@ -78,60 +80,105 @@ export function isAssetType(type) {
78
80
  * 2. fsync the parent directory after rename, so the directory entry change
79
81
  * is durable too. Some filesystems (FAT, certain FUSE mounts) don't
80
82
  * support directory fsync; we ignore EINVAL/ENOTSUP so atomic writes
81
- * don't fail on exotic mounts.
83
+ * don't fail on those mounts. Windows does not support opening a
84
+ * directory for fsync, so the directory-sync step is skipped there.
82
85
  */
83
86
  export function writeFileAtomic(target, content, mode) {
84
87
  const tmp = `${target}.tmp.${process.pid}.${crypto.randomBytes(8).toString("hex")}`;
85
- const fd = fs.openSync(tmp, "w", mode ?? 0o600);
88
+ const data = typeof content === "string" ? Buffer.from(content) : content;
89
+ const fileMode = mode ?? 0o600;
90
+ let fd;
91
+ let tempOwned = false;
92
+ let renamed = false;
93
+ let failed = false;
94
+ let failure;
86
95
  try {
87
- fs.writeSync(fd, typeof content === "string" ? Buffer.from(content) : content);
88
- try {
89
- fs.fdatasyncSync(fd);
90
- }
91
- catch {
92
- // Best-effort: some pseudo-filesystems lack fdatasync. Fall through
93
- // to closeSync — the rename below still preserves atomicity even if
94
- // the data isn't durable, and the calling code's retry will recover.
96
+ fd = fs.openSync(tmp, "wx", fileMode);
97
+ tempOwned = true;
98
+ if (process.platform !== "win32")
99
+ fs.fchmodSync(fd, fileMode);
100
+ let offset = 0;
101
+ while (offset < data.byteLength) {
102
+ const written = fs.writeSync(fd, data, offset, data.byteLength - offset);
103
+ if (written <= 0)
104
+ throw new Error(`Could not make progress writing atomic temp file ${tmp}.`);
105
+ offset += written;
95
106
  }
107
+ fs.fdatasyncSync(fd);
108
+ fs.closeSync(fd);
109
+ fd = undefined;
110
+ fs.renameSync(tmp, target);
111
+ renamed = true;
112
+ }
113
+ catch (error) {
114
+ failed = true;
115
+ failure = error;
96
116
  }
97
117
  finally {
98
- fs.closeSync(fd);
118
+ if (fd !== undefined) {
119
+ try {
120
+ fs.closeSync(fd);
121
+ }
122
+ catch (error) {
123
+ if (!failed) {
124
+ failed = true;
125
+ failure = error;
126
+ }
127
+ }
128
+ }
129
+ if (tempOwned && !renamed) {
130
+ try {
131
+ fs.unlinkSync(tmp);
132
+ }
133
+ catch (error) {
134
+ if (!failed && !hasErrnoCode(error, "ENOENT")) {
135
+ failed = true;
136
+ failure = error;
137
+ }
138
+ }
139
+ }
99
140
  }
100
- fs.renameSync(tmp, target);
101
- try {
102
- const dirFd = fs.openSync(path.dirname(target), "r");
141
+ if (failed)
142
+ throw failure;
143
+ if (process.platform !== "win32") {
144
+ let dirFd;
103
145
  try {
104
- fs.fsyncSync(dirFd);
146
+ dirFd = fs.openSync(path.dirname(target), "r");
147
+ }
148
+ catch (error) {
149
+ if (hasErrnoCode(error, "EINVAL") || hasErrnoCode(error, "ENOTSUP"))
150
+ return;
151
+ throw error;
152
+ }
153
+ try {
154
+ try {
155
+ fs.fsyncSync(dirFd);
156
+ }
157
+ catch (error) {
158
+ if (!hasErrnoCode(error, "EINVAL") && !hasErrnoCode(error, "ENOTSUP"))
159
+ throw error;
160
+ }
105
161
  }
106
162
  finally {
107
163
  fs.closeSync(dirFd);
108
164
  }
109
165
  }
110
- catch {
111
- // Directory fsync is unsupported on FAT, some FUSE mounts, and Windows
112
- // (where directories cannot be opened for read like POSIX). Silently
113
- // ignore so writeFileAtomic remains portable.
114
- }
115
166
  }
116
167
  /**
117
168
  * Resolve the stash directory using a three-level fallback chain:
118
- * 1. AKM_STASH_DIR environment variable (override for CI/scripts)
119
- * 2. stashDir field in config.json
169
+ * 1. AKM_BUNDLE_DIR environment variable (override for CI/scripts)
170
+ * 2. The configured default bundle path
120
171
  * 3. Platform default (~/akm or ~/Documents/akm on Windows)
121
172
  *
122
- * Pure read: never writes to disk. The legacy `readOnly` option is accepted
123
- * (and ignored) for one release cycle so older callers continue to compile;
124
- * it can be removed in the next minor bump.
125
- *
126
173
  * Throws if no valid stash directory is found.
127
174
  */
128
- export function resolveStashDir(_options, env = process.env) {
175
+ export function resolveStashDir(env = process.env) {
129
176
  // 1. Env var override (for CI, scripts, testing)
130
- const envDir = env.AKM_STASH_DIR?.trim();
177
+ const envDir = env.AKM_BUNDLE_DIR?.trim();
131
178
  if (envDir) {
132
179
  return validateStashDir(envDir);
133
180
  }
134
- // 2. Config file stashDir field
181
+ // 2. Configured default bundle path
135
182
  const configStashDir = readStashDirFromConfig();
136
183
  if (configStashDir)
137
184
  return validateStashDir(configStashDir);
@@ -140,8 +187,7 @@ export function resolveStashDir(_options, env = process.env) {
140
187
  if (isValidDirectory(defaultDir)) {
141
188
  return defaultDir;
142
189
  }
143
- throw new ConfigError(`No stash directory found. Run "akm init" to create one at ${defaultDir}, ` +
144
- `or set stashDir in ${getConfigPath()}.`, "STASH_DIR_NOT_FOUND");
190
+ throw new ConfigError(`No bundle directory found. Run "akm bundle create" to create one at ${defaultDir}.`, "STASH_DIR_NOT_FOUND");
145
191
  }
146
192
  function validateStashDir(raw) {
147
193
  const stashDir = path.resolve(raw);
@@ -150,10 +196,10 @@ function validateStashDir(raw) {
150
196
  stat = fs.statSync(stashDir);
151
197
  }
152
198
  catch {
153
- throw new ConfigError(`Unable to read stash directory at "${stashDir}".`, "STASH_DIR_UNREADABLE");
199
+ throw new ConfigError(`Unable to read bundle directory at "${stashDir}".`, "STASH_DIR_UNREADABLE");
154
200
  }
155
201
  if (!stat.isDirectory()) {
156
- throw new ConfigError(`Stash path must point to a directory: "${stashDir}".`, "STASH_DIR_NOT_A_DIRECTORY");
202
+ throw new ConfigError(`Bundle path must point to a directory: "${stashDir}".`, "STASH_DIR_NOT_A_DIRECTORY");
157
203
  }
158
204
  return stashDir;
159
205
  }
@@ -166,20 +212,75 @@ function isValidDirectory(dir) {
166
212
  }
167
213
  }
168
214
  /**
169
- * Read stashDir directly from config.json without pulling in the full config
170
- * module, to avoid circular dependencies.
215
+ * Read the primary stash path directly from config.json without pulling in the
216
+ * full config module, to avoid circular dependencies.
217
+ *
218
+ * Reads ONLY the 0.9.0 `bundles`/`defaultBundle` shape. A config still carrying
219
+ * the retired `stashDir`/`sources`/`installed` keys (with no usable bundles
220
+ * path) is an unmigrated config: this refuses it with the same `akm-migrate
221
+ * apply` hint the schema hard-reject uses (config-schema.ts), rather than
222
+ * silently honouring the retired key — so every `resolveStashDir` caller gets
223
+ * the coherent migrate posture instead of split-brain success.
171
224
  */
172
225
  function readStashDirFromConfig() {
173
226
  try {
174
227
  const configPath = getConfigPath();
175
- const text = fs.readFileSync(configPath, "utf8");
228
+ const text = readTextFileWithLimit(configPath, MAX_CONFIG_FILE_BYTES, "Config file");
176
229
  const raw = JSON.parse(text);
177
- if (typeof raw === "object" && raw !== null && typeof raw.stashDir === "string" && raw.stashDir.trim()) {
178
- return raw.stashDir.trim();
230
+ if (typeof raw !== "object" || raw === null)
231
+ return undefined;
232
+ // 0.9.0 config-shape cutover (spec §10.1): the primary stash is the
233
+ // `defaultBundle`'s filesystem `path`. Read it directly (no config module
234
+ // import) so the primary-stash location survives the stashDir → bundles
235
+ // migration without a runtime rewire.
236
+ const bundles = raw.bundles;
237
+ const defaultBundle = raw.defaultBundle;
238
+ if (bundles &&
239
+ typeof bundles === "object" &&
240
+ typeof defaultBundle === "string" &&
241
+ bundles[defaultBundle] &&
242
+ typeof bundles[defaultBundle] === "object" &&
243
+ typeof bundles[defaultBundle].path === "string" &&
244
+ bundles[defaultBundle].path.trim()) {
245
+ const bundle = bundles[defaultBundle];
246
+ const bundlePath = bundle.path.trim();
247
+ if (bundle.components !== undefined) {
248
+ if (typeof bundle.components !== "object" || bundle.components === null) {
249
+ throw new ConfigError("A bundle components map must contain exactly one component.", "INVALID_CONFIG_FILE");
250
+ }
251
+ const components = Object.values(bundle.components);
252
+ if (components.length !== 1) {
253
+ throw new ConfigError("A bundle components map must contain exactly one component.", "INVALID_CONFIG_FILE");
254
+ }
255
+ const component = components[0];
256
+ if (typeof component === "object" && component !== null) {
257
+ const componentConfig = component;
258
+ if (typeof componentConfig.root !== "string")
259
+ return bundlePath;
260
+ const bundleRoot = path.resolve(bundlePath);
261
+ const componentRoot = path.resolve(bundleRoot, componentConfig.root);
262
+ if (!isWithin(componentRoot, bundleRoot)) {
263
+ throw new ConfigError(`Component root "${componentConfig.root}" escapes bundle "${defaultBundle}".`, "INVALID_CONFIG_FILE");
264
+ }
265
+ return componentRoot;
266
+ }
267
+ }
268
+ return bundlePath;
269
+ }
270
+ // Retired pre-cutover shape with no usable bundles path: refuse with the
271
+ // migrate hint (matches the schema's hard-reject, config-schema.ts) instead
272
+ // of silently resolving the old key.
273
+ for (const key of ["stashDir", "sources", "installed"]) {
274
+ if (key in raw && raw[key] !== undefined) {
275
+ throw new ConfigError(`${key} is the retired pre-cutover source shape; run \`akm-migrate apply\` to convert it to bundles`, "INVALID_CONFIG_FILE");
276
+ }
179
277
  }
180
278
  }
181
- catch {
182
- // Config doesn't exist or is invalid — fall through
279
+ catch (err) {
280
+ // A retired-shape refusal must reach the caller; genuine missing/invalid
281
+ // config (read or JSON-parse failure) falls through to the platform default.
282
+ if (err instanceof ConfigError)
283
+ throw err;
183
284
  }
184
285
  return undefined;
185
286
  }
@@ -240,14 +341,34 @@ function normalizeFsPathForComparison(value) {
240
341
  /**
241
342
  * Fetch with an AbortController timeout.
242
343
  * Defaults to 30 seconds if no timeout is specified.
344
+ *
345
+ * SCOPE — connection + response-header phase only. `timeoutMs` bounds the time
346
+ * until `fetch` RESOLVES (i.e. until the status line + headers arrive); the
347
+ * timer is cleared once the `Response` is returned. It does NOT bound the time
348
+ * spent streaming the response BODY: a server can dribble body bytes forever
349
+ * under any per-byte limit. Callers that read the body MUST bound it
350
+ * themselves — pass `{ bodyTimeoutMs, signal }` to {@link readBodyWithByteCap}
351
+ * (in-memory reads) or use a capped streaming writer for downloads. That is the
352
+ * sanctioned "body-deadline mechanism"; a bounded header timeout here plus a
353
+ * bounded body read there gives a bounded TOTAL window.
354
+ *
355
+ * External `signal`: a caller-supplied `AbortSignal` aborts the in-flight
356
+ * request with the caller's own `reason`. The bridged listener is removed in
357
+ * `finally`, so once this function returns the caller's signal no longer
358
+ * governs the returned `Response`'s body stream — pass the SAME `signal` to the
359
+ * body-read helper so cancellation continues to apply to the body phase with
360
+ * the caller's reason. (A timeout and an external abort can never overwrite
361
+ * each other: whichever fires first aborts the controller, and a second
362
+ * `controller.abort()` is a no-op that preserves the first reason.)
243
363
  */
244
364
  export async function fetchWithTimeout(url, opts, timeoutMs = 30_000, signal) {
245
365
  const controller = new AbortController();
246
- const timer = setTimeout(() => controller.abort(), timeoutMs);
366
+ const timer = timeoutMs === null ? undefined : setTimeout(() => controller.abort(), timeoutMs);
247
367
  const abortExternal = () => controller.abort(signal?.reason);
248
368
  if (signal) {
249
369
  if (signal.aborted) {
250
- clearTimeout(timer);
370
+ if (timer)
371
+ clearTimeout(timer);
251
372
  controller.abort(signal.reason);
252
373
  }
253
374
  else {
@@ -269,7 +390,8 @@ export async function fetchWithTimeout(url, opts, timeoutMs = 30_000, signal) {
269
390
  finally {
270
391
  if (signal)
271
392
  signal.removeEventListener("abort", abortExternal);
272
- clearTimeout(timer);
393
+ if (timer)
394
+ clearTimeout(timer);
273
395
  }
274
396
  }
275
397
  /**
@@ -342,6 +464,69 @@ export class ResponseTooLargeError extends Error {
342
464
  this.observedBytes = observedBytes;
343
465
  }
344
466
  }
467
+ /**
468
+ * Thrown by {@link readBodyWithByteCap} / {@link readChunkWithDeadline} (and the
469
+ * capped disk writer) when streaming a response body exceeds the caller's
470
+ * overall body-read deadline. Distinct from a connection/header timeout
471
+ * (`fetchWithTimeout`) — this is the body-phase bound.
472
+ */
473
+ export class BodyReadTimeoutError extends Error {
474
+ url;
475
+ timeoutMs;
476
+ constructor(url, timeoutMs) {
477
+ super(`Response body read exceeded ${timeoutMs}ms: ${url}`);
478
+ this.name = "BodyReadTimeoutError";
479
+ this.url = url;
480
+ this.timeoutMs = timeoutMs;
481
+ }
482
+ }
483
+ function bodyAbortError(signal, url) {
484
+ const reason = signal?.reason;
485
+ return reason instanceof Error ? reason : new Error(`Response body read aborted: ${url}`);
486
+ }
487
+ /**
488
+ * Read one chunk from `reader`, rejecting if the overall body deadline passes
489
+ * or `signal` aborts first. `deadlineAt` is an absolute epoch-ms instant (null
490
+ * = no deadline). Only races the pending `read()` so a stalled body cannot
491
+ * block forever; the CALLER cancels the reader on rejection. Throws
492
+ * {@link BodyReadTimeoutError} on deadline, or the signal's reason (or an
493
+ * AbortError) on external abort.
494
+ */
495
+ export async function readChunkWithDeadline(reader, deadlineAt, signal, url, timeoutMs) {
496
+ if (signal?.aborted)
497
+ throw bodyAbortError(signal, url);
498
+ const remaining = deadlineAt === null ? null : deadlineAt - Date.now();
499
+ if (remaining !== null && remaining <= 0)
500
+ throw new BodyReadTimeoutError(url, timeoutMs);
501
+ if (remaining === null && !signal)
502
+ return reader.read();
503
+ return new Promise((resolve, reject) => {
504
+ let timer;
505
+ const cleanup = () => {
506
+ if (timer)
507
+ clearTimeout(timer);
508
+ signal?.removeEventListener("abort", onAbort);
509
+ };
510
+ const onAbort = () => {
511
+ cleanup();
512
+ reject(bodyAbortError(signal, url));
513
+ };
514
+ if (remaining !== null) {
515
+ timer = setTimeout(() => {
516
+ cleanup();
517
+ reject(new BodyReadTimeoutError(url, timeoutMs));
518
+ }, remaining);
519
+ }
520
+ signal?.addEventListener("abort", onAbort, { once: true });
521
+ reader.read().then((result) => {
522
+ cleanup();
523
+ resolve(result);
524
+ }, (err) => {
525
+ cleanup();
526
+ reject(err);
527
+ });
528
+ });
529
+ }
345
530
  /**
346
531
  * Read a Response body as a UTF-8 string with a byte-count cap.
347
532
  *
@@ -350,9 +535,12 @@ export class ResponseTooLargeError extends Error {
350
535
  * `Content-Length` larger than the cap, we refuse before reading any
351
536
  * bytes. `response.body` is consumed and cancelled on cap breach.
352
537
  *
353
- * `maxBytes` defaults to {@link DEFAULT_RESPONSE_BYTE_CAP} (10 MB).
538
+ * `maxBytes` defaults to {@link DEFAULT_RESPONSE_BYTE_CAP} (10 MB). `limits`
539
+ * bounds the body PHASE (duration + caller abort) that `fetchWithTimeout` does
540
+ * not cover; pass the same `signal` you gave `fetchWithTimeout` so caller
541
+ * cancellation keeps applying while the body streams.
354
542
  */
355
- export async function readBodyWithByteCap(response, maxBytes = DEFAULT_RESPONSE_BYTE_CAP) {
543
+ export async function readBodyWithByteCap(response, maxBytes = DEFAULT_RESPONSE_BYTE_CAP, limits) {
356
544
  const url = response.url || "(unknown URL)";
357
545
  const contentLengthHeader = response.headers.get("content-length");
358
546
  if (contentLengthHeader) {
@@ -368,28 +556,35 @@ export async function readBodyWithByteCap(response, maxBytes = DEFAULT_RESPONSE_
368
556
  // No streaming body available (e.g., some mock environments). Fall
369
557
  // back to text() but still enforce the cap post-hoc.
370
558
  const text = await response.text();
371
- if (text.length > maxBytes)
372
- throw new ResponseTooLargeError(url, maxBytes, text.length);
559
+ const byteLength = Buffer.byteLength(text, "utf8");
560
+ if (byteLength > maxBytes)
561
+ throw new ResponseTooLargeError(url, maxBytes, byteLength);
373
562
  return text;
374
563
  }
375
564
  const reader = body.getReader();
376
565
  const chunks = [];
377
566
  let total = 0;
567
+ const bodyTimeoutMs = limits?.bodyTimeoutMs;
568
+ const deadlineAt = bodyTimeoutMs != null ? Date.now() + bodyTimeoutMs : null;
378
569
  try {
379
570
  while (true) {
380
- const { done, value } = await reader.read();
571
+ const { done, value } = await readChunkWithDeadline(reader, deadlineAt, limits?.signal, url, bodyTimeoutMs ?? 0);
381
572
  if (done)
382
573
  break;
383
574
  if (!value)
384
575
  continue;
385
576
  total += value.byteLength;
386
- if (total > maxBytes) {
387
- await reader.cancel().catch(() => undefined);
577
+ if (total > maxBytes)
388
578
  throw new ResponseTooLargeError(url, maxBytes, total);
389
- }
390
579
  chunks.push(value);
391
580
  }
392
581
  }
582
+ catch (err) {
583
+ // Cancel the underlying stream on ANY failure (cap breach, body-read
584
+ // timeout, or caller abort) so the socket is released, not just on cap.
585
+ await reader.cancel().catch(() => undefined);
586
+ throw err;
587
+ }
393
588
  finally {
394
589
  reader.releaseLock?.();
395
590
  }
@@ -410,8 +605,8 @@ export async function readBodyWithByteCap(response, maxBytes = DEFAULT_RESPONSE_
410
605
  * around {@link readBodyWithByteCap}; prefer this for registry index
411
606
  * fetches, GitHub API responses, and any other untrusted JSON source.
412
607
  */
413
- export async function jsonWithByteCap(response, maxBytes = DEFAULT_RESPONSE_BYTE_CAP) {
414
- const text = await readBodyWithByteCap(response, maxBytes);
608
+ export async function jsonWithByteCap(response, maxBytes = DEFAULT_RESPONSE_BYTE_CAP, limits) {
609
+ const text = await readBodyWithByteCap(response, maxBytes, limits);
415
610
  return JSON.parse(text);
416
611
  }
417
612
  function parseRetryAfter(response) {