akm-cli 0.9.0-rc.8 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (450) hide show
  1. package/CHANGELOG.md +1063 -44
  2. package/README.md +51 -25
  3. package/SECURITY.md +14 -1
  4. package/STABILITY.md +497 -0
  5. package/dist/akm +148 -35
  6. package/dist/{akm-migrate-storage → akm-migrate} +6 -9
  7. package/dist/assets/hints/cli-hints-full.md +223 -95
  8. package/dist/assets/hints/cli-hints-short.md +85 -22
  9. package/dist/assets/improve-strategies/default.json +1 -1
  10. package/dist/assets/improve-strategies/reflect-distill.json +1 -1
  11. package/dist/assets/prompts/memory-infer-user.md +2 -3
  12. package/dist/assets/stash-skeleton/README.md +6 -5
  13. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +2 -0
  14. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +2 -0
  15. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +2 -0
  16. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +2 -0
  17. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +2 -0
  18. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +2 -0
  19. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +2 -0
  20. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +2 -0
  21. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +2 -0
  22. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +2 -0
  23. package/dist/assets/stash-skeleton/facts/conventions/domains.md +2 -0
  24. package/dist/assets/stash-skeleton/facts/conventions/organization.md +20 -9
  25. package/dist/assets/tasks/core/extract.yml +1 -1
  26. package/dist/assets/tasks/core/version-check.yml +1 -1
  27. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +5 -0
  28. package/dist/assets/tasks/improve/akm-improve-catchup.yml +8 -0
  29. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +5 -0
  30. package/dist/assets/tasks/improve/akm-improve-frequent.yml +5 -0
  31. package/dist/assets/tasks/improve/akm-improve-nightly.yml +5 -0
  32. package/dist/assets/templates/html/health.html +1 -3
  33. package/dist/assets/workflows/workflow-template.md +32 -15
  34. package/dist/cli/invocation.js +40 -15
  35. package/dist/cli/parse-args.js +0 -22
  36. package/dist/cli/retired-commands.js +121 -0
  37. package/dist/cli/shared.js +154 -22
  38. package/dist/cli/unknown-flags.js +236 -0
  39. package/dist/cli-node.mjs +2 -1
  40. package/dist/cli.js +696 -258
  41. package/dist/commands/agent/agent-dispatch.js +14 -3
  42. package/dist/commands/agent/contribute-cli.js +73 -88
  43. package/dist/commands/completions.js +79 -22
  44. package/dist/commands/config-cli.js +17 -150
  45. package/dist/commands/env/env-cli.js +59 -143
  46. package/dist/commands/env/env.js +12 -163
  47. package/dist/commands/env/marker-path.js +6 -0
  48. package/dist/commands/env/secret-cli.js +36 -66
  49. package/dist/commands/env/secret.js +24 -57
  50. package/dist/commands/feedback-cli.js +141 -87
  51. package/dist/commands/health/accept-rate.js +58 -0
  52. package/dist/commands/health/advisories.js +3 -4
  53. package/dist/commands/health/checks.js +85 -23
  54. package/dist/commands/health/html-report.js +7 -10
  55. package/dist/commands/health/improve-metrics.js +25 -83
  56. package/dist/commands/health/md-report.js +5 -9
  57. package/dist/commands/health/metrics.js +62 -20
  58. package/dist/commands/health/renderers.js +47 -0
  59. package/dist/commands/health/report-view-model.js +4 -5
  60. package/dist/commands/health/stash-exposure.js +1 -1
  61. package/dist/commands/health/surfaces.js +3 -48
  62. package/dist/commands/health/task-runs.js +3 -67
  63. package/dist/commands/health/types-improve.js +7 -0
  64. package/dist/commands/health.js +99 -28
  65. package/dist/commands/improve/anti-collapse.js +2 -2
  66. package/dist/commands/improve/autonomy-gate.js +68 -0
  67. package/dist/commands/improve/collapse-detector.js +41 -40
  68. package/dist/commands/improve/consolidate/eligibility.js +1 -23
  69. package/dist/commands/improve/consolidate/merge.js +4 -0
  70. package/dist/commands/improve/consolidate.js +140 -1000
  71. package/dist/commands/improve/distill/promote-memory.js +12 -12
  72. package/dist/commands/improve/distill/quality-gate.js +6 -6
  73. package/dist/commands/improve/distill.js +58 -69
  74. package/dist/commands/improve/eligibility.js +105 -57
  75. package/dist/commands/improve/extract-cli.js +14 -133
  76. package/dist/commands/improve/improve-cli.js +98 -114
  77. package/dist/commands/improve/improve-result-file.js +1 -28
  78. package/dist/commands/improve/improve-strategies.js +8 -5
  79. package/dist/commands/improve/improve.js +128 -91
  80. package/dist/commands/improve/loop-stages.js +182 -20
  81. package/dist/commands/improve/memory/derived-ref.js +45 -43
  82. package/dist/commands/improve/memory/memory-belief.js +1 -1
  83. package/dist/commands/improve/memory/memory-contradiction-detect.js +4 -12
  84. package/dist/commands/improve/memory/memory-improve.js +6 -5
  85. package/dist/commands/improve/outcome-loop.js +22 -65
  86. package/dist/commands/improve/preparation.js +114 -123
  87. package/dist/commands/improve/proactive-maintenance.js +2 -5
  88. package/dist/commands/improve/reflect.js +56 -160
  89. package/dist/commands/improve/salience.js +11 -122
  90. package/dist/commands/improve/source-identity.js +10 -38
  91. package/dist/commands/lint/base-linter.js +20 -124
  92. package/dist/commands/lint/env-key-rules.js +31 -47
  93. package/dist/commands/lint/index.js +249 -43
  94. package/dist/commands/{events.js → log.js} +33 -38
  95. package/dist/commands/migrate-cli.js +92 -12
  96. package/dist/commands/migration-tool.js +46 -0
  97. package/dist/commands/observability-cli.js +70 -209
  98. package/dist/commands/proposal/drain.js +101 -29
  99. package/dist/commands/proposal/proposal-cli.js +76 -48
  100. package/dist/commands/proposal/proposal.js +54 -18
  101. package/dist/commands/proposal/propose-cli.js +88 -0
  102. package/dist/commands/proposal/propose.js +23 -15
  103. package/dist/commands/proposal/repository.js +701 -278
  104. package/dist/commands/proposal/validators/proposal-quality-validators.js +2 -8
  105. package/dist/commands/proposal/validators/proposal-validators.js +55 -7
  106. package/dist/commands/proposal/validators/proposals.js +4 -7
  107. package/dist/commands/read/curate.js +34 -53
  108. package/dist/commands/read/knowledge.js +150 -95
  109. package/dist/commands/read/registry-search.js +2 -2
  110. package/dist/commands/read/remember-cli.js +42 -15
  111. package/dist/commands/read/search-cli.js +180 -78
  112. package/dist/commands/read/search.js +58 -43
  113. package/dist/commands/read/show.js +197 -141
  114. package/dist/commands/registry-cli.js +12 -51
  115. package/dist/commands/remember.js +14 -57
  116. package/dist/commands/sources/add-cli.js +100 -31
  117. package/dist/commands/sources/bundle-cli.js +166 -0
  118. package/dist/commands/sources/bundle-config-ops.js +7 -2
  119. package/dist/commands/sources/info.js +18 -5
  120. package/dist/commands/sources/init.js +12 -12
  121. package/dist/commands/sources/installed-stashes.js +382 -98
  122. package/dist/commands/sources/schema-repair.js +3 -2
  123. package/dist/commands/sources/self-update.js +131 -38
  124. package/dist/commands/sources/source-add.js +72 -17
  125. package/dist/commands/sources/source-clone.js +129 -45
  126. package/dist/commands/sources/source-manage.js +43 -23
  127. package/dist/commands/sources/sources-cli.js +57 -208
  128. package/dist/commands/sources/stash-cli.js +46 -53
  129. package/dist/commands/tasks/tasks-cli.js +91 -97
  130. package/dist/commands/tasks/tasks.js +276 -421
  131. package/dist/commands/workflow-cli.js +175 -450
  132. package/dist/core/adapter/adapters/akm-adapter.js +47 -28
  133. package/dist/core/adapter/adapters/akm-lint.js +42 -27
  134. package/dist/core/adapter/adapters/akm-metadata.js +15 -44
  135. package/dist/core/adapter/adapters/akm-task-adapter.js +15 -13
  136. package/dist/core/adapter/adapters/akm-workflow-adapter.js +55 -71
  137. package/dist/core/adapter/adapters/dotenv-adapter.js +1 -1
  138. package/dist/core/adapter/adapters/generic-files-adapter.js +2 -0
  139. package/dist/core/adapter/adapters/index.js +6 -6
  140. package/dist/core/adapter/adapters/llm-wiki-adapter.js +14 -8
  141. package/dist/core/adapter/adapters/okf-adapter.js +187 -19
  142. package/dist/core/adapter/adapters/shared.js +3 -19
  143. package/dist/core/adapter/adapters/tool-dir-shared.js +8 -3
  144. package/dist/core/adapter/adapters/website-snapshot-adapter.js +1 -0
  145. package/dist/core/adapter/detect-adapter.js +17 -0
  146. package/dist/core/adapter/recognize-match.js +6 -4
  147. package/dist/core/adapter/validate-context.js +214 -0
  148. package/dist/core/asset/akm-markdown.js +63 -0
  149. package/dist/core/asset/asset-placement.js +20 -6
  150. package/dist/core/asset/asset-ref.js +11 -9
  151. package/dist/core/asset/frontmatter-lint.js +30 -0
  152. package/dist/core/asset/frontmatter.js +37 -9
  153. package/dist/core/asset/markdown.js +40 -51
  154. package/dist/core/asset/resolve-ref.js +89 -18
  155. package/dist/core/asset/stash-meta.js +1 -1
  156. package/dist/core/bundle-id.js +51 -0
  157. package/dist/core/common.js +152 -38
  158. package/dist/core/config/config-io.js +12 -1
  159. package/dist/core/config/config-schema.js +35 -8
  160. package/dist/core/config/config-sources.js +55 -11
  161. package/dist/core/config/config-walker.js +25 -9
  162. package/dist/core/config/config.js +9 -48
  163. package/dist/core/config/experimental.js +21 -0
  164. package/dist/core/config/schema/embedding.js +5 -1
  165. package/dist/core/config/schema/experimental.js +30 -0
  166. package/dist/core/config/schema/improve-processes.js +0 -6
  167. package/dist/core/config/schema/improve.js +21 -3
  168. package/dist/core/config/schema/index-config.js +8 -15
  169. package/dist/core/config/schema/output.js +4 -1
  170. package/dist/core/config/schema/setup.js +9 -18
  171. package/dist/core/config/schema/sources-bundles.js +49 -33
  172. package/dist/core/config/schema/workflow.js +3 -3
  173. package/dist/core/env-secret-ref.js +76 -46
  174. package/dist/core/errors.js +18 -12
  175. package/dist/core/events.js +46 -128
  176. package/dist/core/file-change.js +6 -5
  177. package/dist/core/fs-txn.js +83 -7
  178. package/dist/core/git-message.js +2 -2
  179. package/dist/core/improve-result.js +1 -100
  180. package/dist/core/lesson-lint.js +1 -17
  181. package/dist/core/logs-db.js +2 -1
  182. package/dist/core/migration-operation.js +16 -0
  183. package/dist/core/mutation-target.js +78 -0
  184. package/dist/core/parse.js +4 -1
  185. package/dist/core/paths.js +17 -20
  186. package/dist/core/recognition-util.js +12 -14
  187. package/dist/core/redaction.js +34 -0
  188. package/dist/core/standards/resolve-standards-context.js +2 -14
  189. package/dist/core/standards/resolve-stash-standards.js +2 -2
  190. package/dist/core/standards/resolve-type-conventions.js +2 -2
  191. package/dist/core/state/migrations.js +41 -18
  192. package/dist/core/state-db.js +5 -14
  193. package/dist/core/structured.js +1 -1
  194. package/dist/core/subprocess.js +6 -4
  195. package/dist/core/text-truncation.js +9 -5
  196. package/dist/core/type-presentation.js +3 -3
  197. package/dist/core/warn.js +0 -3
  198. package/dist/core/write-source.js +771 -95
  199. package/dist/indexer/bundle-identity-guard.js +3 -2
  200. package/dist/indexer/db/graph-db.js +0 -24
  201. package/dist/indexer/ensure-index.js +1 -0
  202. package/dist/indexer/graph/graph-boost.js +9 -34
  203. package/dist/indexer/graph/graph-extraction.js +8 -5
  204. package/dist/indexer/index-writer-lock.js +53 -17
  205. package/dist/indexer/index-written-assets.js +16 -22
  206. package/dist/indexer/indexer.js +497 -239
  207. package/dist/indexer/installations.js +14 -96
  208. package/dist/indexer/passes/dir-staleness.js +16 -9
  209. package/dist/indexer/passes/memory-inference.js +11 -9
  210. package/dist/indexer/passes/metadata.js +113 -47
  211. package/dist/indexer/scan/doc-to-entry.js +38 -1
  212. package/dist/indexer/scan/drain-dir.js +13 -23
  213. package/dist/indexer/search/db-search.js +99 -54
  214. package/dist/indexer/search/fts-query.js +47 -24
  215. package/dist/indexer/search/ranking-contributors.js +42 -20
  216. package/dist/indexer/search/ranking.js +18 -99
  217. package/dist/indexer/search/search-fields.js +7 -2
  218. package/dist/indexer/search/search-source.js +82 -93
  219. package/dist/indexer/usage/usage-events.js +0 -89
  220. package/dist/indexer/walk/file-context.js +2 -1
  221. package/dist/indexer/walk/matchers.js +30 -43
  222. package/dist/indexer/walk/path-resolver.js +7 -2
  223. package/dist/indexer/walk/walker.js +38 -12
  224. package/dist/integrations/agent/builders.js +0 -6
  225. package/dist/integrations/agent/config.js +2 -2
  226. package/dist/integrations/agent/detect.js +49 -19
  227. package/dist/integrations/agent/engine-fallback.js +76 -0
  228. package/dist/integrations/agent/profiles.js +14 -0
  229. package/dist/integrations/agent/prompts.js +12 -8
  230. package/dist/integrations/agent/runner-dispatch.js +4 -2
  231. package/dist/integrations/agent/runner.js +0 -1
  232. package/dist/integrations/agent/spawn.js +5 -6
  233. package/dist/integrations/github.js +1 -1
  234. package/dist/integrations/harnesses/aider/agent-builder.js +6 -4
  235. package/dist/integrations/harnesses/amazonq/agent-builder.js +7 -4
  236. package/dist/integrations/harnesses/claude/session-log.js +0 -10
  237. package/dist/integrations/harnesses/codex/agent-builder.js +5 -2
  238. package/dist/integrations/harnesses/copilot/agent-builder.js +5 -3
  239. package/dist/integrations/harnesses/gemini/agent-builder.js +5 -3
  240. package/dist/integrations/harnesses/index.js +3 -7
  241. package/dist/integrations/harnesses/opencode/agent-builder.js +21 -2
  242. package/dist/integrations/harnesses/opencode/session-log.js +0 -15
  243. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +13 -4
  244. package/dist/integrations/harnesses/openhands/agent-builder.js +9 -6
  245. package/dist/integrations/harnesses/pi/agent-builder.js +6 -4
  246. package/dist/integrations/lockfile.js +101 -6
  247. package/dist/integrations/session-logs/index.js +3 -28
  248. package/dist/llm/client.js +136 -100
  249. package/dist/llm/embedders/remote.js +13 -5
  250. package/dist/llm/feature-gate.js +4 -12
  251. package/dist/llm/graph-extract.js +5 -11
  252. package/dist/llm/memory-infer.js +144 -1
  253. package/dist/llm/metadata-enhance.js +5 -7
  254. package/dist/llm/structured-call.js +1 -1
  255. package/dist/llm/usage-persist.js +26 -5
  256. package/dist/llm/usage-telemetry.js +25 -2
  257. package/dist/output/cli-hints.js +1 -2
  258. package/dist/output/context.js +22 -7
  259. package/dist/output/format-exempt.js +80 -0
  260. package/dist/output/generic-render.js +259 -0
  261. package/dist/output/render-registry.js +57 -0
  262. package/dist/output/renderers.js +14 -36
  263. package/dist/output/shapes/curate.js +10 -1
  264. package/dist/output/shapes/events.js +12 -7
  265. package/dist/output/shapes/helpers.js +56 -83
  266. package/dist/output/shapes/migrate.js +8 -0
  267. package/dist/output/shapes/passthrough.js +7 -41
  268. package/dist/output/shapes/proposal/producer.js +15 -7
  269. package/dist/output/shapes.js +2 -9
  270. package/dist/output/text/{init.js → bundle-create.js} +3 -1
  271. package/dist/output/text/bundle-show.js +7 -0
  272. package/dist/output/text/command-format.js +164 -96
  273. package/dist/output/text/env.js +1 -3
  274. package/dist/output/text/events.js +8 -7
  275. package/dist/output/text/health-format.js +103 -0
  276. package/dist/output/text/health.js +7 -0
  277. package/dist/output/text/helpers.js +10 -8
  278. package/dist/output/text/lint-format.js +43 -0
  279. package/dist/output/text/{save.js → lint.js} +2 -2
  280. package/dist/output/text/migrate.js +88 -0
  281. package/dist/output/text/proposal/producer.js +4 -2
  282. package/dist/output/text/proposal-format.js +44 -72
  283. package/dist/output/text/registry-commands.js +1 -2
  284. package/dist/output/text/show-directives.js +15 -7
  285. package/dist/output/text/status-list.js +32 -0
  286. package/dist/output/text/sync.js +5 -0
  287. package/dist/output/text/workflow-format.js +24 -203
  288. package/dist/output/text/workflow.js +1 -7
  289. package/dist/output/text.js +16 -17
  290. package/dist/registry/factory.js +4 -6
  291. package/dist/registry/origin-resolve.js +16 -27
  292. package/dist/registry/providers/skills-sh.js +3 -3
  293. package/dist/registry/providers/static-index.js +13 -23
  294. package/dist/registry/resolve.js +42 -7
  295. package/dist/registry/semver.js +34 -84
  296. package/dist/runtime.js +2 -23
  297. package/dist/scripts/akm-migrate-node.js +60290 -0
  298. package/dist/scripts/akm-migrate.js +59628 -0
  299. package/dist/setup/detect.js +42 -15
  300. package/dist/setup/registry-stash-loader.js +2 -2
  301. package/dist/setup/setup.js +236 -136
  302. package/dist/setup/steps/connection.js +7 -9
  303. package/dist/setup/steps/platforms.js +9 -9
  304. package/dist/setup/steps/semantic.js +15 -3
  305. package/dist/setup/steps/sources.js +12 -13
  306. package/dist/setup/steps/stashdir.js +2 -3
  307. package/dist/setup/steps/tasks.js +237 -120
  308. package/dist/sources/freshness.js +1 -1
  309. package/dist/sources/provider-factory.js +11 -17
  310. package/dist/sources/providers/filesystem.js +2 -3
  311. package/dist/sources/providers/git-install.js +278 -34
  312. package/dist/sources/providers/git-provider.js +25 -23
  313. package/dist/sources/providers/git-stash.js +395 -106
  314. package/dist/sources/providers/git.js +2 -2
  315. package/dist/sources/providers/npm.js +16 -19
  316. package/dist/sources/providers/provider-utils.js +7 -4
  317. package/dist/sources/providers/sync-from-ref.js +3 -9
  318. package/dist/sources/providers/website.js +6 -1
  319. package/dist/sources/resolve.js +6 -5
  320. package/dist/sources/snapshot-fetchers/bluesky.js +146 -0
  321. package/dist/sources/snapshot-fetchers/content-extract.js +566 -0
  322. package/dist/sources/snapshot-fetchers/fetcher-util.js +41 -0
  323. package/dist/sources/snapshot-fetchers/github.js +100 -0
  324. package/dist/sources/snapshot-fetchers/host-guard.js +291 -0
  325. package/dist/sources/snapshot-fetchers/registry.js +17 -1
  326. package/dist/sources/snapshot-fetchers/robots.js +348 -0
  327. package/dist/sources/snapshot-fetchers/rss.js +282 -0
  328. package/dist/sources/snapshot-fetchers/secret-seam.js +42 -0
  329. package/dist/sources/snapshot-fetchers/website-ingest.js +566 -268
  330. package/dist/sources/snapshot-fetchers/x.js +910 -0
  331. package/dist/storage/database.js +7 -0
  332. package/dist/storage/engines/sqlite-migrations.js +23 -111
  333. package/dist/storage/managed-db.js +2 -2
  334. package/dist/storage/repositories/canaries-repository.js +1 -1
  335. package/dist/storage/repositories/events-repository.js +27 -11
  336. package/dist/storage/repositories/improve-runs-repository.js +6 -12
  337. package/dist/storage/repositories/index-connection.js +17 -6
  338. package/dist/storage/repositories/index-entries-repository.js +151 -240
  339. package/dist/storage/repositories/index-entry-mapper.js +15 -11
  340. package/dist/storage/repositories/index-fts-repository.js +5 -2
  341. package/dist/storage/repositories/index-llm-cache-repository.js +0 -1
  342. package/dist/storage/repositories/index-meta-repository.js +2 -3
  343. package/dist/storage/repositories/index-schema.js +10 -25
  344. package/dist/storage/repositories/index-utility-repository.js +15 -28
  345. package/dist/storage/repositories/index-vec-repository.js +6 -1
  346. package/dist/storage/repositories/outcome-repository.js +119 -0
  347. package/dist/storage/repositories/proposals-repository.js +296 -59
  348. package/dist/storage/repositories/registry-cache.js +19 -0
  349. package/dist/storage/repositories/salience-repository.js +172 -0
  350. package/dist/storage/repositories/task-history-repository.js +15 -13
  351. package/dist/storage/repositories/workflow-runs-repository.js +52 -40
  352. package/dist/tasks/backends/cron.js +105 -15
  353. package/dist/tasks/backends/index.js +1 -1
  354. package/dist/tasks/backends/launchd.js +85 -38
  355. package/dist/tasks/backends/schtasks.js +135 -15
  356. package/dist/tasks/embedded.js +56 -40
  357. package/dist/tasks/parser.js +7 -157
  358. package/dist/tasks/resolve-akm-bin.js +137 -59
  359. package/dist/tasks/runner.js +79 -42
  360. package/dist/tasks/scheduler-invocation.js +220 -10
  361. package/dist/tasks/schema.js +24 -1
  362. package/dist/tasks/task-id.js +1 -3
  363. package/dist/tasks/validator.js +20 -6
  364. package/dist/workflows/authoring/authoring.js +94 -143
  365. package/dist/workflows/authoring/scope-key.js +1 -1
  366. package/dist/workflows/exec/frozen-judge.js +28 -2
  367. package/dist/workflows/exec/native-executor.js +77 -57
  368. package/dist/workflows/exec/param-secrets.js +9 -9
  369. package/dist/workflows/exec/run-workflow.js +133 -79
  370. package/dist/workflows/exec/step-work.js +219 -346
  371. package/dist/{migrate-storage-node.mjs → workflows/exec/unit-dispatch.js} +1 -5
  372. package/dist/workflows/ir/compile.js +141 -270
  373. package/dist/workflows/ir/freeze.js +40 -30
  374. package/dist/workflows/ir/params.js +135 -11
  375. package/dist/workflows/ir/plan-hash.js +1 -1
  376. package/dist/workflows/ir/schema.js +25 -26
  377. package/dist/workflows/parser.js +872 -307
  378. package/dist/workflows/program/expressions.js +20 -208
  379. package/dist/workflows/program/schema.js +7 -10
  380. package/dist/workflows/renderer.js +95 -68
  381. package/dist/workflows/resource-limits.js +2 -0
  382. package/dist/workflows/runtime/checkin.js +3 -3
  383. package/dist/workflows/runtime/plan-classifier.js +16 -75
  384. package/dist/workflows/runtime/runs.js +186 -127
  385. package/dist/workflows/runtime/unit-checkin.js +1 -1
  386. package/dist/workflows/runtime/unit-phases.js +2 -2
  387. package/dist/workflows/runtime/workflow-asset-loader.js +232 -83
  388. package/dist/workflows/schema.js +1 -11
  389. package/dist/workflows/validate-summary.js +30 -36
  390. package/dist/workflows/validator.js +21 -62
  391. package/docs/README.md +68 -0
  392. package/docs/migration/README.md +8 -0
  393. package/docs/migration/release-notes/0.7.0.md +11 -11
  394. package/docs/migration/release-notes/0.9.0.md +208 -27
  395. package/docs/migration/v0.7-to-v0.8.md +46 -47
  396. package/docs/migration/v0.8-to-v0.9.md +564 -208
  397. package/docs/migration/v0.9.0-troubleshooting.md +561 -0
  398. package/docs/reference/README.md +12 -0
  399. package/docs/reference/cli.md +2253 -0
  400. package/docs/reference/configuration.md +358 -0
  401. package/docs/reference/data-and-telemetry.md +105 -42
  402. package/docs/reference/workflows.md +647 -0
  403. package/package.json +22 -11
  404. package/schemas/akm-asset-envelope.json +93 -0
  405. package/schemas/akm-config.json +81 -128
  406. package/schemas/akm-workflow.json +74 -73
  407. package/dist/assets/tasks/core/backup.yml +0 -5
  408. package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
  409. package/dist/cli/config-migrate.js +0 -1806
  410. package/dist/cli/config-validate.js +0 -41
  411. package/dist/commands/backup-cli.js +0 -56
  412. package/dist/commands/bundle/bundle-cli.js +0 -68
  413. package/dist/commands/bundle/bundle.js +0 -219
  414. package/dist/commands/graph/graph-cli.js +0 -124
  415. package/dist/commands/graph/graph.js +0 -489
  416. package/dist/commands/improve/extract-watch.js +0 -140
  417. package/dist/commands/mv-cli.js +0 -1221
  418. package/dist/commands/sources/history.js +0 -201
  419. package/dist/commands/tasks/default-tasks.js +0 -186
  420. package/dist/core/migration-backup.js +0 -1234
  421. package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -95
  422. package/dist/llm/memory-infer-impl.js +0 -138
  423. package/dist/migrate/legacy/config-source-migration.js +0 -223
  424. package/dist/migrate/legacy/content-migration.js +0 -305
  425. package/dist/migrate/legacy/legacy-layout.js +0 -779
  426. package/dist/migrate/legacy/legacy-paths.js +0 -25
  427. package/dist/migrate/legacy/legacy-stash-json.js +0 -72
  428. package/dist/migrate/legacy/proposal-fs-import.js +0 -168
  429. package/dist/migrate/legacy/task-target-ref-migration.js +0 -272
  430. package/dist/migrate/legacy/three-db-cutover.js +0 -841
  431. package/dist/migrate/legacy/workflow-migrations-bodies.js +0 -52
  432. package/dist/migrate/legacy/workflow-migrations-frozen.js +0 -21
  433. package/dist/migrate/legacy-ref-grammar.js +0 -214
  434. package/dist/output/shapes/distill.js +0 -14
  435. package/dist/output/shapes/history.js +0 -11
  436. package/dist/output/text/distill.js +0 -6
  437. package/dist/output/text/enable-disable.js +0 -8
  438. package/dist/output/text/history.js +0 -6
  439. package/dist/registry/build-index.js +0 -382
  440. package/dist/schemas/akm-config.json +0 -4704
  441. package/dist/schemas/akm-task.json +0 -87
  442. package/dist/schemas/akm-workflow.json +0 -372
  443. package/dist/scripts/migrate-storage.js +0 -3816
  444. package/dist/workflows/authoring/workflow-program-template.yaml +0 -31
  445. package/dist/workflows/cli.js +0 -53
  446. package/dist/workflows/exec/brief.js +0 -481
  447. package/dist/workflows/exec/report.js +0 -1460
  448. package/dist/workflows/exec/watch.js +0 -116
  449. package/dist/workflows/program/parser.js +0 -813
  450. package/dist/workflows/program/project.js +0 -104
@@ -19,10 +19,11 @@ import { parseFrontmatter } from "../../core/asset/frontmatter.js";
19
19
  import { parseRefInput } from "../../core/asset/resolve-ref.js";
20
20
  import { authoringRulesForType } from "../../core/authoring-rules.js";
21
21
  import { appendEvent, readEvents } from "../../core/events.js";
22
+ import { parseEmbeddedJsonResponse } from "../../core/parse.js";
22
23
  import { resolveStandardsContext } from "../../core/standards/resolve-standards-context.js";
23
24
  import { info } from "../../core/warn.js";
24
25
  import { resolveAssetPath } from "../../indexer/walk/path-resolver.js";
25
- import { chatCompletion, parseEmbeddedJsonResponse } from "../../llm/client.js";
26
+ import { chatCompletion } from "../../llm/client.js";
26
27
  import { createProposal, isProposalSkipped } from "../proposal/repository.js";
27
28
  // ── Constants ────────────────────────────────────────────────────────────────
28
29
  /** Minimum gap between schema-repair attempts on the same asset. */
@@ -55,7 +56,7 @@ export async function runSchemaRepairPass(failures, options) {
55
56
  // Cooldown: skip repair if we ran it successfully recently.
56
57
  const recentRepairs = readEvents({ type: "schema_repair_invoked", ref: failure.ref });
57
58
  const lastRepair = recentRepairs.events
58
- .filter((e) => e.metadata?.outcome === "written")
59
+ .filter((e) => e.metadata?.outcome === "queued")
59
60
  .sort((a, b) => new Date(b.ts ?? 0).getTime() - new Date(a.ts ?? 0).getTime())[0];
60
61
  if (lastRepair?.ts && Date.now() - new Date(lastRepair.ts).getTime() < SCHEMA_REPAIR_COOLDOWN_MS) {
61
62
  repairs.push({ ref: failure.ref, reason: failure.reason, outcome: "skipped" });
@@ -5,7 +5,7 @@ import * as childProcess from "node:child_process";
5
5
  import { createHash } from "node:crypto";
6
6
  import fs from "node:fs";
7
7
  import path from "node:path";
8
- import { fetchWithRetry, IS_WINDOWS, ResponseTooLargeError, readBodyWithByteCap } from "../../core/common.js";
8
+ import { fetchWithRetry, IS_WINDOWS, ResponseTooLargeError, readBodyWithByteCap, readChunkWithDeadline, } from "../../core/common.js";
9
9
  import { ConfigError } from "../../core/errors.js";
10
10
  import { warn } from "../../core/warn.js";
11
11
  import { githubHeaders } from "../../integrations/github.js";
@@ -29,7 +29,19 @@ export function defaultMigrationCommand(preparedConfigPath) {
29
29
  apply: (akmBin) => runRequiredCommand(akmBin, ["migrate", "apply", ...configArgs], "Migration apply"),
30
30
  };
31
31
  }
32
- async function streamResponseToFile(response, destination, maxBytes) {
32
+ /**
33
+ * Bounds on the binary body read. `fetchWithTimeout`'s timer only covers
34
+ * time-to-HEADERS — it is cleared the moment `fetch` resolves — so without
35
+ * these a compromised or misconfigured endpoint could dribble bytes forever
36
+ * and hang `akm upgrade` with no output and no way out but Ctrl-C. Two
37
+ * bounds, because either alone is evadable: a per-chunk STALL deadline (a
38
+ * connection making no progress dies quickly, while a slow-but-progressing
39
+ * one is left alone) and an OVERALL deadline (a trickle that sends one byte
40
+ * per stall-window can't stretch the download indefinitely).
41
+ */
42
+ const BINARY_STALL_TIMEOUT_MS = 60_000;
43
+ const BINARY_TOTAL_TIMEOUT_MS = 30 * 60_000;
44
+ export async function streamResponseToFile(response, destination, maxBytes, limits) {
33
45
  const declaredText = response.headers.get("content-length");
34
46
  if (declaredText) {
35
47
  const declared = Number(declaredText);
@@ -40,22 +52,30 @@ async function streamResponseToFile(response, destination, maxBytes) {
40
52
  }
41
53
  if (!response.body)
42
54
  throw new Error("Binary download response did not provide a streaming body.");
55
+ const url = response.url || "binary download";
56
+ const stallTimeoutMs = limits?.stallTimeoutMs ?? BINARY_STALL_TIMEOUT_MS;
57
+ const totalTimeoutMs = limits?.totalTimeoutMs ?? BINARY_TOTAL_TIMEOUT_MS;
58
+ const overallDeadlineAt = Date.now() + totalTimeoutMs;
43
59
  const fd = fs.openSync(destination, "w", 0o600);
44
60
  const reader = response.body.getReader();
45
61
  const hash = createHash("sha256");
46
62
  let byteSize = 0;
47
63
  try {
48
64
  while (true) {
49
- const { done, value } = await reader.read();
65
+ // Whichever bound bites first wins, and the error reports THAT bound —
66
+ // "no data for 60s" and "download exceeded 30m" are different diagnoses.
67
+ const stallDeadlineAt = Date.now() + stallTimeoutMs;
68
+ const deadlineAt = Math.min(stallDeadlineAt, overallDeadlineAt);
69
+ const reportedTimeoutMs = deadlineAt === overallDeadlineAt ? totalTimeoutMs : stallTimeoutMs;
70
+ const { done, value } = await readChunkWithDeadline(reader, deadlineAt, undefined, url, reportedTimeoutMs);
50
71
  if (done)
51
72
  break;
52
73
  if (!value)
53
74
  continue;
54
75
  byteSize += value.byteLength;
55
- if (byteSize > maxBytes) {
56
- await reader.cancel().catch(() => undefined);
57
- throw new ResponseTooLargeError(response.url || "binary download", maxBytes, byteSize);
58
- }
76
+ // The catch block cancels the reader for every throw path, this included.
77
+ if (byteSize > maxBytes)
78
+ throw new ResponseTooLargeError(url, maxBytes, byteSize);
59
79
  hash.update(value);
60
80
  let written = 0;
61
81
  while (written < value.byteLength)
@@ -65,6 +85,9 @@ async function streamResponseToFile(response, destination, maxBytes) {
65
85
  return { byteSize, sha256: hash.digest("hex") };
66
86
  }
67
87
  catch (error) {
88
+ // Cancel so a timed-out (still-open) connection releases its socket rather
89
+ // than lingering until process exit. Already-cancelled readers no-op.
90
+ await reader.cancel().catch(() => undefined);
68
91
  removeFileBestEffort(destination);
69
92
  throw error;
70
93
  }
@@ -141,6 +164,21 @@ export async function checkForUpdate(currentVersion) {
141
164
  installMethod,
142
165
  };
143
166
  }
167
+ /**
168
+ * Whether the operator has asked to bypass upgrade checksum verification.
169
+ *
170
+ * Deliberately NOT a CLI flag (C6): STABILITY.md states checksum verification
171
+ * is not optional, so the recovery hatch for a genuinely broken `checksums.txt`
172
+ * is this internal, undocumented-on-purpose env var — not something
173
+ * discoverable in `--help` or offered by tab completion.
174
+ *
175
+ * Extracted rather than read inline so `performUpgrade` stays under the
176
+ * function-size bar (`scripts/lint-src-fn-size.ts`), whose baseline only
177
+ * shrinks.
178
+ */
179
+ function checksumBypassRequested() {
180
+ return process.env.AKM_UPGRADE_SKIP_CHECKSUM === "1";
181
+ }
144
182
  export async function performUpgrade(check, opts, dependencies) {
145
183
  const { currentVersion, latestVersion, installMethod } = check;
146
184
  const force = opts?.force === true;
@@ -163,31 +201,14 @@ export async function performUpgrade(check, opts, dependencies) {
163
201
  }
164
202
  const packageManagerCommand = getPackageManagerUpgradeCommand(installMethod);
165
203
  if (packageManagerCommand) {
166
- if (!latestVersion) {
167
- throw new Error("Unable to determine latest version from GitHub releases. Check https://github.com/itlackey/akm/releases");
168
- }
169
- migration.preflight("akm");
170
- const result = childProcess.spawnSync(packageManagerCommand.command, packageManagerCommand.args, {
171
- encoding: "utf8",
172
- env: process.env,
173
- stdio: "pipe",
174
- });
175
- if (result.error) {
176
- throw new Error(`Failed to run '${packageManagerCommand.displayCommand}': ${result.error.message}`);
177
- }
178
- if (result.status !== 0) {
179
- const details = (result.stderr ?? "").trim() || (result.stdout ?? "").trim() || `exit code ${result.status}`;
180
- throw new Error(`Failed to upgrade akm via ${installMethod}: ${details}\nRun manually: ${packageManagerCommand.displayCommand}`);
181
- }
182
- migration.apply("akm");
183
- return {
204
+ return runPackageManagerUpgrade({
205
+ packageManagerCommand,
184
206
  currentVersion,
185
- newVersion: latestVersion,
186
- upgraded: true,
207
+ latestVersion,
187
208
  installMethod,
188
- message: `akm upgraded via ${installMethod}`,
189
- postUpgrade: runPostUpgradeTasks("akm", { skip: skipPostUpgrade }),
190
- };
209
+ migration,
210
+ skipPostUpgrade,
211
+ });
191
212
  }
192
213
  if (installMethod === "unknown") {
193
214
  return {
@@ -234,18 +255,18 @@ export async function performUpgrade(check, opts, dependencies) {
234
255
  }
235
256
  throw err;
236
257
  }
237
- // Download and verify checksum (mandatory — upgrade is blocked if checksums cannot be fetched)
258
+ // Download and verify checksum (mandatory — upgrade is blocked if checksums cannot be fetched).
238
259
  let checksumVerified = false;
239
- const skipChecksum = opts?.skipChecksum === true;
260
+ const skipChecksum = checksumBypassRequested();
240
261
  try {
241
262
  const checksumsResponse = await fetchWithRetry(checksumsUrl);
242
263
  if (!checksumsResponse.ok) {
243
264
  if (skipChecksum) {
244
- warn(`WARNING: checksums.txt fetch failed (HTTP ${checksumsResponse.status}). Proceeding without verification because --skip-checksum was provided.`);
265
+ warn(`WARNING: checksums.txt fetch failed (HTTP ${checksumsResponse.status}). Proceeding without verification because AKM_UPGRADE_SKIP_CHECKSUM=1 was set.`);
245
266
  }
246
267
  else {
247
268
  throw new Error(`Checksum verification failed: could not fetch ${checksumsUrl} (HTTP ${checksumsResponse.status}). ` +
248
- `Use --skip-checksum to bypass (not recommended).`);
269
+ `Set AKM_UPGRADE_SKIP_CHECKSUM=1 to bypass (not recommended).`);
249
270
  }
250
271
  }
251
272
  else {
@@ -260,11 +281,11 @@ export async function performUpgrade(check, opts, dependencies) {
260
281
  }
261
282
  else {
262
283
  if (skipChecksum) {
263
- warn(`WARNING: ${binaryName} not found in checksums.txt. Proceeding without verification because --skip-checksum was provided.`);
284
+ warn(`WARNING: ${binaryName} not found in checksums.txt. Proceeding without verification because AKM_UPGRADE_SKIP_CHECKSUM=1 was set.`);
264
285
  }
265
286
  else {
266
287
  throw new Error(`Checksum verification failed: ${binaryName} not listed in checksums.txt. ` +
267
- `Use --skip-checksum to bypass (not recommended).`);
288
+ `Set AKM_UPGRADE_SKIP_CHECKSUM=1 to bypass (not recommended).`);
268
289
  }
269
290
  }
270
291
  }
@@ -277,12 +298,12 @@ export async function performUpgrade(check, opts, dependencies) {
277
298
  }
278
299
  // Network or parse failure
279
300
  if (skipChecksum) {
280
- warn(`WARNING: Could not fetch or parse checksums: ${err instanceof Error ? err.message : String(err)}. Proceeding because --skip-checksum was provided.`);
301
+ warn(`WARNING: Could not fetch or parse checksums: ${err instanceof Error ? err.message : String(err)}. Proceeding because AKM_UPGRADE_SKIP_CHECKSUM=1 was set.`);
281
302
  }
282
303
  else {
283
304
  removeFileBestEffort(stagedPath);
284
305
  throw new Error(`Checksum verification failed: ${err instanceof Error ? err.message : String(err)}. ` +
285
- `Use --skip-checksum to bypass (not recommended).`);
306
+ `Set AKM_UPGRADE_SKIP_CHECKSUM=1 to bypass (not recommended).`);
286
307
  }
287
308
  }
288
309
  try {
@@ -380,6 +401,78 @@ function runPostUpgradeTasks(akmBin, opts) {
380
401
  };
381
402
  }
382
403
  }
404
+ /**
405
+ * The package-manager arm of {@link performUpgrade}: preflight → install →
406
+ * post-install version verification → migrate apply → post-upgrade tasks.
407
+ * Extracted whole so performUpgrade stays under its fn-size baseline.
408
+ */
409
+ function runPackageManagerUpgrade(input) {
410
+ const { packageManagerCommand, currentVersion, latestVersion, installMethod, migration, skipPostUpgrade } = input;
411
+ if (!latestVersion) {
412
+ throw new Error("Unable to determine latest version from GitHub releases. Check https://github.com/itlackey/akm/releases");
413
+ }
414
+ migration.preflight("akm");
415
+ const result = childProcess.spawnSync(packageManagerCommand.command, packageManagerCommand.args, {
416
+ encoding: "utf8",
417
+ env: process.env,
418
+ stdio: "pipe",
419
+ });
420
+ if (result.error) {
421
+ throw new Error(`Failed to run '${packageManagerCommand.displayCommand}': ${result.error.message}`);
422
+ }
423
+ if (result.status !== 0) {
424
+ const details = (result.stderr ?? "").trim() || (result.stdout ?? "").trim() || `exit code ${result.status}`;
425
+ throw new Error(`Failed to upgrade akm via ${installMethod}: ${details}\nRun manually: ${packageManagerCommand.displayCommand}`);
426
+ }
427
+ // The package manager exiting 0 does not prove it delivered
428
+ // `latestVersion`: a lagging `@latest` dist-tag (partial publish,
429
+ // registry mirror lag) "succeeds" while leaving the old version on PATH.
430
+ // Re-read the version the shim actually reports before claiming an
431
+ // upgrade — a confirmed mismatch also means `migrate apply` would run
432
+ // against the OLD binary, so stop before it.
433
+ const installedVersion = readInstalledCliVersion("akm");
434
+ if (installedVersion !== undefined && installedVersion !== latestVersion) {
435
+ return {
436
+ currentVersion,
437
+ newVersion: latestVersion,
438
+ upgraded: false,
439
+ installMethod,
440
+ message: `\`${packageManagerCommand.displayCommand}\` succeeded, but \`akm --version\` still reports ` +
441
+ `v${installedVersion} (expected v${latestVersion}). The ${installMethod} registry's @latest tag ` +
442
+ `may be lagging the GitHub release — try again shortly, or install the exact version: ` +
443
+ `${packageManagerCommand.displayCommand.replace(/@latest\b/, `@${latestVersion}`)}`,
444
+ };
445
+ }
446
+ migration.apply("akm");
447
+ return {
448
+ currentVersion,
449
+ newVersion: latestVersion,
450
+ upgraded: true,
451
+ installMethod,
452
+ message: installedVersion === latestVersion
453
+ ? `akm upgraded via ${installMethod} (verified: akm --version reports v${installedVersion})`
454
+ : `akm upgraded via ${installMethod} (installed version could not be verified)`,
455
+ postUpgrade: runPostUpgradeTasks("akm", { skip: skipPostUpgrade }),
456
+ };
457
+ }
458
+ /**
459
+ * Read the version the on-PATH `akm` actually reports, or `undefined` when
460
+ * that cannot be determined (no shim on PATH in a sandbox, unparseable
461
+ * output). Verification is fail-open on its own infrastructure — only a
462
+ * CONFIRMED mismatch downgrades an upgrade result (§24.2 Package/release
463
+ * gate).
464
+ */
465
+ function readInstalledCliVersion(akmBin) {
466
+ const result = childProcess.spawnSync(akmBin, ["--version"], {
467
+ encoding: "utf8",
468
+ env: process.env,
469
+ stdio: "pipe",
470
+ });
471
+ if (result.error || result.status !== 0)
472
+ return undefined;
473
+ const match = (result.stdout ?? "").match(/\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?/);
474
+ return match?.[0];
475
+ }
383
476
  function runRequiredCommand(akmBin, args, label) {
384
477
  const result = childProcess.spawnSync(akmBin, args, {
385
478
  encoding: "utf8",
@@ -3,6 +3,7 @@
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
4
  import fs from "node:fs";
5
5
  import path from "node:path";
6
+ import { detectAdapterId } from "../../core/adapter/detect-adapter.js";
6
7
  import { isBundleSlug } from "../../core/asset/asset-ref.js";
7
8
  import { isHttpUrl, resolveStashDir } from "../../core/common.js";
8
9
  import { bundleEntryToSourceEntry, getSources, installedSourceDescriptor, loadConfig, mutateConfig, } from "../../core/config/config.js";
@@ -13,13 +14,14 @@ import { readLockfile, removeLockEntry, upsertLockEntry } from "../../integratio
13
14
  import { parseRegistryRef } from "../../registry/resolve.js";
14
15
  import { detectStashRoot } from "../../sources/providers/provider-utils.js";
15
16
  import { syncFromRef } from "../../sources/providers/sync-from-ref.js";
17
+ import { storeSecretResolver } from "../../sources/snapshot-fetchers/secret-seam.js";
16
18
  import { ensureWebsiteMirror, shouldAllowPrivateWebsiteUrlForTests, validateWebsiteInputUrl, } from "../../sources/snapshot-fetchers/website-ingest.js";
17
19
  import { bundleKeyForPath, bundleKeyForUrl, nextBundleKey } from "./bundle-config-ops.js";
18
20
  export async function akmAdd(input) {
19
21
  const ref = input.ref.trim();
20
22
  if (!ref)
21
23
  throw new UsageError("Install ref or local directory is required. " +
22
- "Examples: `akm add @scope/stash`, `akm add github:owner/repo`, `akm add ./local/path`");
24
+ "Examples: `akm bundle add @scope/stash`, `akm bundle add github:owner/repo`, `akm bundle add ./local/path`");
23
25
  const stashDir = resolveStashDir();
24
26
  if (shouldAddAsWebsiteUrl(ref)) {
25
27
  return addWebsiteSource(ref, stashDir, input.name, input.options);
@@ -47,20 +49,31 @@ async function addLocalSource(ref, sourcePath, stashDir, explicitName) {
47
49
  mutateConfig((config) => {
48
50
  const existing = bundleKeyForPath(config, resolvedPath);
49
51
  if (existing) {
50
- // Already configured — the bundle key is the stable identity; leave it.
51
52
  bundleKey = existing;
52
- return config;
53
+ const current = config.bundles?.[existing];
54
+ if (current?.components)
55
+ return config;
56
+ const bundles = { ...(config.bundles ?? {}) };
57
+ bundles[existing] = {
58
+ ...current,
59
+ path: resolvedPath,
60
+ components: { main: { root: ".", adapter: detectAdapterId(resolvedPath) } },
61
+ };
62
+ return { ...config, bundles };
53
63
  }
54
64
  const bundles = { ...(config.bundles ?? {}) };
55
65
  bundleKey = nextBundleKey(bundles, explicitName, resolvedPath);
56
- bundles[bundleKey] = { path: resolvedPath };
66
+ bundles[bundleKey] = {
67
+ path: resolvedPath,
68
+ components: { main: { root: ".", adapter: detectAdapterId(resolvedPath) } },
69
+ };
57
70
  return { ...config, bundles };
58
71
  });
59
72
  const index = await akmIndex({ stashDir });
60
73
  const updatedConfig = loadConfig();
61
74
  return {
62
75
  schemaVersion: 1,
63
- stashDir,
76
+ bundleDir: stashDir,
64
77
  ref,
65
78
  sourceAdded: {
66
79
  type: "filesystem",
@@ -70,7 +83,6 @@ async function addLocalSource(ref, sourcePath, stashDir, explicitName) {
70
83
  },
71
84
  config: {
72
85
  sourceCount: getSources(updatedConfig).length,
73
- installedKitCount: readLockfile().length,
74
86
  },
75
87
  index: {
76
88
  mode: index.mode,
@@ -84,14 +96,31 @@ async function addLocalSource(ref, sourcePath, stashDir, explicitName) {
84
96
  async function addWebsiteSource(ref, stashDir, name, options) {
85
97
  const allowPrivateHosts = shouldAllowPrivateWebsiteUrlForTests(ref);
86
98
  const normalizedUrl = validateWebsiteInputUrl(ref, { allowPrivateHosts });
87
- const maxPages = typeof options?.maxPages === "number" ? options.maxPages : undefined;
99
+ const numberOption = (value) => (typeof value === "number" ? value : undefined);
100
+ const maxPages = numberOption(options?.maxPages);
101
+ const maxDepth = numberOption(options?.maxDepth);
88
102
  let entry;
89
103
  mutateConfig((config) => {
90
104
  const bundles = { ...(config.bundles ?? {}) };
91
105
  const existingKey = bundleKeyForUrl(config, normalizedUrl);
92
106
  const key = existingKey ?? nextBundleKey(bundles, name ?? toWebsiteName(normalizedUrl), normalizedUrl);
93
- const website = { url: normalizedUrl, ...(maxPages !== undefined ? { maxPages } : {}) };
94
- const nextBundle = { ...(existingKey ? bundles[key] : {}), website };
107
+ // Merge onto the existing descriptor rather than replacing it: re-running
108
+ // `bundle add` for a URL that already has a bundle would otherwise drop
109
+ // respectRobots / refresh, silently restoring default robots enforcement
110
+ // and changing what the next update fetches. Explicitly-passed maxPages /
111
+ // maxDepth still win over the stored values.
112
+ const existingWebsite = existingKey ? (bundles[key]?.website ?? {}) : {};
113
+ const website = {
114
+ ...existingWebsite,
115
+ url: normalizedUrl,
116
+ ...(maxPages !== undefined ? { maxPages } : {}),
117
+ ...(maxDepth !== undefined ? { maxDepth } : {}),
118
+ };
119
+ const nextBundle = {
120
+ ...(existingKey ? bundles[key] : {}),
121
+ website,
122
+ components: { main: { root: ".", adapter: "website-snapshot", writable: false } },
123
+ };
95
124
  if (JSON.stringify(bundles[key]) === JSON.stringify(nextBundle)) {
96
125
  entry = bundleEntryToSourceEntry(key, bundles[key]);
97
126
  return config;
@@ -102,13 +131,14 @@ async function addWebsiteSource(ref, stashDir, name, options) {
102
131
  });
103
132
  const cachePaths = await ensureWebsiteMirror(entry, {
104
133
  requireStashDir: true,
134
+ resolveSecret: storeSecretResolver.resolveSecret,
105
135
  ...(allowPrivateHosts ? { allowPrivateHosts: true } : {}),
106
136
  });
107
137
  const index = await akmIndex({ stashDir });
108
138
  const updatedConfig = loadConfig();
109
139
  return {
110
140
  schemaVersion: 1,
111
- stashDir,
141
+ bundleDir: stashDir,
112
142
  ref,
113
143
  sourceAdded: {
114
144
  type: "website",
@@ -118,7 +148,6 @@ async function addWebsiteSource(ref, stashDir, name, options) {
118
148
  },
119
149
  config: {
120
150
  sourceCount: getSources(updatedConfig).length,
121
- installedKitCount: readLockfile().length,
122
151
  },
123
152
  index: {
124
153
  mode: index.mode,
@@ -135,10 +164,23 @@ async function addWebsiteSource(ref, stashDir, name, options) {
135
164
  */
136
165
  async function addRegistryStash(ref, stashDir, writable) {
137
166
  const parsedRef = parseRegistryRef(ref);
138
- if (writable === true && parsedRef.source !== "git") {
167
+ if (writable === true && parsedRef.source !== "git" && parsedRef.source !== "github") {
139
168
  throw new ConfigError("writable: true is only supported on filesystem and git sources", "INVALID_CONFIG_FILE");
140
169
  }
141
- const synced = await syncFromRef(ref, { writable });
170
+ const currentConfig = loadConfig();
171
+ const existingBundleKey = findInstalledBundleKey(currentConfig.bundles ?? {}, parsedRef.id);
172
+ const existingBundle = existingBundleKey ? currentConfig.bundles?.[existingBundleKey] : undefined;
173
+ const priorLock = existingBundleKey ? readLockfile().find((entry) => entry.id === existingBundleKey) : undefined;
174
+ const existingWritable = Object.values(existingBundle?.components ?? {})[0]?.writable ?? existingBundle?.writable === true;
175
+ const effectiveWritable = writable === true || existingWritable;
176
+ const requiredRoots = priorLock?.localRoot
177
+ ? Object.values(existingBundle?.components ?? {}).map((component) => path.resolve(priorLock.localRoot, component.root ?? "."))
178
+ : [];
179
+ const synced = await syncFromRef(ref, {
180
+ writable: effectiveWritable,
181
+ ...(effectiveWritable && priorLock?.localRoot ? { writableRoot: priorLock.localRoot } : {}),
182
+ ...(requiredRoots.length > 0 ? { writableRequiredRoots: requiredRoots } : {}),
183
+ });
142
184
  const { config: updatedConfig, bundleId } = upsertInstalledRegistryEntry({
143
185
  id: synced.id,
144
186
  source: synced.source,
@@ -153,7 +195,7 @@ async function addRegistryStash(ref, stashDir, writable) {
153
195
  });
154
196
  // The prior materialized root (if this is a re-install) — read BEFORE the lock
155
197
  // upsert overwrites it, so a moved cache root can be cleaned afterwards.
156
- const priorLocalRoot = readLockfile().find((e) => e.id === bundleId)?.localRoot;
198
+ const priorLocalRoot = priorLock?.localRoot;
157
199
  await upsertLockEntry({
158
200
  id: bundleId,
159
201
  source: synced.source,
@@ -166,7 +208,7 @@ async function addRegistryStash(ref, stashDir, writable) {
166
208
  installedAt: synced.syncedAt,
167
209
  });
168
210
  // Clean up the old materialized root on re-install (moved cache).
169
- if (priorLocalRoot && path.resolve(priorLocalRoot) !== path.resolve(synced.contentDir)) {
211
+ if (!effectiveWritable && priorLocalRoot && path.resolve(priorLocalRoot) !== path.resolve(synced.contentDir)) {
170
212
  try {
171
213
  fs.rmSync(priorLocalRoot, { recursive: true, force: true });
172
214
  }
@@ -177,7 +219,7 @@ async function addRegistryStash(ref, stashDir, writable) {
177
219
  const index = await akmIndex({ stashDir });
178
220
  return {
179
221
  schemaVersion: 1,
180
- stashDir,
222
+ bundleDir: stashDir,
181
223
  ref,
182
224
  installed: {
183
225
  id: synced.id,
@@ -193,7 +235,6 @@ async function addRegistryStash(ref, stashDir, writable) {
193
235
  },
194
236
  config: {
195
237
  sourceCount: getSources(updatedConfig).length,
196
- installedKitCount: readLockfile().length,
197
238
  },
198
239
  index: {
199
240
  mode: index.mode,
@@ -217,11 +258,25 @@ export function upsertInstalledRegistryEntry(entry) {
217
258
  const config = mutateConfig((current) => {
218
259
  const bundles = { ...(current.bundles ?? {}) };
219
260
  bundleId = resolveInstalledBundleKey(bundles, entry.id, entry.stashRoot);
261
+ const existingComponents = bundles[bundleId]?.components;
262
+ const components = existingComponents
263
+ ? Object.fromEntries(Object.entries(existingComponents).map(([id, component]) => [
264
+ id,
265
+ { ...component, writable: entry.writable === true },
266
+ ]))
267
+ : {
268
+ main: {
269
+ root: ".",
270
+ adapter: detectAdapterId(path.resolve(entry.stashRoot)),
271
+ writable: entry.writable === true,
272
+ },
273
+ };
220
274
  const descriptor = installedSourceDescriptor(entry.source, entry.ref, path.resolve(entry.stashRoot));
221
275
  bundles[bundleId] = {
222
276
  ...descriptor,
223
277
  ...(entry.writable === true ? { writable: true } : {}),
224
278
  ...(entry.id !== bundleId ? { registryId: entry.id } : {}),
279
+ components: components,
225
280
  };
226
281
  return { ...current, bundles };
227
282
  }).config;