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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (598) hide show
  1. package/CHANGELOG.md +1283 -22
  2. package/README.md +62 -37
  3. package/SECURITY.md +46 -31
  4. package/dist/akm +162 -38
  5. package/dist/akm-migrate +44 -0
  6. package/dist/assets/backends/schtasks-template.xml +2 -1
  7. package/dist/assets/hints/cli-hints-full.md +268 -118
  8. package/dist/assets/hints/cli-hints-short.md +87 -24
  9. package/dist/assets/{profiles → improve-strategies}/catchup.json +3 -1
  10. package/dist/assets/{profiles → improve-strategies}/consolidate.json +3 -1
  11. package/dist/assets/{profiles → improve-strategies}/default.json +6 -7
  12. package/dist/assets/improve-strategies/frequent.json +15 -0
  13. package/dist/assets/{profiles → improve-strategies}/graph-refresh.json +4 -2
  14. package/dist/assets/{profiles → improve-strategies}/memory-focus.json +4 -1
  15. package/dist/assets/{profiles → improve-strategies}/proactive-maintenance.json +5 -5
  16. package/dist/assets/{profiles → improve-strategies}/quick.json +4 -2
  17. package/dist/assets/improve-strategies/reflect-distill.json +30 -0
  18. package/dist/assets/{profiles → improve-strategies}/thorough.json +1 -1
  19. package/dist/assets/prompts/consolidate-system.md +5 -5
  20. package/dist/assets/prompts/extract-session.md +2 -6
  21. package/dist/assets/prompts/memory-infer-user.md +2 -3
  22. package/dist/assets/prompts/reflect-llm-framed-contract.md +11 -0
  23. package/dist/assets/prompts/reflect-llm-schema-contract.md +3 -0
  24. package/dist/assets/prompts/reflect-output-repair.md +3 -0
  25. package/dist/assets/prompts/workflow-unit-preamble.md +26 -0
  26. package/dist/assets/stash-skeleton/README.md +38 -10
  27. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +8 -0
  28. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +8 -0
  29. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +14 -1
  30. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +13 -1
  31. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +9 -1
  32. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +11 -0
  33. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +9 -0
  34. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +9 -0
  35. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +8 -0
  36. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +100 -0
  37. package/dist/assets/stash-skeleton/facts/conventions/domains.md +64 -0
  38. package/dist/assets/stash-skeleton/facts/conventions/organization.md +136 -0
  39. package/dist/assets/tasks/core/extract.yml +3 -2
  40. package/dist/assets/tasks/core/improve.yml +2 -1
  41. package/dist/assets/tasks/core/index-refresh.yml +1 -0
  42. package/dist/assets/tasks/core/sync.yml +1 -0
  43. package/dist/assets/tasks/core/version-check.yml +2 -1
  44. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +5 -0
  45. package/dist/assets/tasks/improve/akm-improve-catchup.yml +8 -0
  46. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +5 -0
  47. package/dist/assets/tasks/improve/akm-improve-frequent.yml +5 -0
  48. package/dist/assets/tasks/improve/akm-improve-nightly.yml +5 -0
  49. package/dist/assets/templates/html/health.html +5 -4
  50. package/dist/assets/workflows/workflow-template.md +31 -15
  51. package/dist/cli/invocation.js +279 -0
  52. package/dist/cli/parse-args.js +5 -90
  53. package/dist/cli/retired-commands.js +78 -0
  54. package/dist/cli/shared.js +158 -48
  55. package/dist/cli-node.mjs +2 -1
  56. package/dist/cli.js +747 -293
  57. package/dist/commands/agent/agent-dispatch.js +19 -18
  58. package/dist/commands/agent/agent-support.js +0 -24
  59. package/dist/commands/agent/contribute-cli.js +43 -97
  60. package/dist/commands/completions.js +80 -23
  61. package/dist/commands/config-cli.js +44 -281
  62. package/dist/commands/env/env-binding.js +99 -0
  63. package/dist/commands/env/env-cli.js +84 -224
  64. package/dist/commands/env/env.js +12 -163
  65. package/dist/commands/env/marker-path.js +6 -0
  66. package/dist/commands/env/secret-cli.js +45 -61
  67. package/dist/commands/env/secret.js +32 -62
  68. package/dist/commands/feedback-cli.js +179 -85
  69. package/dist/commands/health/accept-rate.js +58 -0
  70. package/dist/commands/health/advisories.js +7 -8
  71. package/dist/commands/health/checks.js +279 -94
  72. package/dist/commands/health/html-report.js +197 -578
  73. package/dist/commands/health/improve-metrics.js +277 -246
  74. package/dist/commands/health/llm-usage.js +19 -19
  75. package/dist/commands/health/md-report.js +16 -7
  76. package/dist/commands/health/metrics.js +67 -32
  77. package/dist/commands/health/renderers.js +47 -0
  78. package/dist/commands/health/report-view-model.js +508 -0
  79. package/dist/commands/health/stash-exposure.js +1 -1
  80. package/dist/commands/health/surfaces.js +16 -56
  81. package/dist/commands/health/task-runs.js +3 -67
  82. package/dist/{migrate-storage-node.mjs → commands/health/types-checks.js} +1 -5
  83. package/dist/commands/health/types-improve.js +29 -0
  84. package/dist/{output/text/save.js → commands/health/types-metrics.js} +1 -2
  85. package/dist/commands/health/types-result.js +7 -0
  86. package/dist/commands/health/types-runs.js +4 -0
  87. package/dist/commands/health/types-session-log.js +4 -0
  88. package/dist/commands/health/types-windows.js +4 -0
  89. package/dist/commands/health/types.js +26 -21
  90. package/dist/commands/health/windows.js +2 -3
  91. package/dist/commands/health.js +296 -167
  92. package/dist/commands/improve/anti-collapse.js +5 -5
  93. package/dist/commands/improve/autonomy-gate.js +68 -0
  94. package/dist/commands/improve/collapse-detector.js +65 -52
  95. package/dist/commands/improve/consolidate/chunking.js +9 -7
  96. package/dist/commands/improve/consolidate/eligibility.js +1 -23
  97. package/dist/commands/improve/consolidate/merge.js +4 -0
  98. package/dist/commands/improve/consolidate.js +454 -1354
  99. package/dist/commands/improve/content-hash.js +39 -0
  100. package/dist/commands/improve/distill/content-repair.js +4 -10
  101. package/dist/commands/improve/distill/promote-memory.js +89 -64
  102. package/dist/commands/improve/distill/quality-gate.js +118 -42
  103. package/dist/commands/improve/distill-guards.js +1 -1
  104. package/dist/commands/improve/distill-promotion-policy.js +33 -888
  105. package/dist/commands/improve/distill.js +607 -363
  106. package/dist/commands/improve/eligibility.js +165 -79
  107. package/dist/commands/improve/extract-cli.js +35 -126
  108. package/dist/commands/improve/extract-prompt.js +6 -35
  109. package/dist/commands/improve/extract.js +640 -391
  110. package/dist/commands/improve/feedback-valence.js +2 -12
  111. package/dist/commands/improve/improve-cli.js +134 -135
  112. package/dist/commands/improve/improve-result-file.js +30 -50
  113. package/dist/commands/improve/improve-run-types.js +4 -0
  114. package/dist/commands/improve/improve-strategies.js +135 -0
  115. package/dist/commands/improve/improve.js +904 -701
  116. package/dist/commands/improve/locks.js +64 -111
  117. package/dist/commands/improve/loop-stages.js +1110 -923
  118. package/dist/commands/improve/memory/derived-ref.js +124 -0
  119. package/dist/commands/improve/memory/memory-belief.js +79 -7
  120. package/dist/commands/improve/memory/memory-contradiction-detect.js +49 -52
  121. package/dist/commands/improve/memory/memory-improve.js +25 -37
  122. package/dist/commands/improve/outcome-loop.js +25 -88
  123. package/dist/commands/improve/preparation.js +1034 -813
  124. package/dist/commands/improve/proactive-maintenance.js +34 -9
  125. package/dist/commands/improve/proposal-envelope.js +31 -0
  126. package/dist/commands/improve/reflect.js +983 -794
  127. package/dist/commands/improve/run-context.js +119 -0
  128. package/dist/commands/improve/salience.js +24 -127
  129. package/dist/commands/improve/session-asset.js +7 -3
  130. package/dist/commands/improve/shared.js +14 -34
  131. package/dist/commands/improve/source-identity.js +28 -0
  132. package/dist/commands/improve/triage.js +20 -17
  133. package/dist/commands/lint/base-linter.js +340 -313
  134. package/dist/commands/lint/env-key-rules.js +31 -47
  135. package/dist/commands/lint/index.js +185 -30
  136. package/dist/commands/{events.js → log.js} +28 -38
  137. package/dist/commands/migrate-cli.js +54 -0
  138. package/dist/commands/migration-tool.js +55 -0
  139. package/dist/commands/observability-cli.js +70 -208
  140. package/dist/commands/proposal/diff-format.js +50 -0
  141. package/dist/commands/proposal/drain-policies.js +0 -6
  142. package/dist/commands/proposal/drain.js +91 -40
  143. package/dist/commands/proposal/proposal-cli.js +134 -132
  144. package/dist/commands/proposal/proposal-types.js +56 -0
  145. package/dist/commands/proposal/proposal.js +83 -65
  146. package/dist/commands/proposal/propose-cli.js +88 -0
  147. package/dist/commands/proposal/propose.js +105 -88
  148. package/dist/commands/proposal/repository.js +1303 -278
  149. package/dist/commands/proposal/validators/proposal-quality-validators.js +16 -6
  150. package/dist/commands/proposal/validators/proposal-validators.js +61 -12
  151. package/dist/commands/proposal/validators/proposals.js +6 -8
  152. package/dist/commands/read/curate.js +78 -73
  153. package/dist/commands/read/knowledge.js +510 -13
  154. package/dist/commands/read/registry-search.js +2 -2
  155. package/dist/commands/read/remember-cli.js +84 -15
  156. package/dist/commands/read/search-cli.js +203 -96
  157. package/dist/commands/read/search.js +126 -94
  158. package/dist/commands/read/show.js +226 -250
  159. package/dist/commands/registry-cli.js +34 -60
  160. package/dist/commands/remember.js +18 -57
  161. package/dist/commands/sources/add-cli.js +104 -49
  162. package/dist/commands/sources/bundle-cli.js +166 -0
  163. package/dist/commands/sources/bundle-config-ops.js +63 -0
  164. package/dist/commands/sources/info.js +27 -15
  165. package/dist/commands/sources/init.js +30 -40
  166. package/dist/commands/sources/installed-stashes.js +469 -172
  167. package/dist/commands/sources/migration-help.js +7 -4
  168. package/dist/commands/sources/schema-repair.js +10 -9
  169. package/dist/commands/sources/self-update.js +182 -121
  170. package/dist/commands/sources/source-add.js +169 -178
  171. package/dist/commands/sources/source-clone.js +144 -41
  172. package/dist/commands/sources/source-manage.js +94 -59
  173. package/dist/commands/sources/sources-cli.js +64 -205
  174. package/dist/commands/sources/stash-cli.js +91 -54
  175. package/dist/commands/sources/stash-skeleton.js +1 -1
  176. package/dist/commands/tasks/tasks-cli.js +106 -104
  177. package/dist/commands/tasks/tasks.js +445 -262
  178. package/dist/commands/workflow-cli.js +232 -121
  179. package/dist/core/action-contributors.js +1 -1
  180. package/dist/core/activation-policy.js +49 -0
  181. package/dist/core/adapter/adapters/agent-skills-adapter.js +181 -0
  182. package/dist/core/adapter/adapters/akm-adapter.js +528 -0
  183. package/dist/core/adapter/adapters/akm-lint.js +392 -0
  184. package/dist/core/adapter/adapters/akm-metadata.js +387 -0
  185. package/dist/core/adapter/adapters/akm-task-adapter.js +149 -0
  186. package/dist/core/adapter/adapters/akm-workflow-adapter.js +180 -0
  187. package/dist/core/adapter/adapters/claude-adapter.js +61 -0
  188. package/dist/core/adapter/adapters/dotenv-adapter.js +187 -0
  189. package/dist/core/adapter/adapters/generic-files-adapter.js +119 -0
  190. package/dist/core/adapter/adapters/index.js +80 -0
  191. package/dist/core/adapter/adapters/llm-wiki-adapter.js +419 -0
  192. package/dist/core/adapter/adapters/okf-adapter.js +391 -0
  193. package/dist/core/adapter/adapters/opencode-adapter.js +68 -0
  194. package/dist/core/adapter/adapters/shared.js +286 -0
  195. package/dist/core/adapter/adapters/tool-dir-shared.js +217 -0
  196. package/dist/core/adapter/adapters/website-snapshot-adapter.js +155 -0
  197. package/dist/core/adapter/bundle-adapter.js +4 -0
  198. package/dist/core/adapter/detect-adapter.js +17 -0
  199. package/dist/core/adapter/recognize-match.js +44 -0
  200. package/dist/core/adapter/registry.js +56 -0
  201. package/dist/core/adapter/types.js +4 -0
  202. package/dist/core/asset/akm-markdown.js +30 -0
  203. package/dist/core/asset/asset-placement.js +243 -0
  204. package/dist/core/asset/asset-ref.js +110 -79
  205. package/dist/core/asset/asset-serialize.js +20 -0
  206. package/dist/core/asset/frontmatter.js +28 -12
  207. package/dist/core/asset/markdown.js +40 -51
  208. package/dist/core/asset/resolve-ref.js +274 -0
  209. package/dist/core/asset/stash-meta.js +2 -2
  210. package/dist/core/bundle-id.js +51 -0
  211. package/dist/core/common.js +281 -86
  212. package/dist/core/config/config-io.js +42 -128
  213. package/dist/core/config/config-schema.js +233 -834
  214. package/dist/core/config/config-sources.js +162 -39
  215. package/dist/core/config/config-types.js +16 -11
  216. package/dist/core/config/config-version.js +29 -0
  217. package/dist/core/config/config-walker.js +126 -37
  218. package/dist/core/config/config.js +154 -331
  219. package/dist/core/config/deep-merge.js +41 -0
  220. package/dist/core/config/engine-semantics.js +28 -0
  221. package/dist/core/config/experimental.js +21 -0
  222. package/dist/core/config/schema/embedding.js +38 -0
  223. package/dist/core/config/schema/engines.js +116 -0
  224. package/dist/core/config/schema/experimental.js +47 -0
  225. package/dist/core/config/schema/feedback.js +31 -0
  226. package/dist/core/config/schema/improve-processes.js +389 -0
  227. package/dist/core/config/schema/improve.js +94 -0
  228. package/dist/core/config/schema/index-config.js +176 -0
  229. package/dist/core/config/schema/output.js +18 -0
  230. package/dist/core/config/schema/primitives.js +94 -0
  231. package/dist/core/config/schema/search.js +30 -0
  232. package/dist/core/config/schema/setup.js +18 -0
  233. package/dist/core/config/schema/sources-bundles.js +169 -0
  234. package/dist/core/config/schema/workflow.js +29 -0
  235. package/dist/core/env-secret-ref.js +155 -20
  236. package/dist/core/errors.js +17 -15
  237. package/dist/core/events-types.js +4 -0
  238. package/dist/core/events.js +46 -128
  239. package/dist/core/extra-params.js +62 -0
  240. package/dist/core/file-change.js +17 -0
  241. package/dist/core/file-lock.js +202 -57
  242. package/dist/core/fs-txn.js +392 -0
  243. package/dist/core/git-message.js +59 -0
  244. package/dist/core/improve-result.js +167 -0
  245. package/dist/core/json-schema.js +142 -0
  246. package/dist/core/lesson-lint.js +1 -17
  247. package/dist/core/logs-db.js +1 -1
  248. package/dist/core/maintenance-barrier.js +135 -0
  249. package/dist/core/migration-operation.js +44 -0
  250. package/dist/core/mutation-target.js +78 -0
  251. package/dist/core/paths.js +22 -25
  252. package/dist/core/platform.js +10 -0
  253. package/dist/core/recognition-util.js +128 -0
  254. package/dist/core/redaction.js +392 -0
  255. package/dist/core/standards/resolve-standards-context.js +36 -65
  256. package/dist/core/standards/resolve-stash-standards.js +2 -2
  257. package/dist/core/standards/resolve-type-conventions.js +5 -5
  258. package/dist/core/state/migrations.js +242 -11
  259. package/dist/core/state-db.js +98 -10
  260. package/dist/core/structured.js +1 -1
  261. package/dist/core/subprocess.js +303 -0
  262. package/dist/core/text-truncation.js +9 -5
  263. package/dist/core/time.js +20 -0
  264. package/dist/core/type-presentation.js +130 -0
  265. package/dist/core/warn.js +0 -3
  266. package/dist/core/write-source.js +834 -118
  267. package/dist/indexer/bundle-identity-guard.js +92 -0
  268. package/dist/indexer/db/graph-db.js +1 -25
  269. package/dist/indexer/db/llm-cache.js +1 -1
  270. package/dist/indexer/ensure-index.js +30 -9
  271. package/dist/indexer/graph/graph-boost.js +9 -30
  272. package/dist/indexer/graph/graph-extraction.js +41 -27
  273. package/dist/indexer/graph/graph-types.js +4 -0
  274. package/dist/indexer/index-writer-lock.js +93 -49
  275. package/dist/indexer/index-written-assets.js +100 -53
  276. package/dist/indexer/indexer.js +746 -329
  277. package/dist/indexer/init.js +18 -25
  278. package/dist/indexer/installations.js +142 -0
  279. package/dist/indexer/passes/dir-staleness.js +18 -10
  280. package/dist/indexer/passes/memory-inference.js +25 -15
  281. package/dist/indexer/passes/metadata.js +412 -243
  282. package/dist/indexer/scan/doc-to-entry.js +160 -0
  283. package/dist/indexer/scan/drain-dir.js +134 -0
  284. package/dist/indexer/search/db-search.js +292 -108
  285. package/dist/indexer/search/fts-query.js +64 -0
  286. package/dist/indexer/search/ranking-contributors.js +145 -25
  287. package/dist/indexer/search/ranking-types.js +4 -0
  288. package/dist/indexer/search/ranking.js +28 -71
  289. package/dist/indexer/search/search-attribution.js +67 -0
  290. package/dist/indexer/search/search-fields.js +18 -3
  291. package/dist/indexer/search/search-hit-enrichers.js +30 -40
  292. package/dist/indexer/search/search-source.js +157 -111
  293. package/dist/indexer/search/semantic-status.js +4 -1
  294. package/dist/indexer/usage/usage-events.js +10 -30
  295. package/dist/indexer/walk/file-context.js +3 -45
  296. package/dist/indexer/walk/matchers.js +42 -34
  297. package/dist/indexer/walk/path-resolver.js +11 -5
  298. package/dist/indexer/walk/walker.js +42 -14
  299. package/dist/integrations/agent/builder-shared.js +7 -0
  300. package/dist/integrations/agent/builders.js +5 -56
  301. package/dist/integrations/agent/config.js +3 -143
  302. package/dist/integrations/agent/detect.js +17 -2
  303. package/dist/integrations/agent/engine-resolution.js +231 -0
  304. package/dist/integrations/agent/index.js +1 -2
  305. package/dist/integrations/agent/model-aliases.js +16 -2
  306. package/dist/integrations/agent/profiles.js +36 -62
  307. package/dist/integrations/agent/prompts.js +46 -18
  308. package/dist/integrations/agent/runner-dispatch.js +93 -4
  309. package/dist/integrations/agent/runner.js +76 -208
  310. package/dist/integrations/agent/spawn.js +88 -196
  311. package/dist/integrations/harnesses/aider/agent-builder.js +114 -0
  312. package/dist/integrations/harnesses/aider/index.js +48 -0
  313. package/dist/integrations/harnesses/aider/result-extractor.js +53 -0
  314. package/dist/integrations/harnesses/amazonq/agent-builder.js +147 -0
  315. package/dist/integrations/harnesses/amazonq/index.js +45 -0
  316. package/dist/integrations/harnesses/amazonq/result-extractor.js +48 -0
  317. package/dist/integrations/harnesses/claude/agent-builder.js +46 -8
  318. package/dist/integrations/harnesses/claude/config-import.js +1 -3
  319. package/dist/integrations/harnesses/claude/index.js +24 -35
  320. package/dist/integrations/harnesses/claude/result-extractor.js +52 -0
  321. package/dist/integrations/harnesses/claude/session-log.js +27 -75
  322. package/dist/integrations/harnesses/codex/agent-builder.js +138 -0
  323. package/dist/integrations/harnesses/codex/index.js +52 -0
  324. package/dist/integrations/harnesses/codex/result-extractor.js +73 -0
  325. package/dist/integrations/harnesses/copilot/agent-builder.js +122 -0
  326. package/dist/integrations/harnesses/copilot/index.js +48 -0
  327. package/dist/integrations/harnesses/copilot/result-extractor.js +151 -0
  328. package/dist/integrations/harnesses/gemini/agent-builder.js +120 -0
  329. package/dist/integrations/harnesses/gemini/index.js +48 -0
  330. package/dist/integrations/harnesses/gemini/result-extractor.js +121 -0
  331. package/dist/integrations/harnesses/ids.js +24 -0
  332. package/dist/integrations/harnesses/index.js +54 -34
  333. package/dist/integrations/harnesses/opencode/agent-builder.js +23 -5
  334. package/dist/integrations/harnesses/opencode/config-import.js +1 -3
  335. package/dist/integrations/harnesses/opencode/index.js +14 -32
  336. package/dist/integrations/harnesses/opencode/session-log.js +67 -125
  337. package/dist/integrations/harnesses/opencode-sdk/harness.js +51 -0
  338. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +681 -108
  339. package/dist/integrations/harnesses/openhands/agent-builder.js +128 -0
  340. package/dist/integrations/harnesses/openhands/index.js +48 -0
  341. package/dist/integrations/harnesses/openhands/result-extractor.js +103 -0
  342. package/dist/integrations/harnesses/pi/agent-builder.js +97 -0
  343. package/dist/integrations/harnesses/pi/index.js +45 -0
  344. package/dist/integrations/harnesses/pi/result-extractor.js +135 -0
  345. package/dist/integrations/harnesses/shared.js +17 -0
  346. package/dist/integrations/harnesses/types.js +43 -32
  347. package/dist/integrations/lockfile.js +211 -24
  348. package/dist/integrations/session-logs/index.js +36 -39
  349. package/dist/integrations/session-logs/provider-base.js +113 -0
  350. package/dist/llm/client.js +182 -110
  351. package/dist/llm/embedders/deterministic.js +2 -2
  352. package/dist/llm/embedders/remote.js +21 -9
  353. package/dist/llm/feature-gate.js +17 -57
  354. package/dist/llm/graph-extract.js +12 -13
  355. package/dist/llm/index-passes.js +8 -42
  356. package/dist/llm/memory-infer.js +144 -1
  357. package/dist/llm/metadata-enhance.js +45 -30
  358. package/dist/llm/structured-call.js +16 -8
  359. package/dist/llm/usage-persist.js +30 -5
  360. package/dist/llm/usage-telemetry.js +59 -6
  361. package/dist/output/cli-hints.js +1 -2
  362. package/dist/output/command-registry.js +27 -0
  363. package/dist/output/context.js +22 -7
  364. package/dist/output/format-exempt.js +80 -0
  365. package/dist/output/generic-render.js +251 -0
  366. package/dist/output/html-render.js +11 -16
  367. package/dist/output/render-registry.js +57 -0
  368. package/dist/output/renderers.js +14 -279
  369. package/dist/output/shapes/curate.js +10 -1
  370. package/dist/output/shapes/events.js +12 -7
  371. package/dist/output/shapes/helpers.js +58 -84
  372. package/dist/output/shapes/passthrough.js +11 -39
  373. package/dist/output/shapes/proposal/producer.js +15 -7
  374. package/dist/output/shapes/registry.js +12 -6
  375. package/dist/output/shapes.js +0 -9
  376. package/dist/output/text/{init.js → bundle-create.js} +3 -1
  377. package/dist/output/text/bundle-show.js +7 -0
  378. package/dist/output/text/command-format.js +562 -0
  379. package/dist/output/text/env.js +1 -3
  380. package/dist/output/text/events.js +8 -7
  381. package/dist/output/text/helpers.js +15 -1164
  382. package/dist/output/text/proposal/producer.js +4 -2
  383. package/dist/output/text/proposal-format.js +202 -0
  384. package/dist/output/text/registry-commands.js +1 -2
  385. package/dist/output/text/registry.js +12 -6
  386. package/dist/output/text/show-directives.js +117 -0
  387. package/dist/output/text/show-format.js +103 -0
  388. package/dist/output/text/sync.js +5 -0
  389. package/dist/output/text/workflow-format.js +332 -0
  390. package/dist/output/text/workflow.js +3 -2
  391. package/dist/output/text.js +10 -19
  392. package/dist/registry/factory.js +4 -6
  393. package/dist/registry/origin-resolve.js +16 -27
  394. package/dist/registry/providers/skills-sh.js +3 -3
  395. package/dist/registry/providers/static-index.js +15 -25
  396. package/dist/registry/resolve.js +43 -94
  397. package/dist/registry/semver.js +43 -0
  398. package/dist/runtime.js +81 -12
  399. package/dist/scripts/akm-migrate.js +35529 -0
  400. package/dist/setup/detect.js +5 -7
  401. package/dist/setup/detected-engines.js +136 -0
  402. package/dist/setup/engine-config.js +100 -0
  403. package/dist/setup/registry-stash-loader.js +3 -3
  404. package/dist/setup/semantic-assets.js +12 -9
  405. package/dist/setup/setup.js +444 -208
  406. package/dist/setup/steps/connection-shared.js +120 -0
  407. package/dist/setup/steps/connection.js +108 -305
  408. package/dist/setup/steps/platforms.js +13 -12
  409. package/dist/setup/steps/semantic.js +15 -3
  410. package/dist/setup/steps/sources.js +21 -15
  411. package/dist/setup/steps/stashdir.js +6 -4
  412. package/dist/setup/steps/tasks.js +236 -119
  413. package/dist/setup/steps.js +3 -2
  414. package/dist/sources/freshness.js +39 -0
  415. package/dist/sources/provider-factory.js +11 -17
  416. package/dist/sources/providers/filesystem.js +2 -3
  417. package/dist/sources/providers/git-install.js +278 -34
  418. package/dist/sources/providers/git-provider.js +54 -56
  419. package/dist/sources/providers/git-stash.js +420 -91
  420. package/dist/sources/providers/git.js +2 -2
  421. package/dist/sources/providers/npm.js +16 -19
  422. package/dist/sources/providers/provider-utils.js +47 -22
  423. package/dist/sources/providers/sync-from-ref.js +3 -9
  424. package/dist/sources/providers/website.js +2 -2
  425. package/dist/sources/resolve.js +11 -10
  426. package/dist/sources/snapshot-fetchers/types.js +4 -0
  427. package/dist/sources/{website-ingest.js → snapshot-fetchers/website-ingest.js} +110 -41
  428. package/dist/storage/database.js +60 -4
  429. package/dist/storage/engines/sqlite-migrations.js +156 -5
  430. package/dist/storage/locations.js +1 -2
  431. package/dist/storage/repositories/canaries-repository.js +1 -1
  432. package/dist/storage/repositories/events-repository.js +51 -11
  433. package/dist/storage/repositories/improve-runs-repository.js +6 -32
  434. package/dist/storage/repositories/index-connection.js +79 -0
  435. package/dist/storage/repositories/index-db.js +4 -3
  436. package/dist/storage/repositories/index-entries-repository.js +863 -0
  437. package/dist/{indexer/db/entry-mapper.js → storage/repositories/index-entry-mapper.js} +19 -2
  438. package/dist/storage/repositories/index-entry-types.js +4 -0
  439. package/dist/storage/repositories/index-fts-repository.js +167 -0
  440. package/dist/storage/repositories/index-llm-cache-repository.js +108 -0
  441. package/dist/storage/repositories/index-meta-repository.js +49 -0
  442. package/dist/{indexer/db/schema.js → storage/repositories/index-schema.js} +226 -100
  443. package/dist/storage/repositories/index-sql.js +12 -0
  444. package/dist/storage/repositories/index-utility-repository.js +356 -0
  445. package/dist/storage/repositories/index-vec-repository.js +250 -0
  446. package/dist/storage/repositories/outcome-repository.js +119 -0
  447. package/dist/storage/repositories/proposals-repository.js +317 -75
  448. package/dist/storage/repositories/registry-cache.js +1 -1
  449. package/dist/storage/repositories/salience-repository.js +172 -0
  450. package/dist/storage/repositories/task-history-repository.js +110 -3
  451. package/dist/storage/repositories/workflow-runs-repository.js +240 -19
  452. package/dist/tasks/backends/cron.js +169 -46
  453. package/dist/tasks/backends/exec-utils.js +76 -3
  454. package/dist/tasks/backends/index.js +6 -9
  455. package/dist/tasks/backends/launchd.js +292 -55
  456. package/dist/tasks/backends/schtasks.js +557 -70
  457. package/dist/tasks/backends/types.js +4 -0
  458. package/dist/tasks/command-executable.js +93 -0
  459. package/dist/tasks/embedded.js +56 -38
  460. package/dist/tasks/parser.js +156 -64
  461. package/dist/tasks/resolve-akm-bin.js +144 -51
  462. package/dist/tasks/runner.js +377 -209
  463. package/dist/tasks/schedule.js +108 -19
  464. package/dist/tasks/scheduler-invocation.js +296 -0
  465. package/dist/tasks/schema.js +1 -1
  466. package/dist/tasks/task-id.js +35 -0
  467. package/dist/tasks/validator.js +30 -16
  468. package/dist/text-import-hook.mjs +1 -1
  469. package/dist/workflows/authoring/authoring.js +104 -43
  470. package/dist/workflows/authoring/scope-key.js +1 -1
  471. package/dist/workflows/cli.js +0 -16
  472. package/dist/workflows/concurrency-policy.js +15 -0
  473. package/dist/workflows/exec/brief.js +450 -0
  474. package/dist/workflows/exec/frozen-judge.js +47 -0
  475. package/dist/workflows/exec/native-executor.js +1038 -0
  476. package/dist/workflows/exec/param-secrets.js +115 -0
  477. package/dist/workflows/exec/report.js +1460 -0
  478. package/dist/workflows/exec/run-workflow.js +602 -0
  479. package/dist/workflows/exec/scheduler.js +71 -0
  480. package/dist/workflows/exec/step-work.js +1190 -0
  481. package/dist/workflows/exec/unit-writer.js +23 -0
  482. package/dist/workflows/exec/workflow-engine-gate.js +67 -0
  483. package/dist/workflows/exec/worktree.js +171 -0
  484. package/dist/workflows/ir/compile.js +246 -0
  485. package/dist/workflows/ir/freeze.js +233 -0
  486. package/dist/workflows/ir/params.js +54 -0
  487. package/dist/workflows/ir/plan-hash.js +68 -0
  488. package/dist/workflows/ir/schema.js +540 -0
  489. package/dist/workflows/parser.js +878 -304
  490. package/dist/workflows/program/expressions.js +181 -0
  491. package/dist/workflows/program/schema.js +51 -0
  492. package/dist/workflows/renderer.js +100 -45
  493. package/dist/workflows/resource-limits.js +22 -0
  494. package/dist/workflows/runtime/agent-identity.js +59 -14
  495. package/dist/workflows/runtime/checkin.js +1 -1
  496. package/dist/workflows/runtime/plan-classifier.js +131 -0
  497. package/dist/workflows/runtime/runs.js +376 -119
  498. package/dist/workflows/runtime/unit-checkin.js +45 -0
  499. package/dist/workflows/runtime/unit-phases.js +20 -0
  500. package/dist/workflows/runtime/workflow-asset-loader.js +241 -40
  501. package/dist/workflows/schema.js +1 -11
  502. package/dist/workflows/validate-summary.js +2 -3
  503. package/dist/workflows/validator.js +52 -30
  504. package/docs/README.md +42 -78
  505. package/docs/migration/README.md +8 -0
  506. package/docs/migration/release-notes/0.6.0.md +1 -1
  507. package/docs/migration/release-notes/0.7.0.md +9 -8
  508. package/docs/migration/release-notes/0.9.0.md +158 -14
  509. package/docs/migration/v0.7-to-v0.8.md +46 -47
  510. package/docs/migration/v0.8-to-v0.9.md +844 -0
  511. package/docs/reference/README.md +12 -0
  512. package/docs/reference/data-and-telemetry.md +333 -0
  513. package/package.json +21 -17
  514. package/schemas/akm-asset-envelope.json +93 -0
  515. package/schemas/akm-config.json +4636 -0
  516. package/schemas/akm-task.json +87 -0
  517. package/schemas/akm-workflow.json +373 -0
  518. package/dist/akm-migrate-storage +0 -38
  519. package/dist/assets/help/help-accept.md +0 -12
  520. package/dist/assets/help/help-improve.md +0 -84
  521. package/dist/assets/help/help-proposals.md +0 -17
  522. package/dist/assets/help/help-propose.md +0 -17
  523. package/dist/assets/help/help-reject.md +0 -11
  524. package/dist/assets/profiles/frequent.json +0 -13
  525. package/dist/assets/profiles/recombine-only.json +0 -21
  526. package/dist/assets/profiles/reflect-distill.json +0 -30
  527. package/dist/assets/profiles/synthesize.json +0 -15
  528. package/dist/assets/prompts/procedural-system.md +0 -44
  529. package/dist/assets/prompts/recombine-system.md +0 -40
  530. package/dist/assets/prompts/staleness-detect-system.md +0 -6
  531. package/dist/assets/tasks/core/backup.yml +0 -4
  532. package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
  533. package/dist/assets/templates/html/default.html +0 -78
  534. package/dist/assets/templates/html/vendor/echarts.min.js +0 -45
  535. package/dist/assets/wiki/index-template.md +0 -12
  536. package/dist/assets/wiki/ingest-workflow-template.md +0 -83
  537. package/dist/assets/wiki/log-template.md +0 -8
  538. package/dist/assets/wiki/schema-template.md +0 -61
  539. package/dist/cli/config-migrate.js +0 -150
  540. package/dist/cli/config-validate.js +0 -39
  541. package/dist/commands/graph/graph-cli.js +0 -124
  542. package/dist/commands/graph/graph.js +0 -487
  543. package/dist/commands/improve/calibration.js +0 -161
  544. package/dist/commands/improve/dedup.js +0 -482
  545. package/dist/commands/improve/extract-watch.js +0 -140
  546. package/dist/commands/improve/hot-probation.js +0 -45
  547. package/dist/commands/improve/improve-auto-accept.js +0 -276
  548. package/dist/commands/improve/improve-profiles.js +0 -168
  549. package/dist/commands/improve/procedural.js +0 -398
  550. package/dist/commands/improve/recombine.js +0 -818
  551. package/dist/commands/improve/schema-similarity-gate.js +0 -168
  552. package/dist/commands/lint/agent-linter.js +0 -44
  553. package/dist/commands/lint/command-linter.js +0 -44
  554. package/dist/commands/lint/default-linter.js +0 -16
  555. package/dist/commands/lint/fact-linter.js +0 -39
  556. package/dist/commands/lint/knowledge-linter.js +0 -16
  557. package/dist/commands/lint/memory-linter.js +0 -61
  558. package/dist/commands/lint/registry.js +0 -41
  559. package/dist/commands/lint/skill-linter.js +0 -45
  560. package/dist/commands/lint/task-linter.js +0 -50
  561. package/dist/commands/lint/workflow-linter.js +0 -81
  562. package/dist/commands/proposal/legacy-import.js +0 -115
  563. package/dist/commands/sources/history.js +0 -196
  564. package/dist/commands/tasks/default-tasks.js +0 -186
  565. package/dist/commands/wiki-cli.js +0 -292
  566. package/dist/core/asset/asset-registry.js +0 -76
  567. package/dist/core/asset/asset-spec.js +0 -259
  568. package/dist/core/config/config-migration.js +0 -602
  569. package/dist/core/deep-merge.js +0 -38
  570. package/dist/core/eval/rank-metrics.js +0 -113
  571. package/dist/core/ripgrep/install.js +0 -163
  572. package/dist/core/ripgrep/resolve.js +0 -81
  573. package/dist/indexer/db/db.js +0 -1413
  574. package/dist/indexer/manifest.js +0 -170
  575. package/dist/indexer/passes/metadata-contributors.js +0 -31
  576. package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -94
  577. package/dist/integrations/harnesses/opencode-sdk/index.js +0 -49
  578. package/dist/llm/call-ai.js +0 -62
  579. package/dist/llm/memory-infer-impl.js +0 -138
  580. package/dist/output/shapes/distill.js +0 -14
  581. package/dist/output/shapes/history.js +0 -11
  582. package/dist/output/text/distill.js +0 -6
  583. package/dist/output/text/enable-disable.js +0 -8
  584. package/dist/output/text/history.js +0 -6
  585. package/dist/output/text/wiki.js +0 -16
  586. package/dist/registry/build-index.js +0 -386
  587. package/dist/scripts/migrate-storage.js +0 -19108
  588. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +0 -9411
  589. package/dist/scripts/migrations/v16-to-v17.js +0 -141
  590. package/dist/setup/legacy-config.js +0 -106
  591. package/dist/storage/repositories/consolidation-repository.js +0 -38
  592. package/dist/storage/repositories/recombine-repository.js +0 -213
  593. package/dist/wiki/wiki-templates.js +0 -15
  594. package/dist/wiki/wiki.js +0 -1012
  595. package/dist/workflows/db.js +0 -215
  596. package/docs/data-and-telemetry.md +0 -226
  597. /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/registry.js +0 -0
  598. /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/youtube.js +0 -0
@@ -1,13 +1,16 @@
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 { randomBytes } from "node:crypto";
4
5
  import fs from "node:fs";
5
6
  import path from "node:path";
6
- import { TYPE_DIRS } from "../../core/asset/asset-spec.js";
7
+ import { akmAdapter } from "../../core/adapter/adapters/akm-adapter.js";
8
+ import { stashDirNames } from "../../core/asset/asset-placement.js";
7
9
  import { resolveStashDir } from "../../core/common.js";
8
- import { getSources, loadConfig } from "../../core/config/config.js";
10
+ import { getSources, loadConfig, resolveConfiguredSources } from "../../core/config/config.js";
9
11
  import { UsageError } from "../../core/errors.js";
10
- import { sanitizeCommitMessage } from "../../core/write-source.js";
12
+ import { sanitizeCommitMessage } from "../../core/git-message.js";
13
+ import { lockContentRootFor } from "../../integrations/lockfile.js";
11
14
  import { runGit } from "./git-install.js";
12
15
  import { getCachePaths, parseGitRepoUrl } from "./git-provider.js";
13
16
  /**
@@ -22,15 +25,46 @@ import { getCachePaths, parseGitRepoUrl } from "./git-provider.js";
22
25
  export function isGitBackedStash(stashDir) {
23
26
  return fs.existsSync(path.join(stashDir, ".git"));
24
27
  }
28
+ /** Return repo-relative dirty/staged paths without changing the index. */
29
+ export function listGitChangedPaths(repoDir) {
30
+ const result = runGit(["-C", repoDir, "status", "--porcelain", "-z", "--untracked-files=all"]);
31
+ if (result.status !== 0)
32
+ return [];
33
+ const records = result.stdout.split("\0");
34
+ const paths = [];
35
+ for (let i = 0; i < records.length; i++) {
36
+ const record = records[i];
37
+ if (!record)
38
+ continue;
39
+ const status = record.slice(0, 2);
40
+ paths.push(record.slice(3));
41
+ if (status.includes("R") || status.includes("C")) {
42
+ const previousPath = records[++i];
43
+ if (previousPath)
44
+ paths.push(previousPath);
45
+ }
46
+ }
47
+ return paths;
48
+ }
49
+ export class GitStashPushError extends Error {
50
+ commit;
51
+ constructor(message, commit) {
52
+ super(message);
53
+ this.name = "GitStashPushError";
54
+ this.commit = commit;
55
+ }
56
+ }
57
+ const GIT_PUSH_TIMEOUT_MS = 120_000;
58
+ const ZERO_OID = "0000000000000000000000000000000000000000";
25
59
  /**
26
- * Resolve the writable-override flag for an end-of-run / `akm sync` commit on
27
- * the primary stash. Returns `true` when the root config explicitly marks the
28
- * primary stash writable, otherwise `undefined` (leave the per-stash default
29
- * untouched). Extracted so `akm sync`, `akm improve`'s end-of-run sync, and the
30
- * CLI body all derive this identically instead of re-copying the expression.
60
+ * Resolve the writable flag for an end-of-run / `akm sync` commit from the
61
+ * configured default bundle.
31
62
  */
32
63
  export function resolveWritableOverride(config) {
33
- return config.writable === true ? true : undefined;
64
+ const source = resolveConfiguredSources(config).find((entry) => entry.name === config.defaultBundle);
65
+ if (!source)
66
+ return undefined;
67
+ return (source.writable ?? source.type === "filesystem") ? true : undefined;
34
68
  }
35
69
  /**
36
70
  * Commit (and optionally push) local changes in a git-backed stash.
@@ -49,7 +83,7 @@ export function resolveWritableOverride(config) {
49
83
  * primary stash dir (e.g. `akm improve`'s end-of-run sync, whose pre-commit
50
84
  * gate validates that exact directory) pass it here so the gate and the commit
51
85
  * operate on the SAME directory instead of independently calling
52
- * `resolveStashDir({ readOnly: true })`. When absent, behaviour is unchanged.
86
+ * `resolveStashDir()`. When absent, behaviour is unchanged.
53
87
  */
54
88
  export function saveGitStash(name, message, writableOverride, options) {
55
89
  // `push: false` (from `akm sync --no-push`) commits but never pushes, even
@@ -62,8 +96,13 @@ export function saveGitStash(name, message, writableOverride, options) {
62
96
  // Empty result falls back to the timestamped default.
63
97
  const sanitized = message ? sanitizeCommitMessage(message) : "";
64
98
  const commitMessage = sanitized || `akm save ${timestamp}`;
99
+ const transactionId = options?.transactionId;
100
+ if (transactionId !== undefined && !/^[A-Za-z0-9._-]{1,128}$/.test(transactionId)) {
101
+ throw new Error("Invalid Git transaction identifier.");
102
+ }
65
103
  let repoDir;
66
104
  let writable = false;
105
+ let managedContentRoot;
67
106
  if (name) {
68
107
  const config = loadConfig();
69
108
  const stash = findGitStashByTarget(getSources(config), name);
@@ -72,17 +111,28 @@ export function saveGitStash(name, message, writableOverride, options) {
72
111
  if (stash.type !== "git") {
73
112
  throw new UsageError(`Stash "${name}" is not a git stash (type: ${stash.type})`);
74
113
  }
75
- if (!stash.url)
76
- throw new UsageError(`Stash "${name}" has no URL configured`);
77
- const repo = parseGitRepoUrl(stash.url);
78
- repoDir = getCachePaths(repo.canonicalUrl).repoDir;
114
+ const lockedRoot = lockContentRootFor(stash.name, stash.type);
115
+ if (lockedRoot) {
116
+ const topLevel = runGit(["-C", lockedRoot, "rev-parse", "--show-toplevel"]);
117
+ if (topLevel.status !== 0 || !topLevel.stdout.trim()) {
118
+ throw new UsageError(`Managed Git stash "${name}" is not a checkout at ${lockedRoot}`);
119
+ }
120
+ repoDir = path.resolve(topLevel.stdout.trim());
121
+ managedContentRoot = path.resolve(lockedRoot);
122
+ }
123
+ else {
124
+ if (!stash.url)
125
+ throw new UsageError(`Stash "${name}" has no URL configured`);
126
+ const repo = parseGitRepoUrl(stash.url);
127
+ repoDir = getCachePaths(repo.canonicalUrl).repoDir;
128
+ }
79
129
  writable = stash.writable === true;
80
130
  }
81
131
  else {
82
132
  // Honour an explicit primary-stash dir override (keeps the improve gate and
83
133
  // the commit on the same directory); otherwise resolve the default.
84
- repoDir = options?.repoDir ?? resolveStashDir({ readOnly: true });
85
- // Allow caller to override writable for the primary stash (e.g. from root config.writable)
134
+ repoDir = options?.repoDir ?? resolveStashDir();
135
+ // Allow the caller to pass the configured default bundle's writability.
86
136
  if (writableOverride !== undefined) {
87
137
  writable = writableOverride;
88
138
  }
@@ -91,12 +141,11 @@ export function saveGitStash(name, message, writableOverride, options) {
91
141
  if (!isGitBackedStash(repoDir)) {
92
142
  return { committed: false, pushed: false, skipped: true, reason: "not a git repository", output: "" };
93
143
  }
94
- // Nothing to commit?
95
144
  const statusResult = runGit(["-C", repoDir, "status", "--porcelain"]);
96
145
  if (statusResult.error || statusResult.status !== 0) {
97
146
  throw new Error(`git status failed: ${statusResult.error?.message || statusResult.stderr?.trim() || "unknown error"}`);
98
147
  }
99
- if (!statusResult.stdout.trim()) {
148
+ if (!statusResult.stdout.trim() && options?.paths === undefined) {
100
149
  return { committed: false, pushed: false, skipped: false, output: "nothing to commit, working tree clean" };
101
150
  }
102
151
  // Scoped staging (#476 + the auto-sync incident): NEVER refuse akm's commit
@@ -107,103 +156,383 @@ export function saveGitStash(name, message, writableOverride, options) {
107
156
  //
108
157
  // Precedence:
109
158
  // 1. Explicit modified-file list (`options.paths`) — stage exactly those.
110
- // 2. Managed pathspecs (TYPE_DIRS values + `.akm`) that exist on disk —
111
- // stages everything akm owns and, by construction, never stages non-akm
112
- // WIP. This preserves the #476 protection WITHOUT refusing.
113
- // 3. Last-resort `git add -A` — ONLY when neither an explicit list nor any
114
- // managed pathspec can be resolved. This is the maintainer-approved
115
- // "or all files if we cannot determine the exact file list" fallback and
116
- // is the one (rare) path that could include unrelated non-akm files.
117
- if (!stageScopedChanges(repoDir, options?.paths)) {
118
- throw new Error(`git add failed while staging akm changes in ${repoDir}`);
119
- }
120
- // Nothing actually staged → don't create an empty commit. This happens when
121
- // only non-akm files were dirty (precedence 2 staged nothing).
122
- const stagedResult = runGit(["-C", repoDir, "diff", "--cached", "--quiet"]);
123
- if (stagedResult.status === 0) {
159
+ // 2. A lock-backed content root, or managed adapter pathspecs for an
160
+ // ordinary stash. Both stay below AKM's resolved content boundary.
161
+ // 3. No resolvable managed path — no commit. Broad staging is never safe
162
+ // because it can absorb unrelated work already present in the index.
163
+ const managedRelativeRoot = managedContentRoot
164
+ ? path.relative(repoDir, managedContentRoot).replaceAll(path.sep, "/")
165
+ : "";
166
+ const changedPaths = listGitChangedPaths(repoDir);
167
+ const requestedPaths = normalizeExactPaths(repoDir, options?.paths ??
168
+ (managedRelativeRoot && managedRelativeRoot !== ".." && !managedRelativeRoot.startsWith("../")
169
+ ? changedPaths.filter((changedPath) => changedPath === managedRelativeRoot || changedPath.startsWith(`${managedRelativeRoot}/`))
170
+ : managedFallbackPaths(repoDir, changedPaths)));
171
+ if (requestedPaths.length === 0) {
124
172
  return { committed: false, pushed: false, skipped: false, output: "nothing to commit" };
125
173
  }
126
- // Commit — supply fallback identity so fresh environments without
127
- // user.name/user.email configured can always commit to the default stash.
128
- const commitResult = runGit([
129
- "-C",
130
- repoDir,
131
- "-c",
132
- "user.name=akm",
133
- "-c",
134
- "user.email=akm@local",
135
- "commit",
136
- "-m",
137
- commitMessage,
138
- ]);
139
- if (commitResult.status !== 0) {
140
- throw new Error(`git commit failed: ${commitResult.stderr?.trim() || "unknown error"}`);
174
+ assertNoIgnoredExactPaths(repoDir, requestedPaths);
175
+ const baseHead = readOptionalHead(repoDir);
176
+ if (options?.expectedBaseHead !== undefined && (baseHead ?? null) !== options.expectedBaseHead) {
177
+ throw new Error(`Git target advanced before its exact-path commit could be created.`);
141
178
  }
142
- // Push only when there is a remote AND the stash is marked writable
143
179
  const remoteResult = runGit(["-C", repoDir, "remote"]);
144
180
  if (remoteResult.status !== 0) {
145
181
  throw new Error(`git remote failed: ${remoteResult.stderr?.trim() || "unknown error"}`);
146
182
  }
147
183
  const hasRemote = remoteResult.stdout.trim().length > 0;
148
- if (!hasRemote || !writable || !allowPush) {
149
- return { committed: true, pushed: false, skipped: false, output: commitResult.stdout.trim() };
184
+ const pushTarget = hasRemote && writable && allowPush ? readActualUpstream(repoDir, baseHead) : undefined;
185
+ const exactCommit = createExactPathCommit(repoDir, {
186
+ baseHead,
187
+ commitMessage,
188
+ paths: requestedPaths,
189
+ transactionId,
190
+ expectedSnapshots: options?.expectedSnapshots,
191
+ });
192
+ if (!exactCommit) {
193
+ return { committed: false, pushed: false, skipped: false, output: "nothing to commit" };
150
194
  }
151
- const pushResult = runGit(["-C", repoDir, "push"], { timeout: 120_000 });
195
+ if (!pushTarget) {
196
+ return {
197
+ committed: true,
198
+ pushed: false,
199
+ skipped: false,
200
+ output: `commit ${exactCommit}`,
201
+ commit: exactCommit,
202
+ };
203
+ }
204
+ const pushResult = runGit([
205
+ "-C",
206
+ repoDir,
207
+ "push",
208
+ `--force-with-lease=${pushTarget.mergeRef}:${pushTarget.upstreamHead}`,
209
+ pushTarget.remote,
210
+ `${exactCommit}:${pushTarget.mergeRef}`,
211
+ ], { timeout: GIT_PUSH_TIMEOUT_MS });
152
212
  if (pushResult.status !== 0) {
153
- throw new Error(`git push failed: ${pushResult.stderr?.trim() || "unknown error"}`);
213
+ throw new GitStashPushError(`git push failed: ${pushResult.stderr?.trim() || "unknown error"}`, exactCommit);
154
214
  }
155
215
  return {
156
216
  committed: true,
157
217
  pushed: true,
158
218
  skipped: false,
159
- output: (commitResult.stdout + pushResult.stdout).trim() || "changes committed and pushed",
219
+ output: pushResult.stdout.trim() || "changes committed and pushed",
220
+ commit: exactCommit,
160
221
  };
161
222
  }
162
- /**
163
- * Stage akm's changes in `repoDir` using the scoped-staging precedence
164
- * documented at the call site (#476). Returns `false` only when a `git add`
165
- * subprocess fails; returns `true` otherwise (including when nothing matched —
166
- * the caller then detects "nothing staged" via `git diff --cached --quiet`).
167
- *
168
- * @param paths Optional explicit repo-relative paths akm wrote this run. When
169
- * provided and non-empty, exactly those are staged (chunked to stay under
170
- * argv length limits). Otherwise we fall back to the managed pathspecs, and
171
- * finally to `git add -A` only if no managed pathspec exists on disk.
172
- */
173
- function stageScopedChanges(repoDir, paths) {
174
- // Precedence 1: explicit modified-file list.
175
- const explicit = (paths ?? []).filter((p) => typeof p === "string" && p.length > 0);
176
- if (explicit.length > 0) {
177
- return addPathspecsChunked(repoDir, explicit);
178
- }
179
- // Precedence 2: managed pathspecs that exist on disk (TYPE_DIRS + `.akm`).
180
- const managed = [...Object.values(TYPE_DIRS), ".akm"].filter((dir) => fs.existsSync(path.join(repoDir, dir)));
181
- if (managed.length > 0) {
182
- return addPathspecsChunked(repoDir, managed);
183
- }
184
- // Precedence 3 (last resort): nothing akm-managed resolved on disk. Stage
185
- // everything. This is the ONLY branch that can include unrelated non-akm
186
- // files — it is the explicit, maintainer-approved "or all files if we cannot
187
- // determine the exact file list" fallback and should be rare/never in
188
- // practice (a git-backed stash always has at least one managed subtree once
189
- // akm has written to it).
190
- const addAll = runGit(["-C", repoDir, "add", "-A"]);
191
- return addAll.status === 0;
223
+ function managedFallbackPaths(repoDir, changedPaths) {
224
+ const ownedDirs = akmAdapter.directoryList?.({ id: "akm", adapter: "akm", root: repoDir, writable: false }) ?? stashDirNames();
225
+ const prefixes = [...ownedDirs, ".akm"];
226
+ return changedPaths.filter((changedPath) => prefixes.some((prefix) => changedPath === prefix || changedPath.startsWith(`${prefix}/`)));
192
227
  }
193
- /**
194
- * Run `git add -- <pathspec>...` in chunks so a very large path list never
195
- * exceeds the OS argv-length limit. Each chunk must succeed.
196
- */
197
- function addPathspecsChunked(repoDir, pathspecs) {
228
+ function normalizeExactPaths(repoDir, paths) {
229
+ const normalized = [...new Set(paths.map((value) => value.replaceAll(path.sep, "/")))];
230
+ for (const filePath of normalized) {
231
+ const absolute = path.resolve(repoDir, filePath);
232
+ const relative = path.relative(repoDir, absolute).replaceAll(path.sep, "/");
233
+ if (!filePath ||
234
+ filePath.includes("\0") ||
235
+ path.isAbsolute(filePath) ||
236
+ relative !== filePath ||
237
+ relative === ".." ||
238
+ relative.startsWith("../")) {
239
+ throw new Error(`Unsafe exact Git path: ${filePath || "<empty>"}`);
240
+ }
241
+ }
242
+ return normalized;
243
+ }
244
+ function runExactPathChunks(repoDir, command, paths, options = {}) {
198
245
  const CHUNK = 500;
199
- for (let i = 0; i < pathspecs.length; i += CHUNK) {
200
- const chunk = pathspecs.slice(i, i + CHUNK);
201
- const result = runGit(["-C", repoDir, "add", "--", ...chunk]);
246
+ const results = [];
247
+ for (let i = 0; i < paths.length; i += CHUNK) {
248
+ results.push(runGit(["--literal-pathspecs", "-C", repoDir, ...command, "--", ...paths.slice(i, i + CHUNK)], {
249
+ env: options.env,
250
+ }));
251
+ }
252
+ return results;
253
+ }
254
+ export function listIgnoredExactPaths(repoDir, paths) {
255
+ if (paths.length === 0)
256
+ return [];
257
+ const input = `${paths.join("\0")}\0`;
258
+ const result = runGit(["-C", repoDir, "check-ignore", "-z", "--stdin"], { input });
259
+ if (result.status === 1)
260
+ return [];
261
+ if (result.status !== 0) {
262
+ throw new Error(`git check-ignore failed: ${result.stderr.trim() || "unknown error"}`);
263
+ }
264
+ return result.stdout.split("\0").filter(Boolean);
265
+ }
266
+ export function assertNoIgnoredExactPaths(repoDir, paths) {
267
+ const ignored = listIgnoredExactPaths(repoDir, normalizeExactPaths(repoDir, paths));
268
+ if (ignored.length > 0) {
269
+ throw new UsageError(`Exact Git publication path is ignored: ${ignored[0]}. Update .gitignore or choose a tracked destination before writing.`);
270
+ }
271
+ }
272
+ /** Reject exact staged/unstaged paths before an AKM filesystem mutation. */
273
+ export function assertGitExactPathsClean(repoDir, paths) {
274
+ const normalized = normalizeExactPaths(repoDir, paths);
275
+ assertNoIgnoredExactPaths(repoDir, normalized);
276
+ for (const result of runExactPathChunks(repoDir, ["status", "--porcelain=v1", "-z", "--untracked-files=all"], normalized)) {
277
+ if (result.status !== 0) {
278
+ throw new Error(`git status failed: ${result.stderr.trim() || "unknown error"}`);
279
+ }
280
+ if (result.stdout.length > 0) {
281
+ const record = result.stdout.split("\0").find(Boolean) ?? "";
282
+ throw new UsageError(`Exact Git operation path has staged or unstaged work: ${record.slice(3)}. Commit, stash, or discard that path before retrying.`);
283
+ }
284
+ }
285
+ }
286
+ function readOptionalHead(repoDir) {
287
+ const result = runGit(["-C", repoDir, "rev-parse", "--verify", "HEAD"]);
288
+ return result.status === 0 && result.stdout.trim() ? result.stdout.trim() : undefined;
289
+ }
290
+ function readBranchRef(repoDir) {
291
+ const result = runGit(["-C", repoDir, "symbolic-ref", "--quiet", "HEAD"]);
292
+ if (result.status !== 0 || !result.stdout.trim()) {
293
+ throw new UsageError(`Writable Git target at ${repoDir} is detached from a branch.`);
294
+ }
295
+ return result.stdout.trim();
296
+ }
297
+ function readActualUpstream(repoDir, baseHead) {
298
+ if (!baseHead)
299
+ throw new UsageError(`Writable Git target at ${repoDir} has no commit to publish.`);
300
+ const branchRef = readBranchRef(repoDir);
301
+ const branch = branchRef.replace(/^refs\/heads\//, "");
302
+ const remote = runGit(["-C", repoDir, "config", "--get", `branch.${branch}.remote`]);
303
+ const merge = runGit(["-C", repoDir, "config", "--get", `branch.${branch}.merge`]);
304
+ const upstream = runGit(["-C", repoDir, "rev-parse", "--verify", "@{u}"]);
305
+ if (remote.status !== 0 || merge.status !== 0 || upstream.status !== 0) {
306
+ throw new UsageError(`Writable Git target at ${repoDir} has no configured upstream branch.`);
307
+ }
308
+ const upstreamHead = upstream.stdout.trim();
309
+ if (!upstreamHead || upstreamHead !== baseHead) {
310
+ throw new UsageError(`Writable Git target at ${repoDir} is not synchronized with its actual upstream.`);
311
+ }
312
+ return { remote: remote.stdout.trim(), mergeRef: merge.stdout.trim(), upstreamHead };
313
+ }
314
+ function readIndexEntries(repoDir, paths) {
315
+ const entries = new Map(paths.map((filePath) => [filePath, []]));
316
+ for (const result of runExactPathChunks(repoDir, ["ls-files", "--stage", "-z"], paths)) {
202
317
  if (result.status !== 0)
318
+ throw new Error(`Cannot inspect Git index: ${result.stderr.trim() || "unknown error"}`);
319
+ for (const record of result.stdout.split("\0")) {
320
+ if (!record)
321
+ continue;
322
+ const tab = record.indexOf("\t");
323
+ const match = record.slice(0, tab).match(/^(\d+) ([0-9a-f]+) (\d+)$/);
324
+ if (tab < 0 || !match)
325
+ throw new Error(`Cannot parse Git index entry.`);
326
+ const filePath = record.slice(tab + 1);
327
+ entries.get(filePath)?.push({ mode: match[1], oid: match[2], stage: Number(match[3]) });
328
+ }
329
+ }
330
+ return entries;
331
+ }
332
+ function readTreeStates(repoDir, treeish, paths) {
333
+ const states = new Map(paths.map((filePath) => [filePath, null]));
334
+ if (!treeish)
335
+ return states;
336
+ for (const result of runExactPathChunks(repoDir, ["ls-tree", "-z", treeish], paths)) {
337
+ if (result.status !== 0)
338
+ throw new Error(`Cannot inspect Git tree: ${result.stderr.trim() || "unknown error"}`);
339
+ for (const record of result.stdout.split("\0")) {
340
+ if (!record)
341
+ continue;
342
+ const tab = record.indexOf("\t");
343
+ const match = record.slice(0, tab).match(/^(\d+) blob ([0-9a-f]+)$/);
344
+ if (tab < 0 || !match)
345
+ throw new Error(`Exact Git path is not a blob.`);
346
+ const mode = match[1];
347
+ if (mode !== "100644" && mode !== "100755" && mode !== "120000") {
348
+ throw new Error(`Unsupported Git mode ${mode}.`);
349
+ }
350
+ states.set(record.slice(tab + 1), { mode, oid: match[2] });
351
+ }
352
+ }
353
+ return states;
354
+ }
355
+ function assertIndexMatchesBase(paths, indexEntries, baseStates) {
356
+ if (indexMatchesTree(paths, indexEntries, baseStates))
357
+ return;
358
+ const dirtyPath = paths.find((filePath) => {
359
+ const entries = indexEntries.get(filePath) ?? [];
360
+ const base = baseStates.get(filePath) ?? null;
361
+ return !(base === null
362
+ ? entries.length === 0
363
+ : entries.length === 1 &&
364
+ entries[0]?.stage === 0 &&
365
+ entries[0]?.mode === base.mode &&
366
+ entries[0]?.oid === base.oid);
367
+ });
368
+ throw new UsageError(`Exact Git operation path has staged work: ${dirtyPath ?? paths[0]}. Commit, stash, or discard that path before retrying.`);
369
+ }
370
+ function indexMatchesTree(paths, indexEntries, treeStates) {
371
+ for (const filePath of paths) {
372
+ const entries = indexEntries.get(filePath) ?? [];
373
+ const state = treeStates.get(filePath) ?? null;
374
+ if (!(state === null
375
+ ? entries.length === 0
376
+ : entries.length === 1 &&
377
+ entries[0]?.stage === 0 &&
378
+ entries[0]?.mode === state.mode &&
379
+ entries[0]?.oid === state.oid)) {
203
380
  return false;
381
+ }
204
382
  }
205
383
  return true;
206
384
  }
385
+ /** Repair only the stale base index left by a crash after an exact commit CAS. */
386
+ export function reconcileGitExactPathIndex(repoDir, baseCommit, commit, paths) {
387
+ const normalized = normalizeExactPaths(repoDir, paths);
388
+ const current = readIndexEntries(repoDir, normalized);
389
+ const committedStates = readTreeStates(repoDir, commit, normalized);
390
+ if (indexMatchesTree(normalized, current, committedStates))
391
+ return;
392
+ const baseStates = readTreeStates(repoDir, baseCommit, normalized);
393
+ if (!indexMatchesTree(normalized, current, baseStates)) {
394
+ throw new UsageError(`Exact Git operation path has staged work after transaction recovery. Commit, stash, or discard it before retrying.`);
395
+ }
396
+ for (const result of runExactPathChunks(repoDir, ["reset", "--quiet", commit], normalized)) {
397
+ if (result.status !== 0)
398
+ throw new Error(`git index cleanup failed during exact transaction recovery`);
399
+ }
400
+ }
401
+ function captureWorktreeState(repoDir, filePath) {
402
+ const absolutePath = path.join(repoDir, filePath);
403
+ let stat;
404
+ try {
405
+ stat = fs.lstatSync(absolutePath);
406
+ }
407
+ catch (error) {
408
+ if (error.code === "ENOENT")
409
+ return null;
410
+ throw error;
411
+ }
412
+ let mode;
413
+ let result;
414
+ if (stat.isSymbolicLink()) {
415
+ mode = "120000";
416
+ result = runGit(["-C", repoDir, "hash-object", "--stdin"], { input: fs.readlinkSync(absolutePath) });
417
+ }
418
+ else if (stat.isFile()) {
419
+ mode = stat.mode & 0o111 ? "100755" : "100644";
420
+ result = runGit(["-C", repoDir, "hash-object", `--path=${filePath}`, "--", absolutePath]);
421
+ }
422
+ else {
423
+ throw new Error(`Exact Git publication path is not a file: ${filePath}`);
424
+ }
425
+ if (result.status !== 0 || !result.stdout.trim()) {
426
+ throw new Error(`Cannot hash exact Git publication path: ${filePath}`);
427
+ }
428
+ return { oid: result.stdout.trim(), mode };
429
+ }
430
+ function samePathState(left, right) {
431
+ return left === null ? right === null : right !== null && left.oid === right.oid && left.mode === right.mode;
432
+ }
433
+ function assertWorktreeMatchesExpected(repoDir, paths, expected) {
434
+ for (const filePath of paths) {
435
+ if (!samePathState(captureWorktreeState(repoDir, filePath), expected.get(filePath) ?? null)) {
436
+ throw new Error(`Exact Git publication path changed while committing: ${filePath}`);
437
+ }
438
+ }
439
+ }
440
+ function sameIndexEntries(left, right) {
441
+ return JSON.stringify([...left]) === JSON.stringify([...right]);
442
+ }
443
+ function resetRealIndexPaths(repoDir, commit, paths, initial) {
444
+ const current = readIndexEntries(repoDir, paths);
445
+ if (!sameIndexEntries(initial, current))
446
+ return;
447
+ for (const result of runExactPathChunks(repoDir, ["reset", "--quiet", commit], paths)) {
448
+ if (result.status !== 0)
449
+ throw new Error(`git index cleanup failed after exact commit`);
450
+ }
451
+ }
452
+ function createExactPathCommit(repoDir, options) {
453
+ const branchRef = readBranchRef(repoDir);
454
+ const baseStates = readTreeStates(repoDir, options.baseHead, options.paths);
455
+ const initialIndex = readIndexEntries(repoDir, options.paths);
456
+ assertIndexMatchesBase(options.paths, initialIndex, baseStates);
457
+ const expected = new Map();
458
+ for (const filePath of options.paths) {
459
+ expected.set(filePath, options.expectedSnapshots && Object.hasOwn(options.expectedSnapshots, filePath)
460
+ ? (options.expectedSnapshots[filePath] ?? null)
461
+ : captureWorktreeState(repoDir, filePath));
462
+ }
463
+ const gitIndex = runGit(["-C", repoDir, "rev-parse", "--git-path", "index"]);
464
+ if (gitIndex.status !== 0 || !gitIndex.stdout.trim())
465
+ throw new Error(`Cannot resolve Git index path.`);
466
+ const realIndexPath = path.resolve(repoDir, gitIndex.stdout.trim());
467
+ const temporaryIndex = `${realIndexPath}.akm-${process.pid}-${randomBytes(6).toString("hex")}`;
468
+ const env = { GIT_INDEX_FILE: temporaryIndex };
469
+ try {
470
+ const seeded = options.baseHead
471
+ ? runGit(["-C", repoDir, "read-tree", options.baseHead], { env })
472
+ : runGit(["-C", repoDir, "read-tree", "--empty"], { env });
473
+ if (seeded.status !== 0)
474
+ throw new Error(`Cannot seed temporary Git index: ${seeded.stderr.trim()}`);
475
+ const stagePaths = options.paths.filter((filePath) => (expected.get(filePath) ?? null) !== null || (baseStates.get(filePath) ?? null) !== null);
476
+ for (const result of runExactPathChunks(repoDir, ["add", "-A"], stagePaths, { env })) {
477
+ if (result.status !== 0)
478
+ throw new Error(`Cannot stage exact Git paths: ${result.stderr.trim()}`);
479
+ }
480
+ const tree = runGit(["-C", repoDir, "write-tree"], { env });
481
+ if (tree.status !== 0 || !tree.stdout.trim())
482
+ throw new Error(`Cannot write exact Git tree.`);
483
+ const treeOid = tree.stdout.trim();
484
+ const committedStates = readTreeStates(repoDir, treeOid, options.paths);
485
+ for (const filePath of options.paths) {
486
+ if (!samePathState(committedStates.get(filePath) ?? null, expected.get(filePath) ?? null)) {
487
+ throw new Error(`Exact Git publication path changed while committing: ${filePath}`);
488
+ }
489
+ }
490
+ assertWorktreeMatchesExpected(repoDir, options.paths, expected);
491
+ const baseTreeResult = options.baseHead
492
+ ? runGit(["-C", repoDir, "rev-parse", `${options.baseHead}^{tree}`])
493
+ : undefined;
494
+ if (baseTreeResult && (baseTreeResult.status !== 0 || !baseTreeResult.stdout.trim())) {
495
+ throw new Error(`Cannot inspect base Git tree.`);
496
+ }
497
+ const baseTree = baseTreeResult?.stdout.trim() ?? "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
498
+ if (treeOid === baseTree)
499
+ return null;
500
+ const body = options.transactionId
501
+ ? `${options.commitMessage}\n\nAKM-Transaction: ${options.transactionId}\n`
502
+ : `${options.commitMessage}\n`;
503
+ const commit = runGit([
504
+ "-C",
505
+ repoDir,
506
+ "-c",
507
+ "user.name=akm",
508
+ "-c",
509
+ "user.email=akm@local",
510
+ "commit-tree",
511
+ treeOid,
512
+ ...(options.baseHead ? ["-p", options.baseHead] : []),
513
+ ], { input: body });
514
+ if (commit.status !== 0 || !commit.stdout.trim()) {
515
+ throw new Error(`git commit-tree failed: ${commit.stderr.trim() || "unknown error"}`);
516
+ }
517
+ const commitOid = commit.stdout.trim();
518
+ const currentBranch = readBranchRef(repoDir);
519
+ const currentHead = readOptionalHead(repoDir);
520
+ if (currentBranch !== branchRef || currentHead !== options.baseHead) {
521
+ throw new Error(`Git target changed before its exact commit could be attached.`);
522
+ }
523
+ assertWorktreeMatchesExpected(repoDir, options.paths, expected);
524
+ const update = runGit(["-C", repoDir, "update-ref", branchRef, commitOid, options.baseHead ?? ZERO_OID]);
525
+ if (update.status !== 0) {
526
+ throw new Error(`Git target advanced before its exact commit could be attached.`);
527
+ }
528
+ resetRealIndexPaths(repoDir, commitOid, options.paths, initialIndex);
529
+ return commitOid;
530
+ }
531
+ finally {
532
+ fs.rmSync(temporaryIndex, { force: true });
533
+ fs.rmSync(`${temporaryIndex}.lock`, { force: true });
534
+ }
535
+ }
207
536
  function findGitStashByTarget(stashes, target) {
208
537
  return stashes.find((stash) => matchesGitStashTarget(stash, target));
209
538
  }
@@ -8,6 +8,6 @@
8
8
  //
9
9
  // Re-exported here so every consumer (and every `spyOn(gitProvider, …)` test)
10
10
  // keeps importing from a single module namespace.
11
- export { classifyCloneFailure, cloneRepo, syncRegistryGitRef } from "./git-install.js";
11
+ export { classifyCloneFailure, cloneRepo, inspectGitUpstream, runGit, syncExistingWritableCheckout, } from "./git-install.js";
12
12
  export { ensureGitMirror, GitSourceProvider, getCachePaths, parseGitRepoUrl, syncMirroredRepo, } from "./git-provider.js";
13
- export { isGitBackedStash, resolveWritableOverride, saveGitStash, } from "./git-stash.js";
13
+ export { GitStashPushError, isGitBackedStash, listGitChangedPaths, resolveWritableOverride, saveGitStash, } from "./git-stash.js";