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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (587) hide show
  1. package/CHANGELOG.md +1190 -52
  2. package/README.md +62 -37
  3. package/SECURITY.md +46 -31
  4. package/dist/akm +162 -38
  5. package/dist/akm-migrate +44 -0
  6. package/dist/assets/backends/schtasks-template.xml +2 -1
  7. package/dist/assets/hints/cli-hints-full.md +268 -118
  8. package/dist/assets/hints/cli-hints-short.md +87 -24
  9. package/dist/assets/{profiles → improve-strategies}/catchup.json +3 -1
  10. package/dist/assets/{profiles → improve-strategies}/consolidate.json +3 -1
  11. package/dist/assets/{profiles → improve-strategies}/default.json +6 -7
  12. package/dist/assets/improve-strategies/frequent.json +15 -0
  13. package/dist/assets/{profiles → improve-strategies}/graph-refresh.json +4 -2
  14. package/dist/assets/{profiles → improve-strategies}/memory-focus.json +4 -1
  15. package/dist/assets/{profiles → improve-strategies}/proactive-maintenance.json +5 -5
  16. package/dist/assets/{profiles → improve-strategies}/quick.json +4 -2
  17. package/dist/assets/improve-strategies/reflect-distill.json +30 -0
  18. package/dist/assets/{profiles → improve-strategies}/thorough.json +1 -1
  19. package/dist/assets/prompts/consolidate-system.md +5 -5
  20. package/dist/assets/prompts/extract-session.md +2 -6
  21. package/dist/assets/prompts/memory-infer-user.md +2 -3
  22. package/dist/assets/prompts/reflect-llm-framed-contract.md +11 -0
  23. package/dist/assets/prompts/reflect-llm-schema-contract.md +3 -0
  24. package/dist/assets/prompts/reflect-output-repair.md +3 -0
  25. package/dist/assets/stash-skeleton/README.md +38 -10
  26. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +8 -0
  27. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +8 -0
  28. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +14 -1
  29. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +13 -1
  30. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +9 -1
  31. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +11 -0
  32. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +9 -0
  33. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +9 -0
  34. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +8 -0
  35. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +100 -0
  36. package/dist/assets/stash-skeleton/facts/conventions/domains.md +64 -0
  37. package/dist/assets/stash-skeleton/facts/conventions/organization.md +136 -0
  38. package/dist/assets/tasks/core/extract.yml +3 -2
  39. package/dist/assets/tasks/core/improve.yml +2 -1
  40. package/dist/assets/tasks/core/index-refresh.yml +1 -0
  41. package/dist/assets/tasks/core/sync.yml +1 -0
  42. package/dist/assets/tasks/core/version-check.yml +2 -1
  43. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +5 -0
  44. package/dist/assets/tasks/improve/akm-improve-catchup.yml +8 -0
  45. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +5 -0
  46. package/dist/assets/tasks/improve/akm-improve-frequent.yml +5 -0
  47. package/dist/assets/tasks/improve/akm-improve-nightly.yml +5 -0
  48. package/dist/assets/templates/html/health.html +5 -4
  49. package/dist/assets/workflows/workflow-template.md +31 -15
  50. package/dist/cli/invocation.js +279 -0
  51. package/dist/cli/parse-args.js +5 -90
  52. package/dist/cli/retired-commands.js +78 -0
  53. package/dist/cli/shared.js +158 -48
  54. package/dist/cli-node.mjs +2 -1
  55. package/dist/cli.js +747 -293
  56. package/dist/commands/agent/agent-dispatch.js +19 -18
  57. package/dist/commands/agent/agent-support.js +0 -24
  58. package/dist/commands/agent/contribute-cli.js +43 -97
  59. package/dist/commands/completions.js +80 -23
  60. package/dist/commands/config-cli.js +44 -281
  61. package/dist/commands/env/env-binding.js +13 -9
  62. package/dist/commands/env/env-cli.js +76 -159
  63. package/dist/commands/env/env.js +12 -163
  64. package/dist/commands/env/marker-path.js +6 -0
  65. package/dist/commands/env/secret-cli.js +45 -61
  66. package/dist/commands/env/secret.js +32 -62
  67. package/dist/commands/feedback-cli.js +179 -85
  68. package/dist/commands/health/accept-rate.js +58 -0
  69. package/dist/commands/health/advisories.js +7 -8
  70. package/dist/commands/health/checks.js +279 -94
  71. package/dist/commands/health/html-report.js +197 -578
  72. package/dist/commands/health/improve-metrics.js +277 -246
  73. package/dist/commands/health/llm-usage.js +19 -19
  74. package/dist/commands/health/md-report.js +16 -7
  75. package/dist/commands/health/metrics.js +67 -32
  76. package/dist/commands/health/renderers.js +47 -0
  77. package/dist/commands/health/report-view-model.js +508 -0
  78. package/dist/commands/health/stash-exposure.js +1 -1
  79. package/dist/commands/health/surfaces.js +16 -56
  80. package/dist/commands/health/task-runs.js +3 -67
  81. package/dist/{migrate-storage-node.mjs → commands/health/types-checks.js} +1 -5
  82. package/dist/commands/health/types-improve.js +29 -0
  83. package/dist/{output/text/save.js → commands/health/types-metrics.js} +1 -2
  84. package/dist/commands/health/types-result.js +7 -0
  85. package/dist/commands/health/types-runs.js +4 -0
  86. package/dist/commands/health/types-session-log.js +4 -0
  87. package/dist/commands/health/types-windows.js +4 -0
  88. package/dist/commands/health/types.js +26 -21
  89. package/dist/commands/health/windows.js +2 -3
  90. package/dist/commands/health.js +296 -167
  91. package/dist/commands/improve/anti-collapse.js +5 -5
  92. package/dist/commands/improve/autonomy-gate.js +68 -0
  93. package/dist/commands/improve/collapse-detector.js +65 -52
  94. package/dist/commands/improve/consolidate/chunking.js +9 -7
  95. package/dist/commands/improve/consolidate/eligibility.js +1 -23
  96. package/dist/commands/improve/consolidate/merge.js +4 -0
  97. package/dist/commands/improve/consolidate.js +454 -1354
  98. package/dist/commands/improve/content-hash.js +39 -0
  99. package/dist/commands/improve/distill/content-repair.js +4 -10
  100. package/dist/commands/improve/distill/promote-memory.js +89 -64
  101. package/dist/commands/improve/distill/quality-gate.js +118 -42
  102. package/dist/commands/improve/distill-guards.js +1 -1
  103. package/dist/commands/improve/distill-promotion-policy.js +33 -888
  104. package/dist/commands/improve/distill.js +607 -363
  105. package/dist/commands/improve/eligibility.js +165 -79
  106. package/dist/commands/improve/extract-cli.js +35 -126
  107. package/dist/commands/improve/extract-prompt.js +6 -35
  108. package/dist/commands/improve/extract.js +640 -391
  109. package/dist/commands/improve/feedback-valence.js +2 -12
  110. package/dist/commands/improve/improve-cli.js +134 -135
  111. package/dist/commands/improve/improve-result-file.js +30 -50
  112. package/dist/commands/improve/improve-run-types.js +4 -0
  113. package/dist/commands/improve/improve-strategies.js +135 -0
  114. package/dist/commands/improve/improve.js +904 -701
  115. package/dist/commands/improve/locks.js +64 -111
  116. package/dist/commands/improve/loop-stages.js +1110 -923
  117. package/dist/commands/improve/memory/derived-ref.js +124 -0
  118. package/dist/commands/improve/memory/memory-belief.js +79 -7
  119. package/dist/commands/improve/memory/memory-contradiction-detect.js +49 -52
  120. package/dist/commands/improve/memory/memory-improve.js +25 -37
  121. package/dist/commands/improve/outcome-loop.js +25 -88
  122. package/dist/commands/improve/preparation.js +1034 -813
  123. package/dist/commands/improve/proactive-maintenance.js +34 -9
  124. package/dist/commands/improve/proposal-envelope.js +31 -0
  125. package/dist/commands/improve/reflect.js +983 -794
  126. package/dist/commands/improve/run-context.js +119 -0
  127. package/dist/commands/improve/salience.js +24 -127
  128. package/dist/commands/improve/session-asset.js +7 -3
  129. package/dist/commands/improve/shared.js +14 -34
  130. package/dist/commands/improve/source-identity.js +28 -0
  131. package/dist/commands/improve/triage.js +20 -17
  132. package/dist/commands/lint/base-linter.js +340 -313
  133. package/dist/commands/lint/env-key-rules.js +31 -47
  134. package/dist/commands/lint/index.js +185 -30
  135. package/dist/commands/{events.js → log.js} +28 -38
  136. package/dist/commands/migrate-cli.js +54 -0
  137. package/dist/commands/migration-tool.js +55 -0
  138. package/dist/commands/observability-cli.js +70 -208
  139. package/dist/commands/proposal/diff-format.js +50 -0
  140. package/dist/commands/proposal/drain-policies.js +0 -6
  141. package/dist/commands/proposal/drain.js +91 -40
  142. package/dist/commands/proposal/proposal-cli.js +134 -132
  143. package/dist/commands/proposal/proposal-types.js +56 -0
  144. package/dist/commands/proposal/proposal.js +83 -65
  145. package/dist/commands/proposal/propose-cli.js +88 -0
  146. package/dist/commands/proposal/propose.js +105 -88
  147. package/dist/commands/proposal/repository.js +1303 -278
  148. package/dist/commands/proposal/validators/proposal-quality-validators.js +16 -6
  149. package/dist/commands/proposal/validators/proposal-validators.js +61 -12
  150. package/dist/commands/proposal/validators/proposals.js +6 -8
  151. package/dist/commands/read/curate.js +78 -73
  152. package/dist/commands/read/knowledge.js +510 -13
  153. package/dist/commands/read/registry-search.js +2 -2
  154. package/dist/commands/read/remember-cli.js +84 -15
  155. package/dist/commands/read/search-cli.js +203 -96
  156. package/dist/commands/read/search.js +126 -94
  157. package/dist/commands/read/show.js +226 -250
  158. package/dist/commands/registry-cli.js +34 -60
  159. package/dist/commands/remember.js +18 -57
  160. package/dist/commands/sources/add-cli.js +104 -49
  161. package/dist/commands/sources/bundle-cli.js +166 -0
  162. package/dist/commands/sources/bundle-config-ops.js +63 -0
  163. package/dist/commands/sources/info.js +27 -15
  164. package/dist/commands/sources/init.js +30 -40
  165. package/dist/commands/sources/installed-stashes.js +469 -172
  166. package/dist/commands/sources/schema-repair.js +10 -9
  167. package/dist/commands/sources/self-update.js +182 -121
  168. package/dist/commands/sources/source-add.js +169 -178
  169. package/dist/commands/sources/source-clone.js +144 -41
  170. package/dist/commands/sources/source-manage.js +94 -59
  171. package/dist/commands/sources/sources-cli.js +64 -205
  172. package/dist/commands/sources/stash-cli.js +91 -54
  173. package/dist/commands/sources/stash-skeleton.js +1 -1
  174. package/dist/commands/tasks/tasks-cli.js +106 -104
  175. package/dist/commands/tasks/tasks.js +445 -262
  176. package/dist/commands/workflow-cli.js +75 -228
  177. package/dist/core/action-contributors.js +1 -1
  178. package/dist/core/activation-policy.js +49 -0
  179. package/dist/core/adapter/adapters/agent-skills-adapter.js +181 -0
  180. package/dist/core/adapter/adapters/akm-adapter.js +528 -0
  181. package/dist/core/adapter/adapters/akm-lint.js +392 -0
  182. package/dist/core/adapter/adapters/akm-metadata.js +387 -0
  183. package/dist/core/adapter/adapters/akm-task-adapter.js +149 -0
  184. package/dist/core/adapter/adapters/akm-workflow-adapter.js +180 -0
  185. package/dist/core/adapter/adapters/claude-adapter.js +61 -0
  186. package/dist/core/adapter/adapters/dotenv-adapter.js +187 -0
  187. package/dist/core/adapter/adapters/generic-files-adapter.js +119 -0
  188. package/dist/core/adapter/adapters/index.js +80 -0
  189. package/dist/core/adapter/adapters/llm-wiki-adapter.js +419 -0
  190. package/dist/core/adapter/adapters/okf-adapter.js +391 -0
  191. package/dist/core/adapter/adapters/opencode-adapter.js +68 -0
  192. package/dist/core/adapter/adapters/shared.js +286 -0
  193. package/dist/core/adapter/adapters/tool-dir-shared.js +217 -0
  194. package/dist/core/adapter/adapters/website-snapshot-adapter.js +155 -0
  195. package/dist/core/adapter/bundle-adapter.js +4 -0
  196. package/dist/core/adapter/detect-adapter.js +17 -0
  197. package/dist/core/adapter/recognize-match.js +44 -0
  198. package/dist/core/adapter/registry.js +56 -0
  199. package/dist/core/adapter/types.js +4 -0
  200. package/dist/core/asset/akm-markdown.js +30 -0
  201. package/dist/core/asset/asset-placement.js +243 -0
  202. package/dist/core/asset/asset-ref.js +110 -79
  203. package/dist/core/asset/asset-serialize.js +20 -0
  204. package/dist/core/asset/frontmatter.js +28 -12
  205. package/dist/core/asset/markdown.js +40 -51
  206. package/dist/core/asset/resolve-ref.js +274 -0
  207. package/dist/core/asset/stash-meta.js +2 -2
  208. package/dist/core/bundle-id.js +51 -0
  209. package/dist/core/common.js +281 -86
  210. package/dist/core/config/config-io.js +42 -128
  211. package/dist/core/config/config-schema.js +233 -855
  212. package/dist/core/config/config-sources.js +162 -39
  213. package/dist/core/config/config-types.js +16 -11
  214. package/dist/core/config/config-version.js +29 -0
  215. package/dist/core/config/config-walker.js +126 -37
  216. package/dist/core/config/config.js +154 -331
  217. package/dist/core/config/deep-merge.js +41 -0
  218. package/dist/core/config/engine-semantics.js +28 -0
  219. package/dist/core/config/experimental.js +21 -0
  220. package/dist/core/config/schema/embedding.js +38 -0
  221. package/dist/core/config/schema/engines.js +116 -0
  222. package/dist/core/config/schema/experimental.js +47 -0
  223. package/dist/core/config/schema/feedback.js +31 -0
  224. package/dist/core/config/schema/improve-processes.js +389 -0
  225. package/dist/core/config/schema/improve.js +94 -0
  226. package/dist/core/config/schema/index-config.js +176 -0
  227. package/dist/core/config/schema/output.js +18 -0
  228. package/dist/core/config/schema/primitives.js +94 -0
  229. package/dist/core/config/schema/search.js +30 -0
  230. package/dist/core/config/schema/setup.js +18 -0
  231. package/dist/core/config/schema/sources-bundles.js +169 -0
  232. package/dist/core/config/schema/workflow.js +29 -0
  233. package/dist/core/env-secret-ref.js +155 -20
  234. package/dist/core/errors.js +17 -15
  235. package/dist/core/events-types.js +4 -0
  236. package/dist/core/events.js +46 -128
  237. package/dist/core/extra-params.js +62 -0
  238. package/dist/core/file-change.js +17 -0
  239. package/dist/core/file-lock.js +202 -57
  240. package/dist/core/fs-txn.js +392 -0
  241. package/dist/core/git-message.js +59 -0
  242. package/dist/core/improve-result.js +167 -0
  243. package/dist/core/lesson-lint.js +1 -17
  244. package/dist/core/logs-db.js +1 -1
  245. package/dist/core/maintenance-barrier.js +135 -0
  246. package/dist/core/migration-operation.js +44 -0
  247. package/dist/core/mutation-target.js +78 -0
  248. package/dist/core/paths.js +22 -25
  249. package/dist/core/platform.js +10 -0
  250. package/dist/core/recognition-util.js +128 -0
  251. package/dist/core/redaction.js +392 -0
  252. package/dist/core/standards/resolve-standards-context.js +36 -65
  253. package/dist/core/standards/resolve-stash-standards.js +2 -2
  254. package/dist/core/standards/resolve-type-conventions.js +5 -5
  255. package/dist/core/state/migrations.js +242 -11
  256. package/dist/core/state-db.js +98 -10
  257. package/dist/core/structured.js +1 -1
  258. package/dist/core/subprocess.js +303 -0
  259. package/dist/core/text-truncation.js +9 -5
  260. package/dist/core/time.js +20 -0
  261. package/dist/core/type-presentation.js +130 -0
  262. package/dist/core/warn.js +0 -3
  263. package/dist/core/write-source.js +834 -118
  264. package/dist/indexer/bundle-identity-guard.js +92 -0
  265. package/dist/indexer/db/graph-db.js +1 -25
  266. package/dist/indexer/db/llm-cache.js +1 -1
  267. package/dist/indexer/ensure-index.js +30 -9
  268. package/dist/indexer/graph/graph-boost.js +9 -30
  269. package/dist/indexer/graph/graph-extraction.js +41 -27
  270. package/dist/indexer/graph/graph-types.js +4 -0
  271. package/dist/indexer/index-writer-lock.js +93 -49
  272. package/dist/indexer/index-written-assets.js +100 -53
  273. package/dist/indexer/indexer.js +746 -329
  274. package/dist/indexer/init.js +18 -25
  275. package/dist/indexer/installations.js +142 -0
  276. package/dist/indexer/passes/dir-staleness.js +18 -10
  277. package/dist/indexer/passes/memory-inference.js +25 -15
  278. package/dist/indexer/passes/metadata.js +412 -243
  279. package/dist/indexer/scan/doc-to-entry.js +160 -0
  280. package/dist/indexer/scan/drain-dir.js +134 -0
  281. package/dist/indexer/search/db-search.js +292 -108
  282. package/dist/indexer/search/fts-query.js +64 -0
  283. package/dist/indexer/search/ranking-contributors.js +145 -25
  284. package/dist/indexer/search/ranking-types.js +4 -0
  285. package/dist/indexer/search/ranking.js +28 -71
  286. package/dist/indexer/search/search-attribution.js +67 -0
  287. package/dist/indexer/search/search-fields.js +18 -3
  288. package/dist/indexer/search/search-hit-enrichers.js +30 -40
  289. package/dist/indexer/search/search-source.js +157 -111
  290. package/dist/indexer/search/semantic-status.js +4 -1
  291. package/dist/indexer/usage/usage-events.js +10 -30
  292. package/dist/indexer/walk/file-context.js +3 -45
  293. package/dist/indexer/walk/matchers.js +42 -73
  294. package/dist/indexer/walk/path-resolver.js +11 -5
  295. package/dist/indexer/walk/walker.js +42 -14
  296. package/dist/integrations/agent/builder-shared.js +7 -0
  297. package/dist/integrations/agent/builders.js +5 -58
  298. package/dist/integrations/agent/config.js +3 -143
  299. package/dist/integrations/agent/detect.js +17 -2
  300. package/dist/integrations/agent/engine-resolution.js +231 -0
  301. package/dist/integrations/agent/index.js +1 -2
  302. package/dist/integrations/agent/model-aliases.js +8 -3
  303. package/dist/integrations/agent/profiles.js +6 -99
  304. package/dist/integrations/agent/prompts.js +46 -18
  305. package/dist/integrations/agent/runner-dispatch.js +78 -13
  306. package/dist/integrations/agent/runner.js +76 -208
  307. package/dist/integrations/agent/spawn.js +48 -279
  308. package/dist/integrations/harnesses/aider/agent-builder.js +9 -8
  309. package/dist/integrations/harnesses/aider/index.js +2 -12
  310. package/dist/integrations/harnesses/amazonq/agent-builder.js +10 -16
  311. package/dist/integrations/harnesses/amazonq/index.js +3 -17
  312. package/dist/integrations/harnesses/claude/agent-builder.js +2 -3
  313. package/dist/integrations/harnesses/claude/config-import.js +1 -3
  314. package/dist/integrations/harnesses/claude/index.js +1 -14
  315. package/dist/integrations/harnesses/claude/session-log.js +27 -75
  316. package/dist/integrations/harnesses/codex/agent-builder.js +8 -7
  317. package/dist/integrations/harnesses/codex/index.js +2 -13
  318. package/dist/integrations/harnesses/copilot/agent-builder.js +9 -9
  319. package/dist/integrations/harnesses/copilot/index.js +1 -13
  320. package/dist/integrations/harnesses/gemini/agent-builder.js +8 -9
  321. package/dist/integrations/harnesses/gemini/index.js +1 -13
  322. package/dist/integrations/harnesses/ids.js +24 -0
  323. package/dist/integrations/harnesses/index.js +31 -33
  324. package/dist/integrations/harnesses/opencode/agent-builder.js +23 -5
  325. package/dist/integrations/harnesses/opencode/config-import.js +1 -3
  326. package/dist/integrations/harnesses/opencode/index.js +1 -18
  327. package/dist/integrations/harnesses/opencode/session-log.js +67 -125
  328. package/dist/integrations/harnesses/opencode-sdk/harness.js +3 -17
  329. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +274 -191
  330. package/dist/integrations/harnesses/openhands/agent-builder.js +12 -10
  331. package/dist/integrations/harnesses/openhands/index.js +2 -12
  332. package/dist/integrations/harnesses/pi/agent-builder.js +11 -18
  333. package/dist/integrations/harnesses/pi/index.js +3 -16
  334. package/dist/integrations/harnesses/shared.js +17 -0
  335. package/dist/integrations/harnesses/types.js +38 -34
  336. package/dist/integrations/lockfile.js +211 -24
  337. package/dist/integrations/session-logs/index.js +24 -40
  338. package/dist/integrations/session-logs/provider-base.js +113 -0
  339. package/dist/llm/client.js +182 -110
  340. package/dist/llm/embedders/deterministic.js +2 -2
  341. package/dist/llm/embedders/remote.js +21 -9
  342. package/dist/llm/feature-gate.js +17 -57
  343. package/dist/llm/graph-extract.js +12 -13
  344. package/dist/llm/index-passes.js +8 -42
  345. package/dist/llm/memory-infer.js +144 -1
  346. package/dist/llm/metadata-enhance.js +45 -30
  347. package/dist/llm/structured-call.js +16 -8
  348. package/dist/llm/usage-persist.js +30 -5
  349. package/dist/llm/usage-telemetry.js +59 -6
  350. package/dist/output/cli-hints.js +1 -2
  351. package/dist/output/command-registry.js +27 -0
  352. package/dist/output/context.js +22 -7
  353. package/dist/output/format-exempt.js +80 -0
  354. package/dist/output/generic-render.js +251 -0
  355. package/dist/output/html-render.js +11 -16
  356. package/dist/output/render-registry.js +57 -0
  357. package/dist/output/renderers.js +15 -281
  358. package/dist/output/shapes/curate.js +10 -1
  359. package/dist/output/shapes/events.js +12 -7
  360. package/dist/output/shapes/helpers.js +58 -84
  361. package/dist/output/shapes/passthrough.js +8 -40
  362. package/dist/output/shapes/proposal/producer.js +15 -7
  363. package/dist/output/shapes/registry.js +12 -6
  364. package/dist/output/shapes.js +0 -9
  365. package/dist/output/text/{init.js → bundle-create.js} +3 -1
  366. package/dist/output/text/bundle-show.js +7 -0
  367. package/dist/output/text/command-format.js +562 -0
  368. package/dist/output/text/env.js +1 -3
  369. package/dist/output/text/events.js +8 -7
  370. package/dist/output/text/helpers.js +15 -1375
  371. package/dist/output/text/proposal/producer.js +4 -2
  372. package/dist/output/text/proposal-format.js +202 -0
  373. package/dist/output/text/registry-commands.js +1 -2
  374. package/dist/output/text/registry.js +12 -6
  375. package/dist/output/text/show-directives.js +117 -0
  376. package/dist/output/text/show-format.js +103 -0
  377. package/dist/output/text/sync.js +5 -0
  378. package/dist/output/text/workflow-format.js +332 -0
  379. package/dist/output/text/workflow.js +1 -2
  380. package/dist/output/text.js +10 -19
  381. package/dist/registry/factory.js +4 -6
  382. package/dist/registry/origin-resolve.js +16 -27
  383. package/dist/registry/providers/skills-sh.js +3 -3
  384. package/dist/registry/providers/static-index.js +15 -25
  385. package/dist/registry/resolve.js +43 -94
  386. package/dist/registry/semver.js +43 -0
  387. package/dist/runtime.js +81 -12
  388. package/dist/scripts/akm-migrate.js +35529 -0
  389. package/dist/setup/detect.js +5 -7
  390. package/dist/setup/detected-engines.js +136 -0
  391. package/dist/setup/engine-config.js +100 -0
  392. package/dist/setup/registry-stash-loader.js +3 -3
  393. package/dist/setup/semantic-assets.js +12 -9
  394. package/dist/setup/setup.js +444 -208
  395. package/dist/setup/steps/connection-shared.js +120 -0
  396. package/dist/setup/steps/connection.js +108 -305
  397. package/dist/setup/steps/platforms.js +13 -12
  398. package/dist/setup/steps/semantic.js +15 -3
  399. package/dist/setup/steps/sources.js +21 -15
  400. package/dist/setup/steps/stashdir.js +6 -4
  401. package/dist/setup/steps/tasks.js +236 -119
  402. package/dist/setup/steps.js +3 -2
  403. package/dist/sources/freshness.js +39 -0
  404. package/dist/sources/provider-factory.js +11 -17
  405. package/dist/sources/providers/filesystem.js +2 -3
  406. package/dist/sources/providers/git-install.js +278 -34
  407. package/dist/sources/providers/git-provider.js +54 -56
  408. package/dist/sources/providers/git-stash.js +420 -91
  409. package/dist/sources/providers/git.js +2 -2
  410. package/dist/sources/providers/npm.js +16 -19
  411. package/dist/sources/providers/provider-utils.js +47 -22
  412. package/dist/sources/providers/sync-from-ref.js +3 -9
  413. package/dist/sources/providers/website.js +2 -2
  414. package/dist/sources/resolve.js +11 -10
  415. package/dist/sources/snapshot-fetchers/types.js +4 -0
  416. package/dist/sources/{website-ingest.js → snapshot-fetchers/website-ingest.js} +110 -41
  417. package/dist/storage/database.js +60 -4
  418. package/dist/storage/engines/sqlite-migrations.js +156 -5
  419. package/dist/storage/locations.js +1 -2
  420. package/dist/storage/repositories/canaries-repository.js +1 -1
  421. package/dist/storage/repositories/events-repository.js +51 -11
  422. package/dist/storage/repositories/improve-runs-repository.js +6 -32
  423. package/dist/storage/repositories/index-connection.js +79 -0
  424. package/dist/storage/repositories/index-db.js +4 -3
  425. package/dist/storage/repositories/index-entries-repository.js +863 -0
  426. package/dist/{indexer/db/entry-mapper.js → storage/repositories/index-entry-mapper.js} +19 -2
  427. package/dist/storage/repositories/index-entry-types.js +4 -0
  428. package/dist/storage/repositories/index-fts-repository.js +167 -0
  429. package/dist/storage/repositories/index-llm-cache-repository.js +108 -0
  430. package/dist/storage/repositories/index-meta-repository.js +49 -0
  431. package/dist/{indexer/db/schema.js → storage/repositories/index-schema.js} +226 -100
  432. package/dist/storage/repositories/index-sql.js +12 -0
  433. package/dist/storage/repositories/index-utility-repository.js +356 -0
  434. package/dist/storage/repositories/index-vec-repository.js +250 -0
  435. package/dist/storage/repositories/outcome-repository.js +119 -0
  436. package/dist/storage/repositories/proposals-repository.js +317 -75
  437. package/dist/storage/repositories/registry-cache.js +1 -1
  438. package/dist/storage/repositories/salience-repository.js +172 -0
  439. package/dist/storage/repositories/task-history-repository.js +110 -3
  440. package/dist/storage/repositories/workflow-runs-repository.js +68 -35
  441. package/dist/tasks/backends/cron.js +169 -46
  442. package/dist/tasks/backends/exec-utils.js +76 -3
  443. package/dist/tasks/backends/index.js +6 -9
  444. package/dist/tasks/backends/launchd.js +292 -55
  445. package/dist/tasks/backends/schtasks.js +557 -70
  446. package/dist/tasks/backends/types.js +4 -0
  447. package/dist/tasks/command-executable.js +93 -0
  448. package/dist/tasks/embedded.js +56 -38
  449. package/dist/tasks/parser.js +156 -64
  450. package/dist/tasks/resolve-akm-bin.js +144 -51
  451. package/dist/tasks/runner.js +377 -209
  452. package/dist/tasks/schedule.js +108 -19
  453. package/dist/tasks/scheduler-invocation.js +296 -0
  454. package/dist/tasks/schema.js +1 -1
  455. package/dist/tasks/task-id.js +35 -0
  456. package/dist/tasks/validator.js +30 -16
  457. package/dist/workflows/authoring/authoring.js +96 -148
  458. package/dist/workflows/authoring/scope-key.js +1 -1
  459. package/dist/workflows/cli.js +0 -20
  460. package/dist/workflows/concurrency-policy.js +15 -0
  461. package/dist/workflows/exec/brief.js +25 -59
  462. package/dist/workflows/exec/frozen-judge.js +47 -0
  463. package/dist/workflows/exec/native-executor.js +157 -94
  464. package/dist/workflows/exec/report.js +365 -200
  465. package/dist/workflows/exec/run-workflow.js +46 -40
  466. package/dist/workflows/exec/scheduler.js +12 -41
  467. package/dist/workflows/exec/step-work.js +239 -205
  468. package/dist/workflows/exec/workflow-engine-gate.js +67 -0
  469. package/dist/workflows/ir/compile.js +141 -283
  470. package/dist/workflows/ir/freeze.js +233 -0
  471. package/dist/workflows/ir/plan-hash.js +40 -5
  472. package/dist/workflows/ir/schema.js +537 -1
  473. package/dist/workflows/parser.js +878 -306
  474. package/dist/workflows/program/expressions.js +20 -208
  475. package/dist/workflows/program/schema.js +7 -10
  476. package/dist/workflows/renderer.js +99 -121
  477. package/dist/workflows/resource-limits.js +22 -0
  478. package/dist/workflows/runtime/checkin.js +1 -1
  479. package/dist/workflows/runtime/plan-classifier.js +131 -0
  480. package/dist/workflows/runtime/runs.js +200 -113
  481. package/dist/workflows/runtime/unit-checkin.js +1 -1
  482. package/dist/workflows/runtime/unit-phases.js +20 -0
  483. package/dist/workflows/runtime/workflow-asset-loader.js +235 -97
  484. package/dist/workflows/schema.js +1 -11
  485. package/dist/workflows/validate-summary.js +4 -26
  486. package/dist/workflows/validator.js +52 -30
  487. package/docs/README.md +42 -78
  488. package/docs/migration/README.md +8 -0
  489. package/docs/migration/release-notes/0.6.0.md +1 -1
  490. package/docs/migration/release-notes/0.7.0.md +9 -8
  491. package/docs/migration/release-notes/0.9.0.md +158 -14
  492. package/docs/migration/v0.7-to-v0.8.md +46 -47
  493. package/docs/migration/v0.8-to-v0.9.md +844 -0
  494. package/docs/reference/README.md +12 -0
  495. package/docs/reference/data-and-telemetry.md +333 -0
  496. package/package.json +21 -17
  497. package/schemas/akm-asset-envelope.json +93 -0
  498. package/schemas/akm-config.json +4636 -0
  499. package/schemas/akm-task.json +87 -0
  500. package/{dist/schemas → schemas}/akm-workflow.json +127 -82
  501. package/dist/akm-migrate-storage +0 -38
  502. package/dist/assets/help/help-accept.md +0 -12
  503. package/dist/assets/help/help-improve.md +0 -84
  504. package/dist/assets/help/help-proposals.md +0 -17
  505. package/dist/assets/help/help-propose.md +0 -17
  506. package/dist/assets/help/help-reject.md +0 -11
  507. package/dist/assets/profiles/frequent.json +0 -13
  508. package/dist/assets/profiles/recombine-only.json +0 -21
  509. package/dist/assets/profiles/reflect-distill.json +0 -30
  510. package/dist/assets/profiles/synthesize.json +0 -15
  511. package/dist/assets/prompts/procedural-system.md +0 -44
  512. package/dist/assets/prompts/recombine-system.md +0 -40
  513. package/dist/assets/prompts/staleness-detect-system.md +0 -6
  514. package/dist/assets/tasks/core/backup.yml +0 -4
  515. package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
  516. package/dist/assets/templates/html/default.html +0 -78
  517. package/dist/assets/templates/html/vendor/echarts.min.js +0 -45
  518. package/dist/assets/wiki/index-template.md +0 -12
  519. package/dist/assets/wiki/ingest-workflow-template.md +0 -83
  520. package/dist/assets/wiki/log-template.md +0 -8
  521. package/dist/assets/wiki/schema-template.md +0 -61
  522. package/dist/cli/config-migrate.js +0 -150
  523. package/dist/cli/config-validate.js +0 -39
  524. package/dist/commands/graph/graph-cli.js +0 -124
  525. package/dist/commands/graph/graph.js +0 -487
  526. package/dist/commands/improve/calibration.js +0 -161
  527. package/dist/commands/improve/dedup.js +0 -482
  528. package/dist/commands/improve/extract-watch.js +0 -140
  529. package/dist/commands/improve/hot-probation.js +0 -45
  530. package/dist/commands/improve/improve-auto-accept.js +0 -276
  531. package/dist/commands/improve/improve-profiles.js +0 -168
  532. package/dist/commands/improve/procedural.js +0 -398
  533. package/dist/commands/improve/recombine.js +0 -818
  534. package/dist/commands/improve/schema-similarity-gate.js +0 -168
  535. package/dist/commands/lint/agent-linter.js +0 -44
  536. package/dist/commands/lint/command-linter.js +0 -44
  537. package/dist/commands/lint/default-linter.js +0 -16
  538. package/dist/commands/lint/fact-linter.js +0 -39
  539. package/dist/commands/lint/knowledge-linter.js +0 -16
  540. package/dist/commands/lint/memory-linter.js +0 -61
  541. package/dist/commands/lint/registry.js +0 -41
  542. package/dist/commands/lint/skill-linter.js +0 -45
  543. package/dist/commands/lint/task-linter.js +0 -50
  544. package/dist/commands/lint/workflow-linter.js +0 -81
  545. package/dist/commands/proposal/legacy-import.js +0 -115
  546. package/dist/commands/sources/history.js +0 -196
  547. package/dist/commands/tasks/default-tasks.js +0 -186
  548. package/dist/commands/wiki-cli.js +0 -292
  549. package/dist/core/asset/asset-registry.js +0 -76
  550. package/dist/core/asset/asset-spec.js +0 -316
  551. package/dist/core/config/config-migration.js +0 -602
  552. package/dist/core/deep-merge.js +0 -38
  553. package/dist/core/eval/rank-metrics.js +0 -113
  554. package/dist/core/ripgrep/install.js +0 -163
  555. package/dist/core/ripgrep/resolve.js +0 -81
  556. package/dist/indexer/db/db.js +0 -1414
  557. package/dist/indexer/manifest.js +0 -170
  558. package/dist/indexer/passes/metadata-contributors.js +0 -31
  559. package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -94
  560. package/dist/integrations/harnesses/opencode-sdk/index.js +0 -25
  561. package/dist/llm/call-ai.js +0 -62
  562. package/dist/llm/memory-infer-impl.js +0 -138
  563. package/dist/output/shapes/distill.js +0 -14
  564. package/dist/output/shapes/history.js +0 -11
  565. package/dist/output/text/distill.js +0 -6
  566. package/dist/output/text/enable-disable.js +0 -8
  567. package/dist/output/text/history.js +0 -6
  568. package/dist/output/text/wiki.js +0 -16
  569. package/dist/registry/build-index.js +0 -386
  570. package/dist/schemas/akm-config.json +0 -14225
  571. package/dist/scripts/migrate-storage.js +0 -13169
  572. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +0 -10169
  573. package/dist/scripts/migrations/v16-to-v17.js +0 -141
  574. package/dist/setup/legacy-config.js +0 -106
  575. package/dist/storage/repositories/consolidation-repository.js +0 -38
  576. package/dist/storage/repositories/recombine-repository.js +0 -213
  577. package/dist/wiki/wiki-templates.js +0 -15
  578. package/dist/wiki/wiki.js +0 -1012
  579. package/dist/workflows/authoring/workflow-program-template.yaml +0 -31
  580. package/dist/workflows/db.js +0 -350
  581. package/dist/workflows/exec/watch.js +0 -116
  582. package/dist/workflows/program/parser.js +0 -760
  583. package/dist/workflows/program/project.js +0 -105
  584. package/docs/data-and-telemetry.md +0 -227
  585. package/docs/migration/release-notes/0.9.0-beta.60.md +0 -19
  586. /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/registry.js +0 -0
  587. /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/youtube.js +0 -0
@@ -1,20 +1,21 @@
1
1
  // This Source Code Form is subject to the terms of the Mozilla Public
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ import { deepMergeConfig } from "../core/config/deep-merge.js";
4
5
  /**
5
6
  * Build a fresh `SetupContext` over a starting config. The returned context
6
7
  * applies deltas in-place onto an internal accumulator and exposes the
7
8
  * latest snapshot via `ctx.config`.
8
9
  */
9
10
  export function createSetupContext(initial, options) {
10
- let acc = { ...initial };
11
+ let acc = deepMergeConfig({}, initial);
11
12
  return {
12
13
  get config() {
13
14
  return acc;
14
15
  },
15
16
  nonInteractive: options.nonInteractive,
16
17
  apply(delta) {
17
- acc = { ...acc, ...delta };
18
+ acc = deepMergeConfig(acc, delta);
18
19
  },
19
20
  };
20
21
  }
@@ -0,0 +1,39 @@
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ /**
5
+ * Shared freshness-window skeleton for mirror caches (git repos, website
6
+ * snapshots). A marker file's mtime records the last successful refresh:
7
+ * within `ttlMs` the cached copy is served without refreshing; on refresh
8
+ * failure the cached copy is still served while the marker is younger than
9
+ * `staleMs`, so search stays available through upstream outages.
10
+ */
11
+ import fs from "node:fs";
12
+ import { isExpired } from "./providers/provider-utils.js";
13
+ /**
14
+ * Run the fresh → refresh → stale-fallback ladder around `refresh()`.
15
+ * Resolves when the cached copy is usable (fresh, refreshed, or stale-but-
16
+ * within-window); rethrows the refresh error otherwise.
17
+ */
18
+ export async function withFreshnessCache(options) {
19
+ const usable = options.isUsable ?? (() => true);
20
+ let mtime = 0;
21
+ try {
22
+ mtime = fs.statSync(options.markerPath).mtimeMs;
23
+ }
24
+ catch {
25
+ /* no marker — never successfully refreshed */
26
+ }
27
+ if (!options.force && mtime && !isExpired(mtime, options.ttlMs) && usable()) {
28
+ return;
29
+ }
30
+ try {
31
+ await options.refresh();
32
+ }
33
+ catch (err) {
34
+ if (options.allowStaleOnRefreshFailure !== false && mtime && !isExpired(mtime, options.staleMs) && usable()) {
35
+ return;
36
+ }
37
+ throw err;
38
+ }
39
+ }
@@ -1,7 +1,17 @@
1
1
  // This Source Code Form is subject to the terms of the Mozilla Public
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
- import { getSources } from "../core/config/config.js";
4
+ /**
5
+ * Source provider factory map.
6
+ *
7
+ * Maps source kind identifiers (e.g. "filesystem", "git", "website", "npm")
8
+ * to factory functions that build {@link SourceProvider} instances from a
9
+ * {@link SourceConfigEntry}.
10
+ *
11
+ * Distinct from the registry-discovery factory (`registry/factory.ts`).
12
+ * Both share `create-provider-registry.ts` for the underlying string→factory
13
+ * map.
14
+ */
5
15
  import { createProviderRegistry } from "../registry/create-provider-registry.js";
6
16
  // ── Factory map ─────────────────────────────────────────────────────────────
7
17
  const registry = createProviderRegistry();
@@ -11,19 +21,3 @@ export function registerSourceProvider(type, factory) {
11
21
  export function resolveSourceProviderFactory(type) {
12
22
  return registry.resolve(type);
13
23
  }
14
- /**
15
- * Build a {@link SourceProvider} for every enabled source in the config that
16
- * has a registered factory.
17
- */
18
- export function resolveSourceProviders(config) {
19
- const providers = [];
20
- for (const entry of getSources(config)) {
21
- if (entry.enabled === false)
22
- continue;
23
- const factory = registry.resolve(entry.type);
24
- if (factory) {
25
- providers.push(factory(entry));
26
- }
27
- }
28
- return providers;
29
- }
@@ -7,9 +7,8 @@ import { registerSourceProvider } from "../provider-factory.js";
7
7
  /**
8
8
  * Filesystem source — points at a directory the user already manages.
9
9
  *
10
- * Implements the v1 {@link SourceProvider} interface (spec §2.1, §2.4):
11
- * just `{ name, kind, init, path }`. No `sync()` — content is the user's
12
- * own directory, never refreshed by akm.
10
+ * Implements {@link SourceProvider} with `{ name, kind, path }`. No `sync()`:
11
+ * content is the user's own directory, never refreshed by akm.
13
12
  */
14
13
  registerSourceProvider("filesystem", (entry) => {
15
14
  if (entry.type !== "filesystem") {
@@ -5,10 +5,11 @@ import { spawnSync } from "node:child_process";
5
5
  import { randomBytes } from "node:crypto";
6
6
  import fs from "node:fs";
7
7
  import path from "node:path";
8
+ import { isWithin } from "../../core/common.js";
8
9
  import { UsageError } from "../../core/errors.js";
9
10
  import { getRegistryCacheDir } from "../../core/paths.js";
10
11
  import { parseRegistryRef, resolveRegistryArtifact, validateGitRef, validateGitUrl } from "../../registry/resolve.js";
11
- import { applyAkmIncludeConfig, buildInstallCacheDir, copyDirectoryContents, detectStashRoot, isDirectory, } from "./provider-utils.js";
12
+ import { applyAkmIncludeConfig, buildInstallCacheDir, detectStashRoot, isDirectory } from "./provider-utils.js";
12
13
  /**
13
14
  * Shared subprocess wrapper for `git` invocations. Disables git's interactive
14
15
  * terminal prompt so a missing credential never hangs the process.
@@ -20,12 +21,109 @@ export function runGit(args, options) {
20
21
  env: { ...process.env, ...options?.env, GIT_TERMINAL_PROMPT: "0" },
21
22
  });
22
23
  }
24
+ /** Fetch and classify the current branch against its upstream without changing the worktree. */
25
+ export function inspectGitUpstream(repoDir) {
26
+ const remotes = runGit(["-C", repoDir, "remote"]);
27
+ if (remotes.status !== 0)
28
+ throw new UsageError(`Cannot inspect Git remotes at ${repoDir}: ${remotes.stderr.trim()}`);
29
+ if (!remotes.stdout.trim())
30
+ return { hasRemote: false, ahead: 0, behind: 0 };
31
+ const fetch = runGit(["-C", repoDir, "fetch", "--prune"], { timeout: 120_000 });
32
+ if (fetch.status !== 0)
33
+ throw new UsageError(`Cannot refresh Git target at ${repoDir}: ${fetch.stderr.trim()}`);
34
+ const upstream = runGit(["-C", repoDir, "rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"]);
35
+ if (upstream.status !== 0 || !upstream.stdout.trim()) {
36
+ throw new UsageError(`Git target at ${repoDir} has a remote but no upstream branch; configure one before writing.`);
37
+ }
38
+ const relation = gitRelation(repoDir, upstream.stdout.trim());
39
+ return { hasRemote: true, upstream: upstream.stdout.trim(), ...relation };
40
+ }
23
41
  /**
24
- * Sync mode for a one-shot install ref (`akm add github:owner/repo` or
25
- * `akm add git:url`). Runs the clone → strip → include-filter pipeline that
26
- * historically lived in `installRegistryRef()`.
42
+ * Verify the actually-cloned HEAD matches the revision resolved before the
43
+ * clone ran (R-011). This is a presence/identity check only — full
44
+ * content-digest verification across install/update is separate, larger
45
+ * scope work. `expectedRevision` may be an annotated tag's OBJECT id (which
46
+ * `git ls-remote` reports, distinct from the commit it points to), so the
47
+ * comparison peels it (`^{commit}`) before comparing against HEAD; a plain
48
+ * commit SHA peels to itself.
27
49
  */
28
- export async function syncRegistryGitRef(ref, options) {
50
+ export function verifyClonedRevision(cloneDir, url, expectedRevision) {
51
+ if (!expectedRevision)
52
+ return;
53
+ const head = runGit(["-C", cloneDir, "rev-parse", "HEAD"]);
54
+ if (head.status !== 0 || !head.stdout.trim()) {
55
+ throw new UsageError(`Failed to read cloned HEAD at ${cloneDir}: ${head.stderr.trim() || "rev-parse failed"}`);
56
+ }
57
+ const actual = head.stdout.trim();
58
+ const peeled = runGit(["-C", cloneDir, "rev-parse", `${expectedRevision}^{commit}`]);
59
+ const expectedCommit = peeled.status === 0 ? peeled.stdout.trim() : expectedRevision;
60
+ if (actual !== expectedCommit) {
61
+ throw new UsageError(`Cloned HEAD ${actual} at ${cloneDir} does not match the revision resolved from ${url} (${expectedRevision}); refusing to install a mismatched checkout.`);
62
+ }
63
+ }
64
+ function gitRelation(repoDir, target) {
65
+ const result = runGit(["-C", repoDir, "rev-list", "--left-right", "--count", `HEAD...${target}`]);
66
+ const match = result.stdout.trim().match(/^(\d+)\s+(\d+)$/);
67
+ if (result.status !== 0 || !match) {
68
+ throw new UsageError(`Cannot compare Git history at ${repoDir}: ${result.stderr.trim() || "unknown relation"}`);
69
+ }
70
+ return { ahead: Number(match[1]), behind: Number(match[2]) };
71
+ }
72
+ /** Reject a fast-forward that would replace an ignored, untracked local path. */
73
+ export function assertNoIgnoredPathOverwrite(repoDir, targetRevision) {
74
+ const ignored = runGit(["-C", repoDir, "ls-files", "--others", "--ignored", "--exclude-standard", "-z"]);
75
+ if (ignored.status !== 0) {
76
+ throw new UsageError(`Cannot inspect ignored files at ${repoDir}: ${ignored.stderr.trim()}`);
77
+ }
78
+ const ignoredPaths = ignored.stdout.split("\0").filter(Boolean);
79
+ if (ignoredPaths.length === 0)
80
+ return;
81
+ const changed = runGit(["-C", repoDir, "diff", "--name-only", "--no-renames", "-z", "HEAD", targetRevision]);
82
+ if (changed.status !== 0) {
83
+ throw new UsageError(`Cannot inspect incoming Git paths at ${repoDir}: ${changed.stderr.trim()}`);
84
+ }
85
+ const changedPaths = changed.stdout.split("\0").filter(Boolean);
86
+ const conflict = ignoredPaths.find((ignoredPath) => changedPaths.some((changedPath) => changedPath === ignoredPath ||
87
+ changedPath.startsWith(`${ignoredPath}/`) ||
88
+ ignoredPath.startsWith(`${changedPath}/`)));
89
+ if (conflict) {
90
+ throw new UsageError(`Git update would overwrite ignored local path ${path.join(repoDir, conflict)}; move or remove it before update.`);
91
+ }
92
+ }
93
+ function normalizeRemoteUrl(value) {
94
+ return value
95
+ .trim()
96
+ .replace(/\/+$/, "")
97
+ .replace(/\.git$/i, "");
98
+ }
99
+ function replaceDirectory(stagedDir, destination) {
100
+ const backup = `${destination}.backup-${randomBytes(4).toString("hex")}`;
101
+ const hadDestination = fs.existsSync(destination);
102
+ if (hadDestination)
103
+ fs.renameSync(destination, backup);
104
+ try {
105
+ fs.renameSync(stagedDir, destination);
106
+ }
107
+ catch (error) {
108
+ if (hadDestination && fs.existsSync(backup))
109
+ fs.renameSync(backup, destination);
110
+ throw error;
111
+ }
112
+ if (hadDestination) {
113
+ try {
114
+ fs.rmSync(backup, { recursive: true, force: true });
115
+ }
116
+ catch {
117
+ // The new destination is live; a stale backup is safer than reporting a
118
+ // failed replacement after the swap already succeeded.
119
+ }
120
+ }
121
+ }
122
+ /**
123
+ * Materialize a Git install ref (`akm bundle add github:owner/repo` or
124
+ * `akm bundle add git:url`) through the clone, strip, and include-filter pipeline.
125
+ */
126
+ export async function syncGitRef(ref, options) {
29
127
  const parsed = parseRegistryRef(ref);
30
128
  if (parsed.source === "github") {
31
129
  const githubRef = {
@@ -39,22 +137,40 @@ export async function syncRegistryGitRef(ref, options) {
39
137
  return { ...result, source: "github" };
40
138
  }
41
139
  if (parsed.source !== "git") {
42
- throw new UsageError(`syncRegistryGitRef requires a git: or github: ref, got "${ref}"`);
140
+ throw new UsageError(`syncGitRef requires a git: or github: ref, got "${ref}"`);
43
141
  }
44
142
  return doSyncGit(parsed, options);
45
143
  }
46
144
  async function doSyncGit(parsed, options) {
145
+ validateGitUrl(parsed.url);
146
+ if (parsed.requestedRef)
147
+ validateGitRef(parsed.requestedRef);
47
148
  const resolved = await resolveRegistryArtifact(parsed);
48
149
  const syncedAt = (options?.now ?? new Date()).toISOString();
150
+ if (options?.writable && options.writableRoot) {
151
+ return syncExistingWritableCheckout(parsed, resolved, options.writableRoot, syncedAt, options.writableRequiredRoots);
152
+ }
49
153
  const cacheRootDir = options?.cacheRootDir ?? getRegistryCacheDir();
50
- const cacheDir = buildInstallCacheDir(cacheRootDir, parsed.source, parsed.id, resolved.resolvedRevision);
154
+ const cacheDir = buildInstallCacheDir(cacheRootDir, parsed.source, parsed.id, options?.writable ? "writable" : resolved.resolvedRevision);
51
155
  const cloneDir = path.join(cacheDir, "clone");
52
156
  const extractedDir = path.join(cacheDir, "extracted");
53
- // Cache hit
54
- if (!options?.force && isDirectory(extractedDir)) {
157
+ // Cache hit. Writable installs must remain real checkouts so every mutation
158
+ // can be committed; an older extracted snapshot is not eligible.
159
+ if (isDirectory(extractedDir) && (!options?.writable || isDirectory(path.join(extractedDir, ".git")))) {
160
+ if (options?.writable) {
161
+ const provisionalBundleRoot = detectStashRoot(extractedDir);
162
+ const installRoot = applyAkmIncludeConfig(provisionalBundleRoot, cacheDir, extractedDir) ?? provisionalBundleRoot;
163
+ if (installRoot !== provisionalBundleRoot) {
164
+ throw new UsageError("Writable Git installs do not support akm.include (package.json) filtered snapshots.");
165
+ }
166
+ return syncExistingWritableCheckout(parsed, resolved, detectStashRoot(installRoot), syncedAt, options.writableRequiredRoots);
167
+ }
55
168
  try {
56
- const provisionalKitRoot = detectStashRoot(extractedDir);
57
- const installRoot = applyAkmIncludeConfig(provisionalKitRoot, cacheDir, extractedDir) ?? provisionalKitRoot;
169
+ if (options?.force) {
170
+ throw new Error("refresh read-only snapshot");
171
+ }
172
+ const provisionalBundleRoot = detectStashRoot(extractedDir);
173
+ const installRoot = applyAkmIncludeConfig(provisionalBundleRoot, cacheDir, extractedDir) ?? provisionalBundleRoot;
58
174
  const stashRoot = detectStashRoot(installRoot);
59
175
  if (stashRoot) {
60
176
  return {
@@ -72,20 +188,20 @@ async function doSyncGit(parsed, options) {
72
188
  };
73
189
  }
74
190
  }
75
- catch {
191
+ catch (error) {
76
192
  // Cache invalid, re-clone
77
193
  }
78
194
  }
195
+ const cacheExisted = fs.existsSync(cacheDir);
79
196
  fs.mkdirSync(cacheDir, { recursive: true });
80
- // Validate URL and ref before passing to git to prevent command injection
81
- validateGitUrl(parsed.url);
82
- if (parsed.requestedRef)
83
- validateGitRef(parsed.requestedRef);
84
- let provisionalKitRoot;
197
+ fs.rmSync(cloneDir, { recursive: true, force: true });
198
+ let provisionalBundleRoot;
85
199
  let installRoot;
86
200
  let stashRoot;
87
201
  try {
88
- const cloneArgs = ["clone", "--depth", "1"];
202
+ const cloneArgs = ["clone"];
203
+ if (!options?.writable)
204
+ cloneArgs.push("--depth", "1");
89
205
  if (parsed.requestedRef) {
90
206
  cloneArgs.push("--branch", parsed.requestedRef);
91
207
  }
@@ -94,20 +210,40 @@ async function doSyncGit(parsed, options) {
94
210
  if (cloneResult.status !== 0) {
95
211
  throw new Error(classifyCloneFailure(parsed.url, cloneResult.stderr, cloneResult.error));
96
212
  }
97
- // Copy contents to extracted dir without .git
98
- fs.mkdirSync(extractedDir, { recursive: true });
99
- copyDirectoryContents(cloneDir, extractedDir);
100
- // Clean up the clone dir
101
- fs.rmSync(cloneDir, { recursive: true, force: true });
102
- provisionalKitRoot = detectStashRoot(extractedDir);
103
- installRoot = applyAkmIncludeConfig(provisionalKitRoot, cacheDir, extractedDir) ?? provisionalKitRoot;
213
+ // R-011: `resolved.resolvedRevision` was resolved via a SEPARATE
214
+ // `git ls-remote` round-trip before this clone ran (resolveGitArtifact /
215
+ // resolveGithubArtifact in registry/resolve.ts) and was never checked
216
+ // against what actually got cloned. Verify it now, while `.git` still
217
+ // exists (the read-only branch below strips it).
218
+ verifyClonedRevision(cloneDir, parsed.url, resolved.resolvedRevision);
219
+ if (options?.writable) {
220
+ const branch = runGit(["-C", cloneDir, "branch", "--show-current"]);
221
+ if (branch.status !== 0 || !branch.stdout.trim()) {
222
+ throw new UsageError("Writable Git installs require a branch ref; tags and detached revisions are read-only.");
223
+ }
224
+ const stagedRoot = detectStashRoot(cloneDir);
225
+ if (applyAkmIncludeConfig(stagedRoot, cacheDir, cloneDir)) {
226
+ throw new UsageError("Writable Git installs do not support akm.include (package.json) filtered snapshots.");
227
+ }
228
+ replaceDirectory(cloneDir, extractedDir);
229
+ }
230
+ else {
231
+ // Read-only installs are immutable snapshots and do not retain Git metadata.
232
+ fs.rmSync(path.join(cloneDir, ".git"), { recursive: true, force: true });
233
+ replaceDirectory(cloneDir, extractedDir);
234
+ }
235
+ provisionalBundleRoot = detectStashRoot(extractedDir);
236
+ installRoot = applyAkmIncludeConfig(provisionalBundleRoot, cacheDir, extractedDir) ?? provisionalBundleRoot;
237
+ if (options?.writable && installRoot !== provisionalBundleRoot) {
238
+ throw new UsageError("Writable Git installs do not support akm.include (package.json) filtered snapshots.");
239
+ }
104
240
  stashRoot = detectStashRoot(installRoot);
105
241
  }
106
242
  catch (err) {
107
- // Clean up the cache directory so stale or partially-cloned artifacts
108
- // don't cause false cache hits on the next install attempt.
243
+ fs.rmSync(cloneDir, { recursive: true, force: true });
109
244
  try {
110
- fs.rmSync(cacheDir, { recursive: true, force: true });
245
+ if (!cacheExisted)
246
+ fs.rmSync(cacheDir, { recursive: true, force: true });
111
247
  }
112
248
  catch {
113
249
  /* best-effort */
@@ -128,12 +264,123 @@ async function doSyncGit(parsed, options) {
128
264
  syncedAt,
129
265
  };
130
266
  }
267
+ export function syncExistingWritableCheckout(parsed, resolved, contentRoot, syncedAt, requiredRoots = []) {
268
+ const root = path.resolve(contentRoot);
269
+ const repoResult = runGit(["-C", root, "rev-parse", "--show-toplevel"]);
270
+ if (repoResult.status !== 0 || !repoResult.stdout.trim()) {
271
+ throw new UsageError(`Writable Git install at ${root} is not a checkout; refusing to replace it because it may contain local work.`);
272
+ }
273
+ const repoDir = path.resolve(repoResult.stdout.trim());
274
+ if (!isWithin(root, repoDir)) {
275
+ throw new UsageError(`Writable Git content root ${root} resolves outside its checkout at ${repoDir}.`);
276
+ }
277
+ const remote = runGit(["-C", repoDir, "remote", "get-url", "origin"]);
278
+ if (remote.status !== 0 || normalizeRemoteUrl(remote.stdout) !== normalizeRemoteUrl(parsed.url)) {
279
+ throw new UsageError(`Writable Git install at ${root} points at a different origin; refusing to update or replace the checkout.`);
280
+ }
281
+ const status = runGit(["-C", repoDir, "status", "--porcelain"]);
282
+ if (status.status !== 0 || status.stdout.trim()) {
283
+ throw new UsageError(`Writable Git install at ${root} has uncommitted changes; commit or discard them before update.`);
284
+ }
285
+ if (parsed.requestedRef) {
286
+ const branch = runGit(["-C", repoDir, "branch", "--show-current"]);
287
+ const expectedBranch = parsed.requestedRef.replace(/^refs\/heads\//, "");
288
+ if (branch.status !== 0 || !branch.stdout.trim() || branch.stdout.trim() !== expectedBranch) {
289
+ throw new UsageError(`Writable Git install at ${root} is checked out on a different branch than requested ref "${parsed.requestedRef}".`);
290
+ }
291
+ }
292
+ const shallow = runGit(["-C", repoDir, "rev-parse", "--is-shallow-repository"]);
293
+ const fetchArgs = ["-C", repoDir, "fetch", "--prune", "--tags"];
294
+ if (shallow.status === 0 && shallow.stdout.trim() === "true")
295
+ fetchArgs.push("--unshallow");
296
+ fetchArgs.push("origin");
297
+ if (parsed.requestedRef)
298
+ fetchArgs.push(parsed.requestedRef);
299
+ const fetch = runGit(fetchArgs, { timeout: 120_000 });
300
+ if (fetch.status !== 0) {
301
+ throw new UsageError(`Writable Git install at ${root} could not fetch its expected origin; local work was preserved. ${fetch.stderr.trim()}`);
302
+ }
303
+ let targetRevision = resolved.resolvedRevision;
304
+ if (targetRevision) {
305
+ const resolvedTarget = runGit(["-C", repoDir, "rev-parse", "--verify", `${targetRevision}^{commit}`]);
306
+ if (resolvedTarget.status === 0)
307
+ targetRevision = resolvedTarget.stdout.trim();
308
+ else
309
+ targetRevision = undefined;
310
+ }
311
+ if (!targetRevision) {
312
+ const fallbackTarget = runGit([
313
+ "-C",
314
+ repoDir,
315
+ "rev-parse",
316
+ "--verify",
317
+ parsed.requestedRef ? "FETCH_HEAD" : "@{u}",
318
+ ]);
319
+ if (fallbackTarget.status !== 0 || !fallbackTarget.stdout.trim()) {
320
+ throw new UsageError(`Writable Git install at ${root} has no verifiable upstream revision.`);
321
+ }
322
+ targetRevision = fallbackTarget.stdout.trim();
323
+ }
324
+ const relation = gitRelation(repoDir, targetRevision);
325
+ if (relation.ahead > 0) {
326
+ throw new UsageError(`Writable Git install at ${root} has local commits that are not in the requested upstream revision; push or reconcile them before update.`);
327
+ }
328
+ assertNoIgnoredPathOverwrite(repoDir, targetRevision);
329
+ const rootsToPreserve = [...new Set([root, ...requiredRoots.map((candidate) => path.resolve(candidate))])];
330
+ for (const requiredRoot of rootsToPreserve) {
331
+ if (!isWithin(requiredRoot, repoDir)) {
332
+ throw new UsageError(`Configured Git component root ${requiredRoot} resolves outside ${repoDir}.`);
333
+ }
334
+ const relative = path.relative(repoDir, requiredRoot).replaceAll(path.sep, "/");
335
+ if (!relative)
336
+ continue;
337
+ const tree = runGit(["-C", repoDir, "ls-tree", "-d", "-z", "--name-only", targetRevision, "--", relative]);
338
+ const names = tree.stdout.split("\0").filter(Boolean);
339
+ if (tree.status !== 0 || !names.includes(relative)) {
340
+ throw new UsageError(`Writable Git update would remove configured content root ${requiredRoot}; the existing checkout was left unchanged.`);
341
+ }
342
+ }
343
+ if (relation.behind > 0) {
344
+ const statusBeforeMerge = runGit(["-C", repoDir, "status", "--porcelain"]);
345
+ if (statusBeforeMerge.status !== 0 || statusBeforeMerge.stdout.trim()) {
346
+ throw new UsageError(`Writable Git install at ${root} changed while its update was prepared; local work was preserved.`);
347
+ }
348
+ assertNoIgnoredPathOverwrite(repoDir, targetRevision);
349
+ const merge = runGit(["-C", repoDir, "merge", "--ff-only", "--no-overwrite-ignore", targetRevision], {
350
+ timeout: 120_000,
351
+ });
352
+ if (merge.status !== 0) {
353
+ throw new UsageError(`Writable Git install at ${root} cannot fast-forward; local commits were preserved. ${merge.stderr.trim()}`);
354
+ }
355
+ }
356
+ for (const requiredRoot of rootsToPreserve) {
357
+ if (!isDirectory(requiredRoot)) {
358
+ throw new UsageError(`Writable Git update did not preserve configured content root ${requiredRoot}.`);
359
+ }
360
+ }
361
+ const head = runGit(["-C", repoDir, "rev-parse", "HEAD"]);
362
+ return {
363
+ id: resolved.id,
364
+ source: resolved.source,
365
+ ref: resolved.ref,
366
+ artifactUrl: resolved.artifactUrl,
367
+ resolvedVersion: resolved.resolvedVersion,
368
+ resolvedRevision: head.status === 0 ? head.stdout.trim() : targetRevision,
369
+ contentDir: root,
370
+ cacheDir: repoDir,
371
+ extractedDir: repoDir,
372
+ writable: true,
373
+ syncedAt,
374
+ };
375
+ }
131
376
  export function cloneRepo(cloneUrl, ref, destDir, writable = false) {
132
377
  // Stage the clone into a sibling temp dir so that a failed clone never
133
378
  // destroys a previously-valid destDir (e.g. when the remote is temporarily
134
379
  // unreachable and we have a valid cached copy).
135
380
  const tmpDir = `${destDir}.tmp-${randomBytes(4).toString("hex")}`;
136
- const args = ["clone", "--depth", "1"];
381
+ const args = ["clone"];
382
+ if (!writable)
383
+ args.push("--depth", "1");
137
384
  if (ref)
138
385
  args.push("--branch", ref);
139
386
  args.push(cloneUrl, tmpDir);
@@ -150,10 +397,7 @@ export function cloneRepo(cloneUrl, ref, destDir, writable = false) {
150
397
  if (fs.existsSync(gitDir))
151
398
  fs.rmSync(gitDir, { recursive: true, force: true });
152
399
  }
153
- // Swap: remove the old destDir (if any) then atomically rename tmpDir into place.
154
- if (fs.existsSync(destDir))
155
- fs.rmSync(destDir, { recursive: true, force: true });
156
- fs.renameSync(tmpDir, destDir);
400
+ replaceDirectory(tmpDir, destDir);
157
401
  }
158
402
  catch (err) {
159
403
  // Post-clone steps failed — clean up the temp dir to avoid orphaned dirs.
@@ -164,7 +408,7 @@ export function cloneRepo(cloneUrl, ref, destDir, writable = false) {
164
408
  // ── Clone-failure classification (#487) ─────────────────────────────────────
165
409
  /**
166
410
  * Translate git's stderr into an actionable message. Without this, a user
167
- * who passes a nonexistent or private repo to `akm add` sees:
411
+ * who passes a nonexistent or private repo to `akm bundle add` sees:
168
412
  *
169
413
  * "could not read Username for 'https://github.com': No such device or
170
414
  * address"