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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (598) hide show
  1. package/CHANGELOG.md +1283 -22
  2. package/README.md +62 -37
  3. package/SECURITY.md +46 -31
  4. package/dist/akm +162 -38
  5. package/dist/akm-migrate +44 -0
  6. package/dist/assets/backends/schtasks-template.xml +2 -1
  7. package/dist/assets/hints/cli-hints-full.md +268 -118
  8. package/dist/assets/hints/cli-hints-short.md +87 -24
  9. package/dist/assets/{profiles → improve-strategies}/catchup.json +3 -1
  10. package/dist/assets/{profiles → improve-strategies}/consolidate.json +3 -1
  11. package/dist/assets/{profiles → improve-strategies}/default.json +6 -7
  12. package/dist/assets/improve-strategies/frequent.json +15 -0
  13. package/dist/assets/{profiles → improve-strategies}/graph-refresh.json +4 -2
  14. package/dist/assets/{profiles → improve-strategies}/memory-focus.json +4 -1
  15. package/dist/assets/{profiles → improve-strategies}/proactive-maintenance.json +5 -5
  16. package/dist/assets/{profiles → improve-strategies}/quick.json +4 -2
  17. package/dist/assets/improve-strategies/reflect-distill.json +30 -0
  18. package/dist/assets/{profiles → improve-strategies}/thorough.json +1 -1
  19. package/dist/assets/prompts/consolidate-system.md +5 -5
  20. package/dist/assets/prompts/extract-session.md +2 -6
  21. package/dist/assets/prompts/memory-infer-user.md +2 -3
  22. package/dist/assets/prompts/reflect-llm-framed-contract.md +11 -0
  23. package/dist/assets/prompts/reflect-llm-schema-contract.md +3 -0
  24. package/dist/assets/prompts/reflect-output-repair.md +3 -0
  25. package/dist/assets/prompts/workflow-unit-preamble.md +26 -0
  26. package/dist/assets/stash-skeleton/README.md +38 -10
  27. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +8 -0
  28. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +8 -0
  29. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +14 -1
  30. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +13 -1
  31. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +9 -1
  32. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +11 -0
  33. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +9 -0
  34. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +9 -0
  35. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +8 -0
  36. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +100 -0
  37. package/dist/assets/stash-skeleton/facts/conventions/domains.md +64 -0
  38. package/dist/assets/stash-skeleton/facts/conventions/organization.md +136 -0
  39. package/dist/assets/tasks/core/extract.yml +3 -2
  40. package/dist/assets/tasks/core/improve.yml +2 -1
  41. package/dist/assets/tasks/core/index-refresh.yml +1 -0
  42. package/dist/assets/tasks/core/sync.yml +1 -0
  43. package/dist/assets/tasks/core/version-check.yml +2 -1
  44. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +5 -0
  45. package/dist/assets/tasks/improve/akm-improve-catchup.yml +8 -0
  46. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +5 -0
  47. package/dist/assets/tasks/improve/akm-improve-frequent.yml +5 -0
  48. package/dist/assets/tasks/improve/akm-improve-nightly.yml +5 -0
  49. package/dist/assets/templates/html/health.html +5 -4
  50. package/dist/assets/workflows/workflow-template.md +31 -15
  51. package/dist/cli/invocation.js +279 -0
  52. package/dist/cli/parse-args.js +5 -90
  53. package/dist/cli/retired-commands.js +78 -0
  54. package/dist/cli/shared.js +158 -48
  55. package/dist/cli-node.mjs +2 -1
  56. package/dist/cli.js +747 -293
  57. package/dist/commands/agent/agent-dispatch.js +19 -18
  58. package/dist/commands/agent/agent-support.js +0 -24
  59. package/dist/commands/agent/contribute-cli.js +43 -97
  60. package/dist/commands/completions.js +80 -23
  61. package/dist/commands/config-cli.js +44 -281
  62. package/dist/commands/env/env-binding.js +99 -0
  63. package/dist/commands/env/env-cli.js +84 -224
  64. package/dist/commands/env/env.js +12 -163
  65. package/dist/commands/env/marker-path.js +6 -0
  66. package/dist/commands/env/secret-cli.js +45 -61
  67. package/dist/commands/env/secret.js +32 -62
  68. package/dist/commands/feedback-cli.js +179 -85
  69. package/dist/commands/health/accept-rate.js +58 -0
  70. package/dist/commands/health/advisories.js +7 -8
  71. package/dist/commands/health/checks.js +279 -94
  72. package/dist/commands/health/html-report.js +197 -578
  73. package/dist/commands/health/improve-metrics.js +277 -246
  74. package/dist/commands/health/llm-usage.js +19 -19
  75. package/dist/commands/health/md-report.js +16 -7
  76. package/dist/commands/health/metrics.js +67 -32
  77. package/dist/commands/health/renderers.js +47 -0
  78. package/dist/commands/health/report-view-model.js +508 -0
  79. package/dist/commands/health/stash-exposure.js +1 -1
  80. package/dist/commands/health/surfaces.js +16 -56
  81. package/dist/commands/health/task-runs.js +3 -67
  82. package/dist/{migrate-storage-node.mjs → commands/health/types-checks.js} +1 -5
  83. package/dist/commands/health/types-improve.js +29 -0
  84. package/dist/{output/text/save.js → commands/health/types-metrics.js} +1 -2
  85. package/dist/commands/health/types-result.js +7 -0
  86. package/dist/commands/health/types-runs.js +4 -0
  87. package/dist/commands/health/types-session-log.js +4 -0
  88. package/dist/commands/health/types-windows.js +4 -0
  89. package/dist/commands/health/types.js +26 -21
  90. package/dist/commands/health/windows.js +2 -3
  91. package/dist/commands/health.js +296 -167
  92. package/dist/commands/improve/anti-collapse.js +5 -5
  93. package/dist/commands/improve/autonomy-gate.js +68 -0
  94. package/dist/commands/improve/collapse-detector.js +65 -52
  95. package/dist/commands/improve/consolidate/chunking.js +9 -7
  96. package/dist/commands/improve/consolidate/eligibility.js +1 -23
  97. package/dist/commands/improve/consolidate/merge.js +4 -0
  98. package/dist/commands/improve/consolidate.js +454 -1354
  99. package/dist/commands/improve/content-hash.js +39 -0
  100. package/dist/commands/improve/distill/content-repair.js +4 -10
  101. package/dist/commands/improve/distill/promote-memory.js +89 -64
  102. package/dist/commands/improve/distill/quality-gate.js +118 -42
  103. package/dist/commands/improve/distill-guards.js +1 -1
  104. package/dist/commands/improve/distill-promotion-policy.js +33 -888
  105. package/dist/commands/improve/distill.js +607 -363
  106. package/dist/commands/improve/eligibility.js +165 -79
  107. package/dist/commands/improve/extract-cli.js +35 -126
  108. package/dist/commands/improve/extract-prompt.js +6 -35
  109. package/dist/commands/improve/extract.js +640 -391
  110. package/dist/commands/improve/feedback-valence.js +2 -12
  111. package/dist/commands/improve/improve-cli.js +134 -135
  112. package/dist/commands/improve/improve-result-file.js +30 -50
  113. package/dist/commands/improve/improve-run-types.js +4 -0
  114. package/dist/commands/improve/improve-strategies.js +135 -0
  115. package/dist/commands/improve/improve.js +904 -701
  116. package/dist/commands/improve/locks.js +64 -111
  117. package/dist/commands/improve/loop-stages.js +1110 -923
  118. package/dist/commands/improve/memory/derived-ref.js +124 -0
  119. package/dist/commands/improve/memory/memory-belief.js +79 -7
  120. package/dist/commands/improve/memory/memory-contradiction-detect.js +49 -52
  121. package/dist/commands/improve/memory/memory-improve.js +25 -37
  122. package/dist/commands/improve/outcome-loop.js +25 -88
  123. package/dist/commands/improve/preparation.js +1034 -813
  124. package/dist/commands/improve/proactive-maintenance.js +34 -9
  125. package/dist/commands/improve/proposal-envelope.js +31 -0
  126. package/dist/commands/improve/reflect.js +983 -794
  127. package/dist/commands/improve/run-context.js +119 -0
  128. package/dist/commands/improve/salience.js +24 -127
  129. package/dist/commands/improve/session-asset.js +7 -3
  130. package/dist/commands/improve/shared.js +14 -34
  131. package/dist/commands/improve/source-identity.js +28 -0
  132. package/dist/commands/improve/triage.js +20 -17
  133. package/dist/commands/lint/base-linter.js +340 -313
  134. package/dist/commands/lint/env-key-rules.js +31 -47
  135. package/dist/commands/lint/index.js +185 -30
  136. package/dist/commands/{events.js → log.js} +28 -38
  137. package/dist/commands/migrate-cli.js +54 -0
  138. package/dist/commands/migration-tool.js +55 -0
  139. package/dist/commands/observability-cli.js +70 -208
  140. package/dist/commands/proposal/diff-format.js +50 -0
  141. package/dist/commands/proposal/drain-policies.js +0 -6
  142. package/dist/commands/proposal/drain.js +91 -40
  143. package/dist/commands/proposal/proposal-cli.js +134 -132
  144. package/dist/commands/proposal/proposal-types.js +56 -0
  145. package/dist/commands/proposal/proposal.js +83 -65
  146. package/dist/commands/proposal/propose-cli.js +88 -0
  147. package/dist/commands/proposal/propose.js +105 -88
  148. package/dist/commands/proposal/repository.js +1303 -278
  149. package/dist/commands/proposal/validators/proposal-quality-validators.js +16 -6
  150. package/dist/commands/proposal/validators/proposal-validators.js +61 -12
  151. package/dist/commands/proposal/validators/proposals.js +6 -8
  152. package/dist/commands/read/curate.js +78 -73
  153. package/dist/commands/read/knowledge.js +510 -13
  154. package/dist/commands/read/registry-search.js +2 -2
  155. package/dist/commands/read/remember-cli.js +84 -15
  156. package/dist/commands/read/search-cli.js +203 -96
  157. package/dist/commands/read/search.js +126 -94
  158. package/dist/commands/read/show.js +226 -250
  159. package/dist/commands/registry-cli.js +34 -60
  160. package/dist/commands/remember.js +18 -57
  161. package/dist/commands/sources/add-cli.js +104 -49
  162. package/dist/commands/sources/bundle-cli.js +166 -0
  163. package/dist/commands/sources/bundle-config-ops.js +63 -0
  164. package/dist/commands/sources/info.js +27 -15
  165. package/dist/commands/sources/init.js +30 -40
  166. package/dist/commands/sources/installed-stashes.js +469 -172
  167. package/dist/commands/sources/migration-help.js +7 -4
  168. package/dist/commands/sources/schema-repair.js +10 -9
  169. package/dist/commands/sources/self-update.js +182 -121
  170. package/dist/commands/sources/source-add.js +169 -178
  171. package/dist/commands/sources/source-clone.js +144 -41
  172. package/dist/commands/sources/source-manage.js +94 -59
  173. package/dist/commands/sources/sources-cli.js +64 -205
  174. package/dist/commands/sources/stash-cli.js +91 -54
  175. package/dist/commands/sources/stash-skeleton.js +1 -1
  176. package/dist/commands/tasks/tasks-cli.js +106 -104
  177. package/dist/commands/tasks/tasks.js +445 -262
  178. package/dist/commands/workflow-cli.js +232 -121
  179. package/dist/core/action-contributors.js +1 -1
  180. package/dist/core/activation-policy.js +49 -0
  181. package/dist/core/adapter/adapters/agent-skills-adapter.js +181 -0
  182. package/dist/core/adapter/adapters/akm-adapter.js +528 -0
  183. package/dist/core/adapter/adapters/akm-lint.js +392 -0
  184. package/dist/core/adapter/adapters/akm-metadata.js +387 -0
  185. package/dist/core/adapter/adapters/akm-task-adapter.js +149 -0
  186. package/dist/core/adapter/adapters/akm-workflow-adapter.js +180 -0
  187. package/dist/core/adapter/adapters/claude-adapter.js +61 -0
  188. package/dist/core/adapter/adapters/dotenv-adapter.js +187 -0
  189. package/dist/core/adapter/adapters/generic-files-adapter.js +119 -0
  190. package/dist/core/adapter/adapters/index.js +80 -0
  191. package/dist/core/adapter/adapters/llm-wiki-adapter.js +419 -0
  192. package/dist/core/adapter/adapters/okf-adapter.js +391 -0
  193. package/dist/core/adapter/adapters/opencode-adapter.js +68 -0
  194. package/dist/core/adapter/adapters/shared.js +286 -0
  195. package/dist/core/adapter/adapters/tool-dir-shared.js +217 -0
  196. package/dist/core/adapter/adapters/website-snapshot-adapter.js +155 -0
  197. package/dist/core/adapter/bundle-adapter.js +4 -0
  198. package/dist/core/adapter/detect-adapter.js +17 -0
  199. package/dist/core/adapter/recognize-match.js +44 -0
  200. package/dist/core/adapter/registry.js +56 -0
  201. package/dist/core/adapter/types.js +4 -0
  202. package/dist/core/asset/akm-markdown.js +30 -0
  203. package/dist/core/asset/asset-placement.js +243 -0
  204. package/dist/core/asset/asset-ref.js +110 -79
  205. package/dist/core/asset/asset-serialize.js +20 -0
  206. package/dist/core/asset/frontmatter.js +28 -12
  207. package/dist/core/asset/markdown.js +40 -51
  208. package/dist/core/asset/resolve-ref.js +274 -0
  209. package/dist/core/asset/stash-meta.js +2 -2
  210. package/dist/core/bundle-id.js +51 -0
  211. package/dist/core/common.js +281 -86
  212. package/dist/core/config/config-io.js +42 -128
  213. package/dist/core/config/config-schema.js +233 -834
  214. package/dist/core/config/config-sources.js +162 -39
  215. package/dist/core/config/config-types.js +16 -11
  216. package/dist/core/config/config-version.js +29 -0
  217. package/dist/core/config/config-walker.js +126 -37
  218. package/dist/core/config/config.js +154 -331
  219. package/dist/core/config/deep-merge.js +41 -0
  220. package/dist/core/config/engine-semantics.js +28 -0
  221. package/dist/core/config/experimental.js +21 -0
  222. package/dist/core/config/schema/embedding.js +38 -0
  223. package/dist/core/config/schema/engines.js +116 -0
  224. package/dist/core/config/schema/experimental.js +47 -0
  225. package/dist/core/config/schema/feedback.js +31 -0
  226. package/dist/core/config/schema/improve-processes.js +389 -0
  227. package/dist/core/config/schema/improve.js +94 -0
  228. package/dist/core/config/schema/index-config.js +176 -0
  229. package/dist/core/config/schema/output.js +18 -0
  230. package/dist/core/config/schema/primitives.js +94 -0
  231. package/dist/core/config/schema/search.js +30 -0
  232. package/dist/core/config/schema/setup.js +18 -0
  233. package/dist/core/config/schema/sources-bundles.js +169 -0
  234. package/dist/core/config/schema/workflow.js +29 -0
  235. package/dist/core/env-secret-ref.js +155 -20
  236. package/dist/core/errors.js +17 -15
  237. package/dist/core/events-types.js +4 -0
  238. package/dist/core/events.js +46 -128
  239. package/dist/core/extra-params.js +62 -0
  240. package/dist/core/file-change.js +17 -0
  241. package/dist/core/file-lock.js +202 -57
  242. package/dist/core/fs-txn.js +392 -0
  243. package/dist/core/git-message.js +59 -0
  244. package/dist/core/improve-result.js +167 -0
  245. package/dist/core/json-schema.js +142 -0
  246. package/dist/core/lesson-lint.js +1 -17
  247. package/dist/core/logs-db.js +1 -1
  248. package/dist/core/maintenance-barrier.js +135 -0
  249. package/dist/core/migration-operation.js +44 -0
  250. package/dist/core/mutation-target.js +78 -0
  251. package/dist/core/paths.js +22 -25
  252. package/dist/core/platform.js +10 -0
  253. package/dist/core/recognition-util.js +128 -0
  254. package/dist/core/redaction.js +392 -0
  255. package/dist/core/standards/resolve-standards-context.js +36 -65
  256. package/dist/core/standards/resolve-stash-standards.js +2 -2
  257. package/dist/core/standards/resolve-type-conventions.js +5 -5
  258. package/dist/core/state/migrations.js +242 -11
  259. package/dist/core/state-db.js +98 -10
  260. package/dist/core/structured.js +1 -1
  261. package/dist/core/subprocess.js +303 -0
  262. package/dist/core/text-truncation.js +9 -5
  263. package/dist/core/time.js +20 -0
  264. package/dist/core/type-presentation.js +130 -0
  265. package/dist/core/warn.js +0 -3
  266. package/dist/core/write-source.js +834 -118
  267. package/dist/indexer/bundle-identity-guard.js +92 -0
  268. package/dist/indexer/db/graph-db.js +1 -25
  269. package/dist/indexer/db/llm-cache.js +1 -1
  270. package/dist/indexer/ensure-index.js +30 -9
  271. package/dist/indexer/graph/graph-boost.js +9 -30
  272. package/dist/indexer/graph/graph-extraction.js +41 -27
  273. package/dist/indexer/graph/graph-types.js +4 -0
  274. package/dist/indexer/index-writer-lock.js +93 -49
  275. package/dist/indexer/index-written-assets.js +100 -53
  276. package/dist/indexer/indexer.js +746 -329
  277. package/dist/indexer/init.js +18 -25
  278. package/dist/indexer/installations.js +142 -0
  279. package/dist/indexer/passes/dir-staleness.js +18 -10
  280. package/dist/indexer/passes/memory-inference.js +25 -15
  281. package/dist/indexer/passes/metadata.js +412 -243
  282. package/dist/indexer/scan/doc-to-entry.js +160 -0
  283. package/dist/indexer/scan/drain-dir.js +134 -0
  284. package/dist/indexer/search/db-search.js +292 -108
  285. package/dist/indexer/search/fts-query.js +64 -0
  286. package/dist/indexer/search/ranking-contributors.js +145 -25
  287. package/dist/indexer/search/ranking-types.js +4 -0
  288. package/dist/indexer/search/ranking.js +28 -71
  289. package/dist/indexer/search/search-attribution.js +67 -0
  290. package/dist/indexer/search/search-fields.js +18 -3
  291. package/dist/indexer/search/search-hit-enrichers.js +30 -40
  292. package/dist/indexer/search/search-source.js +157 -111
  293. package/dist/indexer/search/semantic-status.js +4 -1
  294. package/dist/indexer/usage/usage-events.js +10 -30
  295. package/dist/indexer/walk/file-context.js +3 -45
  296. package/dist/indexer/walk/matchers.js +42 -34
  297. package/dist/indexer/walk/path-resolver.js +11 -5
  298. package/dist/indexer/walk/walker.js +42 -14
  299. package/dist/integrations/agent/builder-shared.js +7 -0
  300. package/dist/integrations/agent/builders.js +5 -56
  301. package/dist/integrations/agent/config.js +3 -143
  302. package/dist/integrations/agent/detect.js +17 -2
  303. package/dist/integrations/agent/engine-resolution.js +231 -0
  304. package/dist/integrations/agent/index.js +1 -2
  305. package/dist/integrations/agent/model-aliases.js +16 -2
  306. package/dist/integrations/agent/profiles.js +36 -62
  307. package/dist/integrations/agent/prompts.js +46 -18
  308. package/dist/integrations/agent/runner-dispatch.js +93 -4
  309. package/dist/integrations/agent/runner.js +76 -208
  310. package/dist/integrations/agent/spawn.js +88 -196
  311. package/dist/integrations/harnesses/aider/agent-builder.js +114 -0
  312. package/dist/integrations/harnesses/aider/index.js +48 -0
  313. package/dist/integrations/harnesses/aider/result-extractor.js +53 -0
  314. package/dist/integrations/harnesses/amazonq/agent-builder.js +147 -0
  315. package/dist/integrations/harnesses/amazonq/index.js +45 -0
  316. package/dist/integrations/harnesses/amazonq/result-extractor.js +48 -0
  317. package/dist/integrations/harnesses/claude/agent-builder.js +46 -8
  318. package/dist/integrations/harnesses/claude/config-import.js +1 -3
  319. package/dist/integrations/harnesses/claude/index.js +24 -35
  320. package/dist/integrations/harnesses/claude/result-extractor.js +52 -0
  321. package/dist/integrations/harnesses/claude/session-log.js +27 -75
  322. package/dist/integrations/harnesses/codex/agent-builder.js +138 -0
  323. package/dist/integrations/harnesses/codex/index.js +52 -0
  324. package/dist/integrations/harnesses/codex/result-extractor.js +73 -0
  325. package/dist/integrations/harnesses/copilot/agent-builder.js +122 -0
  326. package/dist/integrations/harnesses/copilot/index.js +48 -0
  327. package/dist/integrations/harnesses/copilot/result-extractor.js +151 -0
  328. package/dist/integrations/harnesses/gemini/agent-builder.js +120 -0
  329. package/dist/integrations/harnesses/gemini/index.js +48 -0
  330. package/dist/integrations/harnesses/gemini/result-extractor.js +121 -0
  331. package/dist/integrations/harnesses/ids.js +24 -0
  332. package/dist/integrations/harnesses/index.js +54 -34
  333. package/dist/integrations/harnesses/opencode/agent-builder.js +23 -5
  334. package/dist/integrations/harnesses/opencode/config-import.js +1 -3
  335. package/dist/integrations/harnesses/opencode/index.js +14 -32
  336. package/dist/integrations/harnesses/opencode/session-log.js +67 -125
  337. package/dist/integrations/harnesses/opencode-sdk/harness.js +51 -0
  338. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +681 -108
  339. package/dist/integrations/harnesses/openhands/agent-builder.js +128 -0
  340. package/dist/integrations/harnesses/openhands/index.js +48 -0
  341. package/dist/integrations/harnesses/openhands/result-extractor.js +103 -0
  342. package/dist/integrations/harnesses/pi/agent-builder.js +97 -0
  343. package/dist/integrations/harnesses/pi/index.js +45 -0
  344. package/dist/integrations/harnesses/pi/result-extractor.js +135 -0
  345. package/dist/integrations/harnesses/shared.js +17 -0
  346. package/dist/integrations/harnesses/types.js +43 -32
  347. package/dist/integrations/lockfile.js +211 -24
  348. package/dist/integrations/session-logs/index.js +36 -39
  349. package/dist/integrations/session-logs/provider-base.js +113 -0
  350. package/dist/llm/client.js +182 -110
  351. package/dist/llm/embedders/deterministic.js +2 -2
  352. package/dist/llm/embedders/remote.js +21 -9
  353. package/dist/llm/feature-gate.js +17 -57
  354. package/dist/llm/graph-extract.js +12 -13
  355. package/dist/llm/index-passes.js +8 -42
  356. package/dist/llm/memory-infer.js +144 -1
  357. package/dist/llm/metadata-enhance.js +45 -30
  358. package/dist/llm/structured-call.js +16 -8
  359. package/dist/llm/usage-persist.js +30 -5
  360. package/dist/llm/usage-telemetry.js +59 -6
  361. package/dist/output/cli-hints.js +1 -2
  362. package/dist/output/command-registry.js +27 -0
  363. package/dist/output/context.js +22 -7
  364. package/dist/output/format-exempt.js +80 -0
  365. package/dist/output/generic-render.js +251 -0
  366. package/dist/output/html-render.js +11 -16
  367. package/dist/output/render-registry.js +57 -0
  368. package/dist/output/renderers.js +14 -279
  369. package/dist/output/shapes/curate.js +10 -1
  370. package/dist/output/shapes/events.js +12 -7
  371. package/dist/output/shapes/helpers.js +58 -84
  372. package/dist/output/shapes/passthrough.js +11 -39
  373. package/dist/output/shapes/proposal/producer.js +15 -7
  374. package/dist/output/shapes/registry.js +12 -6
  375. package/dist/output/shapes.js +0 -9
  376. package/dist/output/text/{init.js → bundle-create.js} +3 -1
  377. package/dist/output/text/bundle-show.js +7 -0
  378. package/dist/output/text/command-format.js +562 -0
  379. package/dist/output/text/env.js +1 -3
  380. package/dist/output/text/events.js +8 -7
  381. package/dist/output/text/helpers.js +15 -1164
  382. package/dist/output/text/proposal/producer.js +4 -2
  383. package/dist/output/text/proposal-format.js +202 -0
  384. package/dist/output/text/registry-commands.js +1 -2
  385. package/dist/output/text/registry.js +12 -6
  386. package/dist/output/text/show-directives.js +117 -0
  387. package/dist/output/text/show-format.js +103 -0
  388. package/dist/output/text/sync.js +5 -0
  389. package/dist/output/text/workflow-format.js +332 -0
  390. package/dist/output/text/workflow.js +3 -2
  391. package/dist/output/text.js +10 -19
  392. package/dist/registry/factory.js +4 -6
  393. package/dist/registry/origin-resolve.js +16 -27
  394. package/dist/registry/providers/skills-sh.js +3 -3
  395. package/dist/registry/providers/static-index.js +15 -25
  396. package/dist/registry/resolve.js +43 -94
  397. package/dist/registry/semver.js +43 -0
  398. package/dist/runtime.js +81 -12
  399. package/dist/scripts/akm-migrate.js +35529 -0
  400. package/dist/setup/detect.js +5 -7
  401. package/dist/setup/detected-engines.js +136 -0
  402. package/dist/setup/engine-config.js +100 -0
  403. package/dist/setup/registry-stash-loader.js +3 -3
  404. package/dist/setup/semantic-assets.js +12 -9
  405. package/dist/setup/setup.js +444 -208
  406. package/dist/setup/steps/connection-shared.js +120 -0
  407. package/dist/setup/steps/connection.js +108 -305
  408. package/dist/setup/steps/platforms.js +13 -12
  409. package/dist/setup/steps/semantic.js +15 -3
  410. package/dist/setup/steps/sources.js +21 -15
  411. package/dist/setup/steps/stashdir.js +6 -4
  412. package/dist/setup/steps/tasks.js +236 -119
  413. package/dist/setup/steps.js +3 -2
  414. package/dist/sources/freshness.js +39 -0
  415. package/dist/sources/provider-factory.js +11 -17
  416. package/dist/sources/providers/filesystem.js +2 -3
  417. package/dist/sources/providers/git-install.js +278 -34
  418. package/dist/sources/providers/git-provider.js +54 -56
  419. package/dist/sources/providers/git-stash.js +420 -91
  420. package/dist/sources/providers/git.js +2 -2
  421. package/dist/sources/providers/npm.js +16 -19
  422. package/dist/sources/providers/provider-utils.js +47 -22
  423. package/dist/sources/providers/sync-from-ref.js +3 -9
  424. package/dist/sources/providers/website.js +2 -2
  425. package/dist/sources/resolve.js +11 -10
  426. package/dist/sources/snapshot-fetchers/types.js +4 -0
  427. package/dist/sources/{website-ingest.js → snapshot-fetchers/website-ingest.js} +110 -41
  428. package/dist/storage/database.js +60 -4
  429. package/dist/storage/engines/sqlite-migrations.js +156 -5
  430. package/dist/storage/locations.js +1 -2
  431. package/dist/storage/repositories/canaries-repository.js +1 -1
  432. package/dist/storage/repositories/events-repository.js +51 -11
  433. package/dist/storage/repositories/improve-runs-repository.js +6 -32
  434. package/dist/storage/repositories/index-connection.js +79 -0
  435. package/dist/storage/repositories/index-db.js +4 -3
  436. package/dist/storage/repositories/index-entries-repository.js +863 -0
  437. package/dist/{indexer/db/entry-mapper.js → storage/repositories/index-entry-mapper.js} +19 -2
  438. package/dist/storage/repositories/index-entry-types.js +4 -0
  439. package/dist/storage/repositories/index-fts-repository.js +167 -0
  440. package/dist/storage/repositories/index-llm-cache-repository.js +108 -0
  441. package/dist/storage/repositories/index-meta-repository.js +49 -0
  442. package/dist/{indexer/db/schema.js → storage/repositories/index-schema.js} +226 -100
  443. package/dist/storage/repositories/index-sql.js +12 -0
  444. package/dist/storage/repositories/index-utility-repository.js +356 -0
  445. package/dist/storage/repositories/index-vec-repository.js +250 -0
  446. package/dist/storage/repositories/outcome-repository.js +119 -0
  447. package/dist/storage/repositories/proposals-repository.js +317 -75
  448. package/dist/storage/repositories/registry-cache.js +1 -1
  449. package/dist/storage/repositories/salience-repository.js +172 -0
  450. package/dist/storage/repositories/task-history-repository.js +110 -3
  451. package/dist/storage/repositories/workflow-runs-repository.js +240 -19
  452. package/dist/tasks/backends/cron.js +169 -46
  453. package/dist/tasks/backends/exec-utils.js +76 -3
  454. package/dist/tasks/backends/index.js +6 -9
  455. package/dist/tasks/backends/launchd.js +292 -55
  456. package/dist/tasks/backends/schtasks.js +557 -70
  457. package/dist/tasks/backends/types.js +4 -0
  458. package/dist/tasks/command-executable.js +93 -0
  459. package/dist/tasks/embedded.js +56 -38
  460. package/dist/tasks/parser.js +156 -64
  461. package/dist/tasks/resolve-akm-bin.js +144 -51
  462. package/dist/tasks/runner.js +377 -209
  463. package/dist/tasks/schedule.js +108 -19
  464. package/dist/tasks/scheduler-invocation.js +296 -0
  465. package/dist/tasks/schema.js +1 -1
  466. package/dist/tasks/task-id.js +35 -0
  467. package/dist/tasks/validator.js +30 -16
  468. package/dist/text-import-hook.mjs +1 -1
  469. package/dist/workflows/authoring/authoring.js +104 -43
  470. package/dist/workflows/authoring/scope-key.js +1 -1
  471. package/dist/workflows/cli.js +0 -16
  472. package/dist/workflows/concurrency-policy.js +15 -0
  473. package/dist/workflows/exec/brief.js +450 -0
  474. package/dist/workflows/exec/frozen-judge.js +47 -0
  475. package/dist/workflows/exec/native-executor.js +1038 -0
  476. package/dist/workflows/exec/param-secrets.js +115 -0
  477. package/dist/workflows/exec/report.js +1460 -0
  478. package/dist/workflows/exec/run-workflow.js +602 -0
  479. package/dist/workflows/exec/scheduler.js +71 -0
  480. package/dist/workflows/exec/step-work.js +1190 -0
  481. package/dist/workflows/exec/unit-writer.js +23 -0
  482. package/dist/workflows/exec/workflow-engine-gate.js +67 -0
  483. package/dist/workflows/exec/worktree.js +171 -0
  484. package/dist/workflows/ir/compile.js +246 -0
  485. package/dist/workflows/ir/freeze.js +233 -0
  486. package/dist/workflows/ir/params.js +54 -0
  487. package/dist/workflows/ir/plan-hash.js +68 -0
  488. package/dist/workflows/ir/schema.js +540 -0
  489. package/dist/workflows/parser.js +878 -304
  490. package/dist/workflows/program/expressions.js +181 -0
  491. package/dist/workflows/program/schema.js +51 -0
  492. package/dist/workflows/renderer.js +100 -45
  493. package/dist/workflows/resource-limits.js +22 -0
  494. package/dist/workflows/runtime/agent-identity.js +59 -14
  495. package/dist/workflows/runtime/checkin.js +1 -1
  496. package/dist/workflows/runtime/plan-classifier.js +131 -0
  497. package/dist/workflows/runtime/runs.js +376 -119
  498. package/dist/workflows/runtime/unit-checkin.js +45 -0
  499. package/dist/workflows/runtime/unit-phases.js +20 -0
  500. package/dist/workflows/runtime/workflow-asset-loader.js +241 -40
  501. package/dist/workflows/schema.js +1 -11
  502. package/dist/workflows/validate-summary.js +2 -3
  503. package/dist/workflows/validator.js +52 -30
  504. package/docs/README.md +42 -78
  505. package/docs/migration/README.md +8 -0
  506. package/docs/migration/release-notes/0.6.0.md +1 -1
  507. package/docs/migration/release-notes/0.7.0.md +9 -8
  508. package/docs/migration/release-notes/0.9.0.md +158 -14
  509. package/docs/migration/v0.7-to-v0.8.md +46 -47
  510. package/docs/migration/v0.8-to-v0.9.md +844 -0
  511. package/docs/reference/README.md +12 -0
  512. package/docs/reference/data-and-telemetry.md +333 -0
  513. package/package.json +21 -17
  514. package/schemas/akm-asset-envelope.json +93 -0
  515. package/schemas/akm-config.json +4636 -0
  516. package/schemas/akm-task.json +87 -0
  517. package/schemas/akm-workflow.json +373 -0
  518. package/dist/akm-migrate-storage +0 -38
  519. package/dist/assets/help/help-accept.md +0 -12
  520. package/dist/assets/help/help-improve.md +0 -84
  521. package/dist/assets/help/help-proposals.md +0 -17
  522. package/dist/assets/help/help-propose.md +0 -17
  523. package/dist/assets/help/help-reject.md +0 -11
  524. package/dist/assets/profiles/frequent.json +0 -13
  525. package/dist/assets/profiles/recombine-only.json +0 -21
  526. package/dist/assets/profiles/reflect-distill.json +0 -30
  527. package/dist/assets/profiles/synthesize.json +0 -15
  528. package/dist/assets/prompts/procedural-system.md +0 -44
  529. package/dist/assets/prompts/recombine-system.md +0 -40
  530. package/dist/assets/prompts/staleness-detect-system.md +0 -6
  531. package/dist/assets/tasks/core/backup.yml +0 -4
  532. package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
  533. package/dist/assets/templates/html/default.html +0 -78
  534. package/dist/assets/templates/html/vendor/echarts.min.js +0 -45
  535. package/dist/assets/wiki/index-template.md +0 -12
  536. package/dist/assets/wiki/ingest-workflow-template.md +0 -83
  537. package/dist/assets/wiki/log-template.md +0 -8
  538. package/dist/assets/wiki/schema-template.md +0 -61
  539. package/dist/cli/config-migrate.js +0 -150
  540. package/dist/cli/config-validate.js +0 -39
  541. package/dist/commands/graph/graph-cli.js +0 -124
  542. package/dist/commands/graph/graph.js +0 -487
  543. package/dist/commands/improve/calibration.js +0 -161
  544. package/dist/commands/improve/dedup.js +0 -482
  545. package/dist/commands/improve/extract-watch.js +0 -140
  546. package/dist/commands/improve/hot-probation.js +0 -45
  547. package/dist/commands/improve/improve-auto-accept.js +0 -276
  548. package/dist/commands/improve/improve-profiles.js +0 -168
  549. package/dist/commands/improve/procedural.js +0 -398
  550. package/dist/commands/improve/recombine.js +0 -818
  551. package/dist/commands/improve/schema-similarity-gate.js +0 -168
  552. package/dist/commands/lint/agent-linter.js +0 -44
  553. package/dist/commands/lint/command-linter.js +0 -44
  554. package/dist/commands/lint/default-linter.js +0 -16
  555. package/dist/commands/lint/fact-linter.js +0 -39
  556. package/dist/commands/lint/knowledge-linter.js +0 -16
  557. package/dist/commands/lint/memory-linter.js +0 -61
  558. package/dist/commands/lint/registry.js +0 -41
  559. package/dist/commands/lint/skill-linter.js +0 -45
  560. package/dist/commands/lint/task-linter.js +0 -50
  561. package/dist/commands/lint/workflow-linter.js +0 -81
  562. package/dist/commands/proposal/legacy-import.js +0 -115
  563. package/dist/commands/sources/history.js +0 -196
  564. package/dist/commands/tasks/default-tasks.js +0 -186
  565. package/dist/commands/wiki-cli.js +0 -292
  566. package/dist/core/asset/asset-registry.js +0 -76
  567. package/dist/core/asset/asset-spec.js +0 -259
  568. package/dist/core/config/config-migration.js +0 -602
  569. package/dist/core/deep-merge.js +0 -38
  570. package/dist/core/eval/rank-metrics.js +0 -113
  571. package/dist/core/ripgrep/install.js +0 -163
  572. package/dist/core/ripgrep/resolve.js +0 -81
  573. package/dist/indexer/db/db.js +0 -1413
  574. package/dist/indexer/manifest.js +0 -170
  575. package/dist/indexer/passes/metadata-contributors.js +0 -31
  576. package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -94
  577. package/dist/integrations/harnesses/opencode-sdk/index.js +0 -49
  578. package/dist/llm/call-ai.js +0 -62
  579. package/dist/llm/memory-infer-impl.js +0 -138
  580. package/dist/output/shapes/distill.js +0 -14
  581. package/dist/output/shapes/history.js +0 -11
  582. package/dist/output/text/distill.js +0 -6
  583. package/dist/output/text/enable-disable.js +0 -8
  584. package/dist/output/text/history.js +0 -6
  585. package/dist/output/text/wiki.js +0 -16
  586. package/dist/registry/build-index.js +0 -386
  587. package/dist/scripts/migrate-storage.js +0 -19108
  588. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +0 -9411
  589. package/dist/scripts/migrations/v16-to-v17.js +0 -141
  590. package/dist/setup/legacy-config.js +0 -106
  591. package/dist/storage/repositories/consolidation-repository.js +0 -38
  592. package/dist/storage/repositories/recombine-repository.js +0 -213
  593. package/dist/wiki/wiki-templates.js +0 -15
  594. package/dist/wiki/wiki.js +0 -1012
  595. package/dist/workflows/db.js +0 -215
  596. package/docs/data-and-telemetry.md +0 -226
  597. /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/registry.js +0 -0
  598. /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/youtube.js +0 -0
@@ -4,8 +4,9 @@
4
4
  import fs from "node:fs";
5
5
  import path from "node:path";
6
6
  import { writeFileAtomic } from "../core/common.js";
7
- import { rethrowIfTestIsolationError } from "../core/errors.js";
8
- import { probeLock, releaseLock, tryAcquireLockSync } from "../core/file-lock.js";
7
+ import { ConfigError, rethrowIfTestIsolationError } from "../core/errors.js";
8
+ import { createLockPayload, probeLock, reclaimStaleLock, releaseLock, tryAcquireLockSync } from "../core/file-lock.js";
9
+ import { acquireMaintenanceBarrier } from "../core/maintenance-barrier.js";
9
10
  import { getDataDir, getLockfileLockPath, getLockfilePath } from "../core/paths.js";
10
11
  // ── Lock sentinel ────────────────────────────────────────────────────────────
11
12
  const LOCK_MAX_RETRIES = 3;
@@ -15,23 +16,26 @@ async function acquireLockSentinel() {
15
16
  // Ensure the directory exists before attempting to create the sentinel.
16
17
  fs.mkdirSync(path.dirname(sentinelPath), { recursive: true });
17
18
  for (let attempt = 0; attempt < LOCK_MAX_RETRIES; attempt++) {
18
- if (tryAcquireLockSync(sentinelPath, String(process.pid))) {
19
- return true; // Sentinel created — we own the lock.
19
+ const releaseBarrier = acquireMaintenanceBarrier();
20
+ try {
21
+ const ownership = tryAcquireLockSync(sentinelPath, createLockPayload());
22
+ if (ownership) {
23
+ return () => releaseLock(ownership);
24
+ }
25
+ const probe = probeLock(sentinelPath);
26
+ if (probe.state === "stale" && reclaimStaleLock(sentinelPath, probe)) {
27
+ continue; // Reclaimed — retry immediately.
28
+ }
20
29
  }
21
- if (probeLock(sentinelPath).state === "stale") {
22
- releaseLock(sentinelPath);
23
- continue; // Reclaimed — retry immediately.
30
+ finally {
31
+ releaseBarrier();
24
32
  }
25
33
  // Another process holds the lock — wait briefly before retrying.
26
34
  if (attempt < LOCK_MAX_RETRIES - 1) {
27
35
  await new Promise((resolve) => setTimeout(resolve, LOCK_RETRY_DELAY_MS));
28
36
  }
29
37
  }
30
- // Best-effort: proceed without the lock rather than failing the install.
31
- return false;
32
- }
33
- function releaseLockSentinel() {
34
- releaseLock(getLockfileLockPath());
38
+ throw new ConfigError(`Could not acquire lockfile sentinel at ${sentinelPath}; refusing to write without exclusive ownership.`, "INVALID_CONFIG_FILE");
35
39
  }
36
40
  // ── Read / Write ────────────────────────────────────────────────────────────
37
41
  export function readLockfile() {
@@ -50,40 +54,223 @@ export function readLockfile() {
50
54
  return [];
51
55
  }
52
56
  }
53
- export function writeLockfile(entries) {
57
+ /**
58
+ * Like {@link readLockfile}, but THROWS instead of silently degrading to `[]`
59
+ * when the on-disk lockfile exists yet is not parseable JSON or not a JSON
60
+ * array (R-012).
61
+ *
62
+ * `readLockfile`'s fail-open contract is intentional for READ paths — a
63
+ * corrupt lock degrades a managed bundle to "unmanaged" rather than erroring
64
+ * every read-only command (`list`, `installed-stashes`, …). But
65
+ * {@link upsertLockEntry} and {@link removeLockEntry} read the current
66
+ * entries and then WRITE `[...entries, change]` back out; if that read
67
+ * silently returned `[]` for a corrupt file, the write would silently
68
+ * replace the corrupt file with one containing only the single new/changed
69
+ * entry — permanently destroying every other surviving lock record. Write
70
+ * paths use this strict variant so a corrupt lockfile fails the operation
71
+ * loudly (matching the guard `mergeLockEntriesSync` already applies) instead
72
+ * of quietly deleting user state. A missing file is NOT corruption — there
73
+ * is nothing to preserve, so that case still returns `[]`. Entries that fail
74
+ * per-entry validation are still tolerated (filtered out), matching
75
+ * `readLockfile`'s existing shape-tolerant behavior.
76
+ */
77
+ function readLockfileOrThrow() {
78
+ const lockfilePath = getLockfilePath();
79
+ let raw;
80
+ try {
81
+ raw = fs.readFileSync(lockfilePath, "utf8");
82
+ }
83
+ catch (err) {
84
+ rethrowIfTestIsolationError(err);
85
+ return []; // File does not exist (or is otherwise unreadable) — nothing to preserve.
86
+ }
87
+ let parsed;
88
+ try {
89
+ parsed = JSON.parse(raw);
90
+ }
91
+ catch (error) {
92
+ throw new ConfigError(`Refusing to modify lockfile ${lockfilePath}: existing content is not valid JSON (${error instanceof Error ? error.message : String(error)}). Fix or remove the file by hand before retrying — every existing lock entry would otherwise be lost.`, "INVALID_CONFIG_FILE");
93
+ }
94
+ if (!Array.isArray(parsed)) {
95
+ throw new ConfigError(`Refusing to modify lockfile ${lockfilePath}: existing content is not a JSON array. Fix or remove the file by hand before retrying — every existing lock entry would otherwise be lost.`, "INVALID_CONFIG_FILE");
96
+ }
97
+ return parsed.filter(isValidLockfileEntry);
98
+ }
99
+ /**
100
+ * The materialized content root recorded in the lock for a managed (git/npm)
101
+ * bundle — spec §10.2 desired/resolved split, where the desired config carries
102
+ * only the source LOCATOR and the resolved `localRoot` lives here.
103
+ *
104
+ * This is the SINGLE lock-first resolution point shared by the indexer READ
105
+ * path (`resolveEntryContentDir` in indexer/search) and the command-layer WRITE
106
+ * path (`adaptConfiguredSource` in core/write-source): consulting it first makes
107
+ * a write land in exactly the directory a read walks. Returns `undefined` for a
108
+ * bundle with no lock `localRoot` (e.g. a config migrated from a `sources[]`
109
+ * url, whose provider re-derives the cache path) or a non-managed type, so both
110
+ * callers fall back to the identical provider-path derivation.
111
+ */
112
+ export function lockContentRootFor(bundleId, type) {
113
+ if (!bundleId || (type !== "git" && type !== "npm"))
114
+ return undefined;
115
+ for (const lock of readLockfile()) {
116
+ if (lock.id === bundleId && typeof lock.localRoot === "string" && lock.localRoot.length > 0) {
117
+ return lock.localRoot;
118
+ }
119
+ }
120
+ return undefined;
121
+ }
122
+ function writeLockfileUnlocked(entries) {
54
123
  // Always write to $DATA — never to the legacy $CONFIG location.
55
124
  const lockfilePath = getLockfilePath();
56
125
  const dir = path.dirname(lockfilePath);
57
126
  fs.mkdirSync(dir, { recursive: true });
58
127
  writeFileAtomic(lockfilePath, `${JSON.stringify(entries, null, 2)}\n`);
59
128
  }
129
+ export async function writeLockfile(entries) {
130
+ const release = await acquireLockSentinel();
131
+ try {
132
+ writeLockfileUnlocked(entries);
133
+ }
134
+ finally {
135
+ release();
136
+ }
137
+ }
138
+ /** Replace one exact parsed lock generation while the lockfile sentinel is held. */
139
+ export async function compareAndSwapLockfile(expected, desired) {
140
+ const release = await acquireLockSentinel();
141
+ try {
142
+ const current = readLockfileOrThrow();
143
+ if (JSON.stringify(current) !== JSON.stringify(expected))
144
+ return false;
145
+ writeLockfileUnlocked(desired);
146
+ return true;
147
+ }
148
+ finally {
149
+ release();
150
+ }
151
+ }
60
152
  export async function upsertLockEntry(entry) {
61
- const acquired = await acquireLockSentinel();
153
+ const release = await acquireLockSentinel();
62
154
  try {
63
- const entries = readLockfile();
155
+ // R-012: readLockfileOrThrow (not readLockfile) — a corrupt/malformed
156
+ // lockfile must abort the upsert loudly rather than read as `[]` and get
157
+ // silently overwritten with just this one entry.
158
+ const entries = readLockfileOrThrow();
64
159
  const withoutExisting = entries.filter((e) => e.id !== entry.id);
65
- writeLockfile([...withoutExisting, entry]);
160
+ writeLockfileUnlocked([...withoutExisting, entry]);
66
161
  }
67
162
  finally {
68
- if (acquired)
69
- releaseLockSentinel();
163
+ release();
70
164
  }
71
165
  }
166
+ /**
167
+ * Synchronously upsert lock entries (merge by id) WITHOUT acquiring the async
168
+ * sentinel — for a caller already holding an exclusive lifecycle lock (e.g.
169
+ * migrate-apply's config lock + maintenance barrier) whose synchronous body
170
+ * cannot await the sentinel's retry loop. No-op for an empty list.
171
+ */
172
+ export function mergeLockEntriesSync(entries) {
173
+ if (entries.length === 0)
174
+ return;
175
+ let existing = [];
176
+ const lockfilePath = getLockfilePath();
177
+ if (fs.existsSync(lockfilePath)) {
178
+ let raw;
179
+ try {
180
+ raw = JSON.parse(fs.readFileSync(lockfilePath, "utf8"));
181
+ }
182
+ catch (error) {
183
+ throw new ConfigError(`Cannot merge migration lock entries into unreadable lockfile ${lockfilePath}: ${error instanceof Error ? error.message : String(error)}.`, "INVALID_CONFIG_FILE");
184
+ }
185
+ if (!Array.isArray(raw) || !raw.every(isValidLockfileEntry)) {
186
+ throw new ConfigError(`Cannot merge migration lock entries into malformed lockfile ${lockfilePath}.`, "INVALID_CONFIG_FILE");
187
+ }
188
+ existing = raw;
189
+ }
190
+ // MERGE per id, never replace: the migrator's entries are sparse (id/source/
191
+ // ref/localRoot), while an existing row may carry `resolvedVersion`,
192
+ // `resolvedRevision`, `integrity`, `installedAt`, … from a real install.
193
+ // Replacing the whole row discarded the user's recorded resolution/pin and
194
+ // left later update reporting comparing against an unknown prior version.
195
+ // Incoming DEFINED fields win; existing fields absent from the incoming
196
+ // entry are preserved.
197
+ const byId = new Map(existing.map((e) => [e.id, e]));
198
+ const merged = entries.map((incoming) => {
199
+ const prior = byId.get(incoming.id);
200
+ return prior ? { ...prior, ...incoming } : incoming;
201
+ });
202
+ const incomingIds = new Set(entries.map((e) => e.id));
203
+ writeLockfileUnlocked([...existing.filter((e) => !incomingIds.has(e.id)), ...merged]);
204
+ }
205
+ /** Freeze exact old/desired lockfile bytes while the migration lifecycle lock is held. */
206
+ export function planLockMergeSync(entries) {
207
+ const lockfilePath = getLockfilePath();
208
+ let oldBytes = null;
209
+ let existing = [];
210
+ try {
211
+ oldBytes = fs.readFileSync(lockfilePath, "utf8");
212
+ }
213
+ catch (error) {
214
+ if (error.code !== "ENOENT")
215
+ throw error;
216
+ }
217
+ if (oldBytes !== null) {
218
+ let parsed;
219
+ try {
220
+ parsed = JSON.parse(oldBytes);
221
+ }
222
+ catch (error) {
223
+ throw new ConfigError(`Cannot plan migration lock entries from unreadable lockfile ${lockfilePath}: ${error instanceof Error ? error.message : String(error)}.`, "INVALID_CONFIG_FILE");
224
+ }
225
+ if (!Array.isArray(parsed) || !parsed.every(isValidLockfileEntry)) {
226
+ throw new ConfigError(`Cannot plan migration lock entries from malformed lockfile ${lockfilePath}.`, "INVALID_CONFIG_FILE");
227
+ }
228
+ existing = parsed;
229
+ }
230
+ if (entries.length === 0)
231
+ return { oldBytes, desiredBytes: oldBytes };
232
+ const byId = new Map(existing.map((entry) => [entry.id, entry]));
233
+ for (const entry of entries)
234
+ byId.set(entry.id, { ...byId.get(entry.id), ...entry });
235
+ return { oldBytes, desiredBytes: `${JSON.stringify([...byId.values()], null, 2)}\n` };
236
+ }
237
+ /** Publish a frozen migration lock generation, accepting only exact old or exact desired bytes. */
238
+ export function publishPlannedLockMergeSync(plan) {
239
+ const lockfilePath = getLockfilePath();
240
+ let current = null;
241
+ try {
242
+ current = fs.readFileSync(lockfilePath, "utf8");
243
+ }
244
+ catch (error) {
245
+ if (error.code !== "ENOENT")
246
+ throw error;
247
+ }
248
+ if (current === plan.desiredBytes)
249
+ return;
250
+ if (current !== plan.oldBytes) {
251
+ throw new ConfigError(`Refusing migration lockfile publication because ${lockfilePath} is a third generation, neither the journaled old nor desired bytes.`, "INVALID_CONFIG_FILE");
252
+ }
253
+ if (plan.desiredBytes === null)
254
+ return;
255
+ fs.mkdirSync(path.dirname(lockfilePath), { recursive: true });
256
+ writeFileAtomic(lockfilePath, plan.desiredBytes, 0o600);
257
+ }
72
258
  export async function removeLockEntry(id) {
73
259
  if (!fs.existsSync(getDataDir()))
74
260
  return;
75
- const acquired = await acquireLockSentinel();
261
+ const release = await acquireLockSentinel();
76
262
  try {
77
- const entries = readLockfile();
78
- writeLockfile(entries.filter((e) => e.id !== id));
263
+ // R-012: see upsertLockEntry — same destructive-overwrite risk on a
264
+ // corrupt lockfile, so the same strict reader is used here.
265
+ const entries = readLockfileOrThrow();
266
+ writeLockfileUnlocked(entries.filter((e) => e.id !== id));
79
267
  }
80
268
  finally {
81
- if (acquired)
82
- releaseLockSentinel();
269
+ release();
83
270
  }
84
271
  }
85
272
  // ── Helpers ─────────────────────────────────────────────────────────────────
86
- function isValidLockfileEntry(value) {
273
+ export function isValidLockfileEntry(value) {
87
274
  if (typeof value !== "object" || value === null || Array.isArray(value))
88
275
  return false;
89
276
  const obj = value;
@@ -2,24 +2,46 @@
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
4
  import { getHarness, SESSION_LOG_HARNESSES } from "../harnesses/index.js";
5
- import { ClaudeCodeProvider } from "../harnesses/claude/session-log.js";
6
- import { OpenCodeProvider } from "../harnesses/opencode/session-log.js";
7
5
  export { extractInlineRefMentions } from "./inline-refs.js";
8
- const HARNESSES = [new ClaudeCodeProvider(), new OpenCodeProvider()];
9
- // #562: the unified HARNESS_REGISTRY is the single source of truth for which
10
- // harnesses expose session logs. Validate (behaviour-preserving) that every
11
- // session-log provider instantiated above resolves to a registry harness whose
12
- // `sessionLogs` capability is set — and via the id-normalization bridge, so a
13
- // provider named "claude-code" still maps to the canonical "claude" harness.
14
- // This turns a silently-drifting third registry into a startup invariant.
6
+ // #562/P2 (plan §"Kill registry drift"): the provider array is DERIVED from
7
+ // the unified HARNESS_REGISTRY — every harness with `capabilities.sessionLogs`
8
+ // must supply a `sessionLogProvider` factory on its descriptor, and this is
9
+ // the only place providers are instantiated. Adding a session-log harness is
10
+ // therefore one registry entry, never an edit here.
11
+ //
12
+ // Ordered by canonical id so the pre-derivation provider order
13
+ // ([claude-code, opencode] — visible in e.g. `extract --auto` result order)
14
+ // is preserved deterministically, independent of HARNESS_REGISTRY declaration
15
+ // order (which is pinned for JSON-schema enum stability).
16
+ //
17
+ // WI-9.7 (H1): `SESSION_LOG_HARNESSES` is `HARNESS_REGISTRY` narrowed by the
18
+ // `isSessionLogHarness` type-predicate (see `../harnesses/types.ts`), so every
19
+ // element's `capabilities.sessionLogs` is the literal `true` and
20
+ // `sessionLogProvider` is a required (non-optional) field — a harness
21
+ // declaring `sessionLogs: true` without a provider is now a compile error at
22
+ // its `HARNESS_REGISTRY` entry, not a throw here. The load-time throw this
23
+ // used to guard (`h.sessionLogProvider?.()` returning undefined) is gone;
24
+ // `h.sessionLogProvider()` cannot be called with no provider to invoke.
25
+ const HARNESSES = [...SESSION_LOG_HARNESSES]
26
+ .sort((a, b) => a.id.localeCompare(b.id))
27
+ .map((h) => h.sessionLogProvider());
28
+ // Reverse invariant (kept from #562): every derived provider's runtime `name`
29
+ // — a plain string the provider implementation sets independently of the
30
+ // harness registry (e.g. `ClaudeCodeProvider`'s `"claude-code"`) — must
31
+ // resolve back, via the id-normalization bridge, to a registry harness whose
32
+ // `sessionLogs` capability is set. This is NOT expressible as a compile-time
33
+ // type constraint: `SessionLogHarness.name` is a runtime string with no
34
+ // static link to the `AkmHarness` that produced it, so a typo'd or
35
+ // stale `name` (e.g. copy-pasted from another provider, or renamed on one
36
+ // side only) would only ever surface as a wrong/silent lookup miss much later
37
+ // (health scans, `extract --auto` attribution) rather than here at load time.
38
+ // Kept as a runtime guard for that reason.
15
39
  for (const provider of HARNESSES) {
16
40
  const harness = getHarness(provider.name);
17
41
  if (!harness?.capabilities.sessionLogs) {
18
42
  throw new Error(`[akm] session-log provider "${provider.name}" is not registered as a sessionLogs harness in HARNESS_REGISTRY (src/integrations/harnesses). Add it there.`);
19
43
  }
20
44
  }
21
- // Touch the derived list so the dependency is explicit and tree-shake-safe.
22
- void SESSION_LOG_HARNESSES;
23
45
  const ERROR_PATTERNS = /error|failed|exception|cannot|undefined|null pointer|ENOENT|timeout/i;
24
46
  /**
25
47
  * Returns all available session log harnesses for the current machine.
@@ -28,22 +50,6 @@ const ERROR_PATTERNS = /error|failed|exception|cannot|undefined|null pointer|ENO
28
50
  export function getAvailableHarnesses() {
29
51
  return HARNESSES.filter((harness) => harness.isAvailable());
30
52
  }
31
- /**
32
- * Map each available harness to its `{ harnessName, roots }` watch target,
33
- * skipping harnesses that expose no roots (absent `watchRoots()` or an empty
34
- * result). This is the one stable entry point the watcher uses so it never
35
- * reaches into providers directly.
36
- */
37
- export function getWatchTargets() {
38
- const targets = [];
39
- for (const harness of getAvailableHarnesses()) {
40
- const roots = harness.watchRoots?.() ?? [];
41
- if (roots.length === 0)
42
- continue;
43
- targets.push({ harnessName: harness.name, roots });
44
- }
45
- return targets;
46
- }
47
53
  export function normalizeSessionTopic(text) {
48
54
  const normalized = text.replace(/\s+/g, " ").trim().toLowerCase();
49
55
  if (normalized.length < 10)
@@ -85,16 +91,13 @@ export function aggregateSessionEvents(events) {
85
91
  * Collect normalized session events from a set of harnesses for the health
86
92
  * candidate scan (#568).
87
93
  *
88
- * Pipeline selection per harness (capability-gated):
89
- * - readSession-capable harness (`supportsReadSession !== false`): drive the
90
- * richer `listSessions()` + `readSession()` pipeline. `readSession` flattens
94
+ * Pipeline selection per harness:
95
+ * - Drive the richer `listSessions()` + `readSession()` pipeline. `readSession` flattens
91
96
  * structured content — tool calls, assistant content blocks, thinking,
92
97
  * tool_result — into event text (e.g. ClaudeCodeProvider's `parseClaudeEvent`
93
98
  * surfaces `[tool:*]` / `[tool_result]` blocks that the legacy flat
94
99
  * `readEvents` scan drops entirely). This is what lets health advisories see
95
100
  * repeated tool failures / long runs that the flat scan hid.
96
- * - legacy-only harness (`supportsReadSession === false`): fall back to the
97
- * legacy flat `readEvents()` scan (behaviour-preserving).
98
101
  *
99
102
  * Extracted as a pure function (harnesses injected) so it is unit-testable
100
103
  * without touching the real on-disk session-log locations.
@@ -107,19 +110,13 @@ export function aggregateSessionEvents(events) {
107
110
  * health command (`akm health`, which calls this synchronously) blow past its
108
111
  * latency budget. `listSessions()` returns summaries sorted newest-first, so
109
112
  * capping to the most-recent N sessions per harness keeps the richer signal
110
- * for what actually matters (recent activity) while bounding cost. The legacy
111
- * flat-scan path is naturally cheaper and is left uncapped.
113
+ * for what actually matters (recent activity) while bounding cost.
112
114
  */
113
115
  const DEFAULT_MAX_SESSIONS_PER_HARNESS = 50;
114
116
  export function collectSessionEvents(harnesses, sinceMs, maxSessionsPerHarness = DEFAULT_MAX_SESSIONS_PER_HARNESS) {
115
117
  const events = [];
116
118
  for (const harness of harnesses) {
117
119
  try {
118
- if (harness.supportsReadSession === false) {
119
- // Legacy-only harness: only the flat event scan is available.
120
- events.push(...harness.readEvents({ sinceMs }));
121
- continue;
122
- }
123
120
  // Rich path: enumerate sessions cheaply, then read each one's full
124
121
  // structured event stream. Falls back to readEvents if listSessions
125
122
  // surfaces nothing (e.g. a harness that wired readSession but whose
@@ -0,0 +1,113 @@
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 skeleton for {@link SessionLogHarness} providers (§4.6 dedup).
6
+ *
7
+ * Holds only what the concrete providers genuinely share: safe stat'ing,
8
+ * recursive directory walking, the mtime-filtered file→summary listing loop,
9
+ * the flat JSONL/log line→event scan, and conditional-spread assembly of
10
+ * {@link SessionSummary} refs stamped with the provider's runtime name.
11
+ * Everything platform-specific — file layouts, metadata peeking, SQLite
12
+ * stores, message flattening — stays in the subclasses.
13
+ */
14
+ import fs from "node:fs";
15
+ import path from "node:path";
16
+ export class AbstractSessionLogProvider {
17
+ isAvailable() {
18
+ return fs.existsSync(this.availabilityRoot());
19
+ }
20
+ /** `fs.statSync` that returns `undefined` instead of throwing. */
21
+ statSafe(target) {
22
+ try {
23
+ return fs.statSync(target);
24
+ }
25
+ catch {
26
+ return undefined;
27
+ }
28
+ }
29
+ /** Recursively yield files under `dir` whose basename passes `matches`. */
30
+ *walkFiles(dir, matches) {
31
+ try {
32
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
33
+ const full = path.join(dir, entry.name);
34
+ if (entry.isDirectory())
35
+ yield* this.walkFiles(full, matches);
36
+ else if (matches(entry.name))
37
+ yield full;
38
+ }
39
+ }
40
+ catch {
41
+ // permission errors etc.
42
+ }
43
+ }
44
+ /**
45
+ * Assemble a {@link SessionSummary} stamped with this provider's name.
46
+ * Timestamps are included whenever defined (0 is valid); `projectHint` /
47
+ * `title` only when non-empty, so absent metadata stays absent.
48
+ */
49
+ sessionRef(input) {
50
+ return {
51
+ harness: this.name,
52
+ sessionId: input.sessionId,
53
+ filePath: input.filePath,
54
+ ...(input.startedAt !== undefined ? { startedAt: input.startedAt } : {}),
55
+ ...(input.endedAt !== undefined ? { endedAt: input.endedAt } : {}),
56
+ ...(input.projectHint ? { projectHint: input.projectHint } : {}),
57
+ ...(input.title ? { title: input.title } : {}),
58
+ };
59
+ }
60
+ /**
61
+ * The shared listing loop: enumerate candidate session files, drop the
62
+ * unstat'able and the ones older than `sinceMs`, summarize the rest, and
63
+ * sort newest-ended first. An enumeration failure (root missing or
64
+ * unreadable) returns what was collected so far rather than throwing.
65
+ */
66
+ listSessionsFromFiles(input) {
67
+ const summaries = [];
68
+ try {
69
+ for (const filePath of input.enumerate()) {
70
+ const stat = this.statSafe(filePath);
71
+ if (!stat)
72
+ continue;
73
+ if (stat.mtimeMs < input.sinceMs)
74
+ continue;
75
+ const summary = input.summarize(filePath, stat);
76
+ if (summary)
77
+ summaries.push(summary);
78
+ }
79
+ }
80
+ catch {
81
+ // Root missing or unreadable — return what we have.
82
+ }
83
+ return summaries.sort((a, b) => (b.endedAt ?? 0) - (a.endedAt ?? 0));
84
+ }
85
+ /**
86
+ * The legacy flat line scan shared by both providers' `readEvents`: parse
87
+ * each line as JSON, pull text/session-id through the per-provider
88
+ * selectors, skip entries whose text is missing or under 10 chars, and
89
+ * fall back to the file mtime when the entry carries no numeric timestamp.
90
+ */
91
+ *logLineEvents(input) {
92
+ for (const line of input.lines) {
93
+ try {
94
+ const entry = JSON.parse(line);
95
+ const text = input.selectText(entry);
96
+ if (typeof text !== "string" || text.length < 10)
97
+ continue;
98
+ const sessionId = input.selectSessionId(entry);
99
+ yield {
100
+ harness: this.name,
101
+ text,
102
+ ts: typeof entry?.timestamp === "number" ? entry.timestamp : input.fallbackTsMs,
103
+ sessionId: typeof sessionId === "string" ? sessionId : undefined,
104
+ role: typeof entry?.role === "string" ? entry.role : "unknown",
105
+ filePath: input.filePath,
106
+ };
107
+ }
108
+ catch {
109
+ // skip malformed lines
110
+ }
111
+ }
112
+ }
113
+ }