akm-cli 0.9.0-beta.9 → 0.9.0-rc.1

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 (381) hide show
  1. package/CHANGELOG.md +715 -0
  2. package/README.md +12 -4
  3. package/dist/akm +38 -0
  4. package/dist/akm-migrate-storage +38 -0
  5. package/dist/assets/help/help-improve.md +9 -6
  6. package/dist/assets/hints/cli-hints-full.md +6 -5
  7. package/dist/assets/profiles/default.json +9 -4
  8. package/dist/assets/profiles/frequent.json +1 -1
  9. package/dist/assets/profiles/memory-focus.json +1 -1
  10. package/dist/assets/profiles/proactive-maintenance.json +25 -0
  11. package/dist/assets/profiles/quick.json +1 -1
  12. package/dist/assets/profiles/recombine-only.json +21 -0
  13. package/dist/assets/profiles/reflect-distill.json +30 -0
  14. package/dist/assets/profiles/synthesize.json +15 -0
  15. package/dist/assets/profiles/thorough.json +1 -1
  16. package/dist/assets/prompts/consolidate-system.md +23 -0
  17. package/dist/assets/prompts/contradiction-judge.md +33 -0
  18. package/dist/assets/prompts/distill-knowledge-system.md +22 -0
  19. package/dist/assets/prompts/distill-lesson-system.md +36 -0
  20. package/dist/assets/prompts/extract-session.md +11 -3
  21. package/dist/assets/prompts/graph-extract-system.md +1 -0
  22. package/dist/assets/prompts/graph-extract-user-prompt.md +1 -1
  23. package/dist/assets/prompts/memory-infer-system.md +1 -0
  24. package/dist/assets/prompts/memory-infer-user.md +5 -0
  25. package/dist/assets/prompts/metadata-enhance-system.md +1 -0
  26. package/dist/assets/prompts/procedural-system.md +44 -0
  27. package/dist/assets/prompts/recombine-system.md +40 -0
  28. package/dist/assets/prompts/staleness-detect-system.md +6 -0
  29. package/dist/assets/prompts/validate-summary-judge.md +1 -0
  30. package/dist/assets/prompts/workflow-unit-preamble.md +26 -0
  31. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +38 -0
  32. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +38 -0
  33. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +39 -0
  34. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +40 -0
  35. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +43 -0
  36. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +38 -0
  37. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +43 -0
  38. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +40 -0
  39. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +43 -0
  40. package/dist/assets/templates/html/health.html +281 -111
  41. package/dist/assets/wiki/ingest-workflow-template.md +45 -16
  42. package/dist/assets/wiki/schema-template.md +4 -4
  43. package/dist/cli/clack.js +56 -0
  44. package/dist/cli/config-migrate.js +7 -1
  45. package/dist/cli/confirm.js +1 -1
  46. package/dist/cli/parse-args.js +46 -1
  47. package/dist/cli/shared.js +28 -0
  48. package/dist/cli.js +25 -21
  49. package/dist/commands/agent/agent-dispatch.js +3 -2
  50. package/dist/commands/agent/agent-support.js +0 -7
  51. package/dist/commands/agent/contribute-cli.js +26 -7
  52. package/dist/commands/config-cli.js +26 -13
  53. package/dist/commands/env/child-env.js +47 -0
  54. package/dist/commands/env/env-binding.js +95 -0
  55. package/dist/commands/env/env-cli.js +228 -292
  56. package/dist/commands/env/env.js +14 -67
  57. package/dist/commands/env/secret-cli.js +140 -138
  58. package/dist/commands/feedback-cli.js +156 -155
  59. package/dist/commands/graph/graph-cli.js +5 -13
  60. package/dist/commands/graph/graph.js +3 -3
  61. package/dist/commands/health/advisories.js +151 -0
  62. package/dist/commands/health/checks.js +103 -16
  63. package/dist/commands/health/html-report.js +447 -81
  64. package/dist/commands/health/improve-metrics.js +771 -0
  65. package/dist/commands/health/llm-usage.js +65 -0
  66. package/dist/commands/health/md-report.js +103 -0
  67. package/dist/commands/health/metrics.js +278 -0
  68. package/dist/commands/health/stash-exposure.js +46 -0
  69. package/dist/commands/health/surfaces.js +216 -0
  70. package/dist/commands/health/task-runs.js +135 -0
  71. package/dist/commands/health/types.js +26 -0
  72. package/dist/commands/health/windows.js +195 -0
  73. package/dist/commands/health.js +91 -1091
  74. package/dist/commands/improve/anti-collapse.js +170 -0
  75. package/dist/commands/improve/calibration.js +161 -0
  76. package/dist/commands/improve/collapse-detector.js +421 -0
  77. package/dist/commands/improve/consolidate/chunking.js +141 -0
  78. package/dist/commands/improve/consolidate/eligibility.js +64 -0
  79. package/dist/commands/improve/consolidate/merge.js +145 -0
  80. package/dist/commands/improve/consolidate/sanitize.js +231 -0
  81. package/dist/commands/{lint.js → improve/consolidate/types.js} +1 -1
  82. package/dist/commands/improve/consolidate.js +1295 -1277
  83. package/dist/commands/improve/dedup.js +482 -0
  84. package/dist/commands/improve/distill/content-repair.js +202 -0
  85. package/dist/commands/improve/distill/promote-memory.js +229 -0
  86. package/dist/commands/improve/distill/quality-gate.js +236 -0
  87. package/dist/commands/improve/distill-guards.js +127 -0
  88. package/dist/commands/improve/distill-promotion-policy.js +826 -167
  89. package/dist/commands/improve/distill.js +228 -605
  90. package/dist/commands/improve/eligibility.js +434 -0
  91. package/dist/commands/improve/encoding-salience.js +205 -0
  92. package/dist/commands/improve/extract-cli.js +179 -59
  93. package/dist/commands/improve/extract-prompt.js +54 -3
  94. package/dist/commands/improve/extract-watch.js +140 -0
  95. package/dist/commands/improve/extract.js +409 -43
  96. package/dist/commands/improve/feedback-valence.js +54 -0
  97. package/dist/commands/improve/hot-probation.js +45 -0
  98. package/dist/commands/improve/improve-auto-accept.js +157 -10
  99. package/dist/commands/improve/improve-cli.js +115 -73
  100. package/dist/commands/improve/improve-profiles.js +28 -8
  101. package/dist/commands/improve/improve-result-file.js +15 -25
  102. package/dist/commands/improve/improve-session.js +58 -0
  103. package/dist/commands/improve/improve.js +485 -2764
  104. package/dist/commands/improve/locks.js +154 -0
  105. package/dist/commands/improve/loop-stages.js +1100 -0
  106. package/dist/commands/improve/memory/memory-belief.js +14 -15
  107. package/dist/commands/improve/memory/memory-contradiction-detect.js +83 -60
  108. package/dist/commands/improve/memory/memory-improve.js +27 -27
  109. package/dist/commands/improve/outcome-loop.js +270 -0
  110. package/dist/commands/improve/preparation.js +2002 -0
  111. package/dist/commands/improve/proactive-maintenance.js +37 -35
  112. package/dist/commands/improve/procedural.js +398 -0
  113. package/dist/commands/improve/recombine.js +818 -0
  114. package/dist/commands/improve/reflect-noise.js +0 -0
  115. package/dist/commands/improve/reflect.js +206 -45
  116. package/dist/commands/improve/salience.js +455 -0
  117. package/dist/commands/improve/schema-similarity-gate.js +168 -0
  118. package/dist/commands/improve/shared.js +51 -0
  119. package/dist/commands/improve/triage.js +93 -0
  120. package/dist/commands/lint/agent-linter.js +19 -24
  121. package/dist/commands/lint/base-linter.js +173 -60
  122. package/dist/commands/lint/command-linter.js +19 -24
  123. package/dist/commands/lint/env-key-rules.js +38 -1
  124. package/dist/commands/lint/fact-linter.js +39 -0
  125. package/dist/commands/lint/index.js +31 -13
  126. package/dist/commands/lint/memory-linter.js +1 -1
  127. package/dist/commands/lint/registry.js +7 -2
  128. package/dist/commands/lint/task-linter.js +3 -3
  129. package/dist/commands/lint/workflow-linter.js +26 -1
  130. package/dist/commands/observability-cli.js +4 -4
  131. package/dist/commands/proposal/drain-policies.js +13 -4
  132. package/dist/commands/proposal/drain.js +45 -51
  133. package/dist/commands/proposal/legacy-import.js +115 -0
  134. package/dist/commands/proposal/proposal-cli.js +24 -34
  135. package/dist/commands/proposal/proposal.js +2 -1
  136. package/dist/commands/proposal/propose.js +8 -3
  137. package/dist/commands/proposal/repository.js +829 -0
  138. package/dist/commands/proposal/validators/proposal-quality-validators.js +9 -8
  139. package/dist/commands/proposal/validators/proposals.js +93 -895
  140. package/dist/commands/read/curate.js +410 -111
  141. package/dist/commands/read/knowledge.js +10 -3
  142. package/dist/commands/read/remember-cli.js +133 -138
  143. package/dist/commands/read/search-cli.js +15 -8
  144. package/dist/commands/read/search.js +22 -11
  145. package/dist/commands/read/show.js +106 -14
  146. package/dist/commands/registry-cli.js +76 -87
  147. package/dist/commands/remember.js +11 -12
  148. package/dist/commands/sources/add-cli.js +91 -95
  149. package/dist/commands/sources/history.js +1 -1
  150. package/dist/commands/sources/init.js +66 -18
  151. package/dist/commands/sources/installed-stashes.js +11 -3
  152. package/dist/commands/sources/migration-help.js +7 -4
  153. package/dist/commands/sources/schema-repair.js +44 -46
  154. package/dist/commands/sources/self-update.js +2 -2
  155. package/dist/commands/sources/source-add.js +7 -3
  156. package/dist/commands/sources/sources-cli.js +3 -3
  157. package/dist/commands/sources/stash-cli.js +19 -39
  158. package/dist/commands/sources/stash-skeleton.js +57 -8
  159. package/dist/commands/tasks/default-tasks.js +15 -2
  160. package/dist/commands/tasks/tasks-cli.js +20 -29
  161. package/dist/commands/tasks/tasks.js +39 -11
  162. package/dist/commands/wiki-cli.js +23 -38
  163. package/dist/commands/workflow-cli.js +291 -13
  164. package/dist/core/asset/asset-registry.js +3 -1
  165. package/dist/core/asset/asset-spec.js +79 -5
  166. package/dist/core/asset/frontmatter.js +188 -167
  167. package/dist/core/asset/markdown.js +8 -0
  168. package/dist/core/authoring-rules.js +92 -0
  169. package/dist/core/common.js +4 -23
  170. package/dist/core/concurrent.js +10 -1
  171. package/dist/core/config/config-io.js +10 -1
  172. package/dist/core/config/config-migration.js +18 -40
  173. package/dist/core/config/config-schema.js +403 -62
  174. package/dist/core/config/config-types.js +3 -3
  175. package/dist/core/config/config.js +67 -22
  176. package/dist/core/deep-merge.js +38 -0
  177. package/dist/core/errors.js +1 -0
  178. package/dist/core/eval/rank-metrics.js +113 -0
  179. package/dist/core/events.js +4 -7
  180. package/dist/core/improve-types.js +47 -8
  181. package/dist/core/json-schema.js +142 -0
  182. package/dist/core/logs-db.js +14 -75
  183. package/dist/core/parse.js +36 -16
  184. package/dist/core/paths.js +18 -18
  185. package/dist/core/standards/resolve-standards-context.js +87 -0
  186. package/dist/core/standards/resolve-stash-standards.js +99 -0
  187. package/dist/core/standards/resolve-type-conventions.js +66 -0
  188. package/dist/core/state/migrations.js +770 -0
  189. package/dist/core/state-db.js +132 -1126
  190. package/dist/core/structured.js +69 -0
  191. package/dist/core/time.js +53 -0
  192. package/dist/core/warn.js +21 -0
  193. package/dist/core/write-source.js +37 -0
  194. package/dist/indexer/db/db.js +261 -770
  195. package/dist/indexer/db/entry-mapper.js +41 -0
  196. package/dist/indexer/db/graph-db.js +129 -86
  197. package/dist/indexer/db/llm-cache.js +2 -2
  198. package/dist/indexer/db/schema.js +516 -0
  199. package/dist/indexer/ensure-index.js +36 -92
  200. package/dist/indexer/feedback/utility-policy.js +75 -0
  201. package/dist/indexer/graph/graph-boost.js +51 -41
  202. package/dist/indexer/graph/graph-extraction.js +207 -4
  203. package/dist/indexer/index-writer-lock.js +18 -11
  204. package/dist/indexer/index-written-assets.js +105 -0
  205. package/dist/indexer/indexer.js +182 -204
  206. package/dist/indexer/passes/dir-staleness.js +114 -0
  207. package/dist/indexer/passes/memory-inference.js +13 -5
  208. package/dist/indexer/passes/metadata.js +20 -0
  209. package/dist/indexer/read-preflight.js +23 -0
  210. package/dist/indexer/search/db-search.js +89 -13
  211. package/dist/indexer/search/fts-query.js +51 -0
  212. package/dist/indexer/search/ranking-contributors.js +95 -9
  213. package/dist/indexer/search/ranking.js +79 -3
  214. package/dist/indexer/search/search-fields.js +6 -0
  215. package/dist/indexer/search/search-source.js +32 -21
  216. package/dist/indexer/search/semantic-status.js +4 -0
  217. package/dist/indexer/walk/matchers.js +48 -0
  218. package/dist/indexer/walk/walker.js +21 -13
  219. package/dist/integrations/agent/builders.js +41 -13
  220. package/dist/integrations/agent/config.js +20 -59
  221. package/dist/integrations/agent/detect.js +9 -0
  222. package/dist/integrations/agent/index.js +3 -19
  223. package/dist/integrations/agent/model-aliases.js +16 -2
  224. package/dist/integrations/agent/profiles.js +79 -6
  225. package/dist/integrations/agent/prompts.js +75 -9
  226. package/dist/integrations/agent/runner-dispatch.js +83 -0
  227. package/dist/integrations/agent/runner.js +13 -9
  228. package/dist/integrations/agent/spawn.js +206 -81
  229. package/dist/integrations/harnesses/aider/agent-builder.js +113 -0
  230. package/dist/integrations/harnesses/aider/index.js +58 -0
  231. package/dist/integrations/harnesses/aider/result-extractor.js +53 -0
  232. package/dist/integrations/harnesses/amazonq/agent-builder.js +153 -0
  233. package/dist/integrations/harnesses/amazonq/index.js +59 -0
  234. package/dist/integrations/harnesses/amazonq/result-extractor.js +48 -0
  235. package/dist/integrations/harnesses/claude/agent-builder.js +46 -7
  236. package/dist/integrations/harnesses/claude/index.js +27 -23
  237. package/dist/integrations/harnesses/claude/result-extractor.js +52 -0
  238. package/dist/integrations/harnesses/claude/session-log.js +10 -0
  239. package/dist/integrations/harnesses/codex/agent-builder.js +137 -0
  240. package/dist/integrations/harnesses/codex/index.js +63 -0
  241. package/dist/integrations/harnesses/codex/result-extractor.js +73 -0
  242. package/dist/integrations/harnesses/copilot/agent-builder.js +122 -0
  243. package/dist/integrations/harnesses/copilot/index.js +60 -0
  244. package/dist/integrations/harnesses/copilot/result-extractor.js +151 -0
  245. package/dist/integrations/harnesses/gemini/agent-builder.js +121 -0
  246. package/dist/integrations/harnesses/gemini/index.js +60 -0
  247. package/dist/integrations/harnesses/gemini/result-extractor.js +121 -0
  248. package/dist/integrations/harnesses/index.js +28 -7
  249. package/dist/integrations/harnesses/opencode/agent-builder.js +1 -1
  250. package/dist/integrations/harnesses/opencode/index.js +17 -16
  251. package/dist/integrations/harnesses/opencode/session-log.js +173 -3
  252. package/dist/integrations/harnesses/opencode-sdk/harness.js +65 -0
  253. package/dist/integrations/harnesses/opencode-sdk/index.js +10 -34
  254. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +642 -71
  255. package/dist/integrations/harnesses/openhands/agent-builder.js +126 -0
  256. package/dist/integrations/harnesses/openhands/index.js +58 -0
  257. package/dist/integrations/harnesses/openhands/result-extractor.js +103 -0
  258. package/dist/integrations/harnesses/pi/agent-builder.js +104 -0
  259. package/dist/integrations/harnesses/pi/index.js +58 -0
  260. package/dist/integrations/harnesses/pi/result-extractor.js +135 -0
  261. package/dist/integrations/harnesses/types.js +8 -0
  262. package/dist/integrations/session-logs/index.js +40 -11
  263. package/dist/llm/call-ai.js +2 -2
  264. package/dist/llm/client.js +34 -11
  265. package/dist/llm/embedder.js +67 -4
  266. package/dist/llm/embedders/cache.js +3 -1
  267. package/dist/llm/embedders/deterministic.js +66 -0
  268. package/dist/llm/embedders/local.js +73 -3
  269. package/dist/llm/feature-gate.js +16 -15
  270. package/dist/llm/graph-extract.js +67 -44
  271. package/dist/llm/memory-infer-impl.js +138 -0
  272. package/dist/llm/memory-infer.js +1 -127
  273. package/dist/llm/metadata-enhance.js +44 -31
  274. package/dist/llm/structured-call.js +49 -0
  275. package/dist/migrate-storage-node.mjs +8 -0
  276. package/dist/output/context.js +5 -5
  277. package/dist/output/renderers.js +87 -15
  278. package/dist/output/shapes/curate.js +14 -2
  279. package/dist/output/shapes/helpers.js +0 -3
  280. package/dist/output/shapes/passthrough.js +6 -1
  281. package/dist/output/text/helpers.js +241 -2
  282. package/dist/output/text/workflow.js +4 -1
  283. package/dist/registry/providers/skills-sh.js +21 -147
  284. package/dist/registry/providers/static-index.js +15 -157
  285. package/dist/registry/resolve.js +27 -9
  286. package/dist/runtime.js +25 -1
  287. package/dist/schemas/akm-config.json +14225 -0
  288. package/dist/schemas/akm-workflow.json +328 -0
  289. package/dist/scripts/migrate-storage.js +2743 -8390
  290. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +1652 -607
  291. package/dist/setup/detect.js +9 -0
  292. package/dist/setup/legacy-config.js +106 -0
  293. package/dist/setup/prompt.js +57 -0
  294. package/dist/setup/providers.js +14 -0
  295. package/dist/setup/registry-stash-loader.js +12 -0
  296. package/dist/setup/semantic-assets.js +124 -0
  297. package/dist/setup/setup.js +52 -1614
  298. package/dist/setup/steps/connection.js +734 -0
  299. package/dist/setup/steps/output.js +31 -0
  300. package/dist/setup/steps/platforms.js +124 -0
  301. package/dist/setup/steps/semantic.js +27 -0
  302. package/dist/setup/steps/sources.js +222 -0
  303. package/dist/setup/steps/stashdir.js +42 -0
  304. package/dist/setup/steps/tasks.js +152 -0
  305. package/dist/sources/include.js +6 -2
  306. package/dist/sources/providers/filesystem.js +0 -1
  307. package/dist/sources/providers/git-install.js +210 -0
  308. package/dist/sources/providers/git-provider.js +234 -0
  309. package/dist/sources/providers/git-stash.js +248 -0
  310. package/dist/sources/providers/git.js +10 -661
  311. package/dist/sources/providers/npm.js +2 -6
  312. package/dist/sources/providers/provider-utils.js +13 -7
  313. package/dist/sources/providers/sync-from-ref.js +9 -1
  314. package/dist/sources/providers/website.js +9 -5
  315. package/dist/sources/website-ingest.js +187 -29
  316. package/dist/sources/wiki-fetchers/registry.js +53 -0
  317. package/dist/sources/wiki-fetchers/youtube.js +239 -0
  318. package/dist/storage/database.js +45 -10
  319. package/dist/storage/managed-db.js +82 -0
  320. package/dist/storage/repositories/canaries-repository.js +107 -0
  321. package/dist/storage/repositories/consolidation-repository.js +38 -0
  322. package/dist/storage/repositories/embeddings-repository.js +72 -0
  323. package/dist/storage/repositories/events-repository.js +187 -0
  324. package/dist/storage/repositories/extract-sessions-repository.js +96 -0
  325. package/dist/storage/repositories/improve-runs-repository.js +146 -0
  326. package/dist/storage/repositories/index-db.js +14 -8
  327. package/dist/storage/repositories/proposals-repository.js +220 -0
  328. package/dist/storage/repositories/recombine-repository.js +213 -0
  329. package/dist/storage/repositories/registry-cache.js +93 -0
  330. package/dist/storage/repositories/registry-index-cache-repository.js +46 -0
  331. package/dist/storage/repositories/task-history-repository.js +93 -0
  332. package/dist/storage/repositories/workflow-runs-repository.js +189 -1
  333. package/dist/storage/sqlite-pragmas.js +146 -0
  334. package/dist/tasks/backends/cron.js +1 -1
  335. package/dist/tasks/backends/index.js +9 -0
  336. package/dist/tasks/backends/launchd.js +1 -1
  337. package/dist/tasks/backends/schtasks.js +1 -1
  338. package/dist/tasks/{resolveAkmBin.js → resolve-akm-bin.js} +2 -2
  339. package/dist/tasks/runner.js +15 -13
  340. package/dist/text-import-hook.mjs +1 -1
  341. package/dist/wiki/wiki.js +52 -11
  342. package/dist/workflows/authoring/authoring.js +123 -10
  343. package/dist/workflows/authoring/workflow-program-template.yaml +31 -0
  344. package/dist/workflows/cli.js +5 -0
  345. package/dist/workflows/db.js +138 -4
  346. package/dist/workflows/exec/brief.js +484 -0
  347. package/dist/workflows/exec/native-executor.js +975 -0
  348. package/dist/workflows/exec/param-secrets.js +115 -0
  349. package/dist/workflows/exec/report.js +1295 -0
  350. package/dist/workflows/exec/run-workflow.js +596 -0
  351. package/dist/workflows/exec/scheduler.js +100 -0
  352. package/dist/workflows/exec/step-work.js +1156 -0
  353. package/dist/workflows/exec/unit-writer.js +23 -0
  354. package/dist/workflows/exec/watch.js +116 -0
  355. package/dist/workflows/exec/worktree.js +171 -0
  356. package/dist/workflows/ir/compile.js +388 -0
  357. package/dist/workflows/ir/params.js +54 -0
  358. package/dist/workflows/ir/plan-hash.js +33 -0
  359. package/dist/workflows/ir/schema.js +4 -0
  360. package/dist/workflows/parser.js +3 -1
  361. package/dist/workflows/program/expressions.js +369 -0
  362. package/dist/workflows/program/parser.js +760 -0
  363. package/dist/workflows/program/project.js +105 -0
  364. package/dist/workflows/program/schema.js +54 -0
  365. package/dist/workflows/renderer.js +82 -5
  366. package/dist/workflows/runtime/agent-identity.js +59 -14
  367. package/dist/workflows/runtime/runs.js +248 -153
  368. package/dist/workflows/runtime/unit-checkin.js +45 -0
  369. package/dist/workflows/runtime/workflow-asset-loader.js +188 -0
  370. package/dist/workflows/validate-summary.js +26 -10
  371. package/dist/workflows/validator.js +1 -1
  372. package/docs/README.md +69 -18
  373. package/docs/data-and-telemetry.md +7 -5
  374. package/docs/migration/release-notes/0.7.0.md +1 -1
  375. package/docs/migration/release-notes/0.9.0-beta.60.md +19 -0
  376. package/docs/migration/release-notes/0.9.0.md +39 -0
  377. package/package.json +10 -10
  378. package/dist/assets/tasks/core/update-stashes.yml +0 -4
  379. package/dist/commands/db-cli.js +0 -23
  380. package/dist/indexer/db/db-backup.js +0 -376
  381. package/dist/indexer/passes/staleness-detect.js +0 -488
@@ -2,23 +2,29 @@
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
  /**
5
- * Auto-index: silently run an incremental `akm index` when the local index
6
- * is stale or absent, so that `search`, `show`, and `feedback` always operate
7
- * against current on-disk state without requiring the user to manually run
8
- * `akm index` first.
5
+ * Auto-index bootstrap: silently build the local index inline when it cannot
6
+ * serve the caller's stash at all (missing DB, no `entries` table, zero rows,
7
+ * or built for a different stash), so `search`, `show`, and `feedback` work
8
+ * on first use without a manual `akm index`.
9
9
  *
10
- * This replaces the old filesystem fallbacks that were scattered across
11
- * `searchLocal()` and `show.ts`, centralizing the "indexed yet?" gap handling
12
- * behind a single entry point.
10
+ * Content FRESHNESS is intentionally not this module's job on the read path.
11
+ * Writers maintain the index (`indexWrittenAssets` for `remember`/extract
12
+ * session assets; the mutation commands run `akmIndex()` themselves), and the
13
+ * improve cron / explicit `akm index` do full refreshes. Reads serve whatever
14
+ * populated index exists. The previous design — a staleness walk plus a
15
+ * detached background reindex per read — made every read on an actively
16
+ * written stash spawn a writer that the read's own telemetry then queued
17
+ * behind (see docs/design/read-path-reindex-contention-findings.md).
18
+ *
19
+ * `mode: "blocking"` (improve) still checks staleness and rebuilds inline,
20
+ * because its planning logic needs a current `entries` table in-process.
13
21
  */
14
- import { spawn } from "node:child_process";
15
22
  import fs from "node:fs";
16
23
  import path from "node:path";
17
24
  import { ASSET_SPECS, TYPE_DIRS } from "../core/asset/asset-spec.js";
18
- import { getDataDir, getDbPath } from "../core/paths.js";
25
+ import { getDbPath } from "../core/paths.js";
19
26
  import { warn } from "../core/warn.js";
20
27
  import { closeDatabase, getEntryCount, getIndexedFilePaths, getMeta, openExistingDatabase } from "./db/db.js";
21
- import { acquireIndexWriterLease, handoffIndexWriterLeaseToPid } from "./index-writer-lock.js";
22
28
  function getIndexableFiles(root, spec) {
23
29
  if (!fs.existsSync(root))
24
30
  return [];
@@ -138,12 +144,9 @@ export function isIndexStale(stashDir) {
138
144
  * i.e. the DB file exists, the `entries` table holds rows, and those rows were
139
145
  * built for this stash (it is the stored primary stash or appears in the
140
146
  * stored `stashDirs` set). When this is true the index is at worst
141
- * content-stale, so the `#607` background-reindex optimization is safe: the
142
- * caller gets slightly-stale-but-relevant results immediately. When it is
143
- * false the existing index has nothing relevant to return (no DB, no `entries`
144
- * table, zero rows, or built for a different stash), so a background reindex
145
- * would leave the caller empty until the next read — those cases must rebuild
146
- * inline.
147
+ * content-stale, so read paths serve it as-is. When it is false the existing
148
+ * index has nothing relevant to return (no DB, no `entries` table, zero rows,
149
+ * or built for a different stash), so those cases must rebuild inline.
147
150
  */
148
151
  function indexCanServeStash(stashDir) {
149
152
  const dbPath = getDbPath();
@@ -174,43 +177,6 @@ function indexCanServeStash(stashDir) {
174
177
  closeDatabase(db);
175
178
  }
176
179
  }
177
- /**
178
- * Spawn a background `akm index` process. Non-blocking — returns immediately.
179
- * Background callers share the same global index-writer lease as foreground
180
- * writers, so stale-read-triggered auto-index attempts coalesce safely.
181
- */
182
- async function spawnBackgroundReindex(_stashDir) {
183
- const dataDir = getDataDir();
184
- const logFile = path.join(dataDir, "logs", "index-background.log");
185
- fs.mkdirSync(path.dirname(logFile), { recursive: true });
186
- const lease = await acquireIndexWriterLease({ mode: "try", purpose: "background-reindex-spawn" });
187
- if (!lease)
188
- return;
189
- const akmBin = process.argv[0];
190
- const akmScript = process.argv[1];
191
- try {
192
- const child = spawn(akmBin, [akmScript, "index", "--background"], {
193
- detached: true,
194
- stdio: ["ignore", fs.openSync(logFile, "a"), fs.openSync(logFile, "a")],
195
- env: { ...process.env },
196
- });
197
- if (!child.pid) {
198
- lease.release();
199
- return;
200
- }
201
- handoffIndexWriterLeaseToPid(lease, child.pid, "background-reindex");
202
- try {
203
- child.unref();
204
- }
205
- catch {
206
- // ignore
207
- }
208
- }
209
- catch (error) {
210
- lease.release();
211
- throw error;
212
- }
213
- }
214
180
  async function runInlineReindex(stashDir) {
215
181
  try {
216
182
  const { akmIndex } = await import("./indexer.js");
@@ -219,53 +185,31 @@ async function runInlineReindex(stashDir) {
219
185
  }
220
186
  catch (error) {
221
187
  warn("Auto-index failed, proceeding with existing index:", error instanceof Error ? error.message : String(error));
222
- return true;
188
+ return false;
223
189
  }
224
190
  }
225
191
  /**
226
- * Ensure the local index exists and is fresh enough for the caller's needs.
192
+ * Ensure the local index exists and can serve the caller.
227
193
  *
228
- * Default mode is `background`, which preserves the low-latency behavior used
229
- * by read paths (`search`, `show`, `feedback`): when a populated index is
230
- * merely stale, spawn a detached reindex and proceed against the existing
231
- * index. When the index is entirely absent (no DB / no `entries` table / zero
232
- * rows) the rebuild runs inline regardless of mode, since there is nothing to
233
- * proceed against.
194
+ * Default mode is `background` the read-path contract (`search`, `show`,
195
+ * `feedback`): a populated index built for this stash is served as-is (its
196
+ * freshness is the writers' job, see module doc); an unusable index rebuilds
197
+ * inline, since there is nothing to proceed against.
234
198
  *
235
- * `mode: "blocking"` waits for the rebuild to finish before returning. Use
236
- * this for callers like `improve` whose planning logic depends on a populated
237
- * `entries` table in the same process.
199
+ * `mode: "blocking"` additionally treats content-staleness as a rebuild
200
+ * trigger and waits for it. Use this for callers like `improve` whose
201
+ * planning logic depends on a current `entries` table in the same process.
238
202
  *
239
- * Returns `true` if an index run was attempted.
203
+ * Returns `true` only when an inline index run succeeds.
204
+ * A rebuild attempt that fails (throws) resolves to `false`.
240
205
  */
241
206
  export async function ensureIndex(stashDir, options = {}) {
242
- if (!isIndexStale(stashDir))
243
- return false;
244
- // Blocking when explicitly requested, or whenever the existing index cannot
245
- // serve this stash (absent DB, no `entries` table, zero rows, or built for a
246
- // different stash): a background reindex returns immediately and would leave
247
- // a first-time caller (search, curate, wiki, show, feedback) with empty
248
- // results. Building inline is a one-off cost; a populated index for this
249
- // stash that is merely content-stale still refreshes in the background.
250
- if (options.mode === "blocking" || !indexCanServeStash(stashDir)) {
251
- return runInlineReindex(stashDir);
252
- }
253
- // The background path re-invokes the akm CLI as a detached child via
254
- // `process.argv[1]`. That is only the akm entrypoint when THIS process is the
255
- // akm CLI itself — which the CLI startup block signals with AKM_CLI_ENTRY=1.
256
- // In any other host (the in-process test runner, a library embedding akm),
257
- // argv[1] points at the host (e.g. the test runner), so spawning it would
258
- // launch the wrong program and orphan it. Build inline there instead — same
259
- // resulting index, no detached process.
260
- if (process.env.AKM_CLI_ENTRY !== "1") {
207
+ if (options.mode === "blocking") {
208
+ if (!isIndexStale(stashDir))
209
+ return false;
261
210
  return runInlineReindex(stashDir);
262
211
  }
263
- try {
264
- await spawnBackgroundReindex(stashDir);
265
- return true;
266
- }
267
- catch (error) {
268
- warn("Background reindex spawn failed, proceeding with existing index:", error instanceof Error ? error.message : String(error));
269
- return true;
270
- }
212
+ if (indexCanServeStash(stashDir))
213
+ return false;
214
+ return runInlineReindex(stashDir);
271
215
  }
@@ -0,0 +1,75 @@
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
+ * MemRL feedback → utility policy, extracted from indexer/db/db.ts.
6
+ *
7
+ * This is the domain/policy math (arXiv:2601.03192) that decides how a batch of
8
+ * positive/negative feedback signals moves an asset's utility score. It is pure
9
+ * — no database access — so the bounded-step behaviour is unit-testable in
10
+ * isolation; the DB read/write stays with `applyFeedbackToUtilityScore` in db.ts.
11
+ */
12
+ /**
13
+ * MemRL learning rate for feedback-driven utility updates (F-5 / #386).
14
+ *
15
+ * Follows the bounded-step formula from MemRL (arXiv:2601.03192):
16
+ * next = clamp(current + lr × (reward − current), 0, 1)
17
+ *
18
+ * This replaces the unbounded `-0.03 × negativeCount` delta that could
19
+ * silently remove high-utility assets from the improvement loop.
20
+ */
21
+ export const FEEDBACK_LR = 0.1;
22
+ /**
23
+ * Positive reward signal for a single positive feedback event.
24
+ * Reward 1.0 means "fully correct / helpful".
25
+ */
26
+ const FEEDBACK_REWARD_POSITIVE = 1.0;
27
+ /**
28
+ * Negative reward signal for a single negative feedback event.
29
+ * Reward 0.0 means "not helpful" (lowest MemRL signal).
30
+ */
31
+ const FEEDBACK_REWARD_NEGATIVE = 0.0;
32
+ /**
33
+ * Utility threshold below which a review-needed escalation is triggered.
34
+ * When a previously high-utility asset (≥ HIGH_UTILITY_THRESHOLD) drops
35
+ * below this value, the caller should create an escalation proposal.
36
+ */
37
+ export const UTILITY_REVIEW_THRESHOLD = 0.5;
38
+ /**
39
+ * Utility level considered "high" — assets above this are tracked for
40
+ * threshold-crossing escalation.
41
+ */
42
+ export const HIGH_UTILITY_THRESHOLD = 0.5;
43
+ /**
44
+ * Compute the next utility from accumulated feedback counts using the MemRL
45
+ * bounded-step EMA formula (F-5 / #386, arXiv:2601.03192):
46
+ *
47
+ * reward = weighted average of positive and negative signals
48
+ * nextUtil = clamp(currentUtil + lr × (reward − currentUtil), 0, 1)
49
+ *
50
+ * The step is inherently bounded: reward ∈ [0, 1] and currentUtil ∈ [0, 1], so
51
+ * a single call moves utility by at most {@link FEEDBACK_LR} in either
52
+ * direction. `reward` is a proportion of the counts, not their magnitude, so
53
+ * with no positive signals the number of negatives is irrelevant (reward is 0
54
+ * whether there is 1 negative or 100). Mixing in positives shifts reward and so
55
+ * the step, but never past the learning-rate bound.
56
+ *
57
+ * Pure: no DB access. When both counts are zero, utility is unchanged.
58
+ */
59
+ export function computeNextUtility(previousUtility, positiveCount, negativeCount) {
60
+ if (positiveCount === 0 && negativeCount === 0) {
61
+ return { previousUtility, nextUtility: previousUtility, crossedReviewThreshold: false };
62
+ }
63
+ const total = positiveCount + negativeCount;
64
+ // Weighted reward: proportion of positive signals.
65
+ const reward = positiveCount > 0 && negativeCount === 0
66
+ ? FEEDBACK_REWARD_POSITIVE
67
+ : negativeCount > 0 && positiveCount === 0
68
+ ? FEEDBACK_REWARD_NEGATIVE
69
+ : (positiveCount * FEEDBACK_REWARD_POSITIVE + negativeCount * FEEDBACK_REWARD_NEGATIVE) / total;
70
+ // MemRL bounded-step EMA: lr × (reward − current). |delta| ≤ FEEDBACK_LR.
71
+ const delta = FEEDBACK_LR * (reward - previousUtility);
72
+ const nextUtility = Math.max(0, Math.min(1, previousUtility + delta));
73
+ const crossedReviewThreshold = previousUtility >= HIGH_UTILITY_THRESHOLD && nextUtility < UTILITY_REVIEW_THRESHOLD;
74
+ return { previousUtility, nextUtility, crossedReviewThreshold };
75
+ }
@@ -241,13 +241,18 @@ export function collectGraphRelatedHit(context, filePath) {
241
241
  * Find graph files that share entities with the given file.
242
242
  *
243
243
  * Implementation: SQL self-join on graph_file_entities, scoped by stash_root,
244
- * grouped by entry_id, ordered by shared-entity count desc. Touches ~50-200
244
+ * grouped by file_path, ordered by shared-entity count desc. Touches ~50-200
245
245
  * rows instead of loading the entire snapshot into memory. Cold-call latency
246
246
  * drops from ~30-60ms (full snapshot parse) to ~2-5ms on typical stashes.
247
247
  *
248
+ * #624-P1: the graph tables are keyed on (stash_root, file_path, body_hash) —
249
+ * NOT entries.id — so candidates are identified by file_path (the unique index
250
+ * idx_graph_files_path guarantees one graph_files row per path).
251
+ *
248
252
  * The returned `ref` field carries the canonical asset ref (`type:name`)
249
- * resolved from entries.entry_key when the entry is indexed. Callers should
250
- * fall back to formatting `path` when `ref` is undefined (orphan graph row).
253
+ * resolved from entries.entry_key when the file is indexed. Callers should
254
+ * fall back to formatting `path` when `ref` is undefined (graph row with no
255
+ * matching entries row).
251
256
  */
252
257
  export function listRelatedPathsForFile(stashRoot, filePath, limit = 5, db) {
253
258
  if (!db) {
@@ -255,113 +260,118 @@ export function listRelatedPathsForFile(stashRoot, filePath, limit = 5, db) {
255
260
  // callers pass a handle), so degrade to empty rather than reopening.
256
261
  return [];
257
262
  }
258
- // Resolve target's entry_id from the stash_root + file_path. The graph rows
259
- // are keyed on entry_id; without it we can't run the join.
260
- let targetEntryId;
263
+ // Confirm the target file has a graph row; without it there is nothing to
264
+ // relate. (Identity is file_path within the stash one row per path.)
261
265
  try {
262
266
  const row = db
263
- .prepare("SELECT entry_id FROM graph_files WHERE stash_root = ? AND file_path = ? LIMIT 1")
267
+ .prepare("SELECT 1 AS present FROM graph_files WHERE stash_root = ? AND file_path = ? LIMIT 1")
264
268
  .get(stashRoot, filePath);
265
- targetEntryId = row?.entry_id;
269
+ if (row === undefined)
270
+ return [];
266
271
  }
267
272
  catch {
268
273
  return [];
269
274
  }
270
- if (targetEntryId == null)
271
- return [];
272
275
  const effectiveLimit = Math.max(1, limit);
273
- // Shared-entity count per candidate entry_id.
276
+ // Shared-entity count per candidate file_path. The target's entities are the
277
+ // rows for `filePath`; candidates are any OTHER file_path in the stash that
278
+ // shares a normalized entity.
274
279
  let candidateRows;
275
280
  try {
276
281
  candidateRows = db
277
- .prepare(`SELECT gf.entry_id AS entry_id,
278
- gf.file_path AS file_path,
282
+ .prepare(`SELECT gf.file_path AS file_path,
279
283
  gf.file_type AS file_type,
280
284
  COUNT(*) AS shared
281
285
  FROM graph_file_entities target
282
286
  JOIN graph_file_entities e
283
287
  ON e.stash_root = target.stash_root
284
288
  AND e.entity_norm = target.entity_norm
285
- AND e.entry_id != target.entry_id
289
+ AND e.file_path != target.file_path
286
290
  JOIN graph_files gf
287
- ON gf.entry_id = e.entry_id
288
- WHERE target.entry_id = ?
291
+ ON gf.stash_root = e.stash_root
292
+ AND gf.file_path = e.file_path
293
+ AND gf.body_hash = e.body_hash
294
+ WHERE target.file_path = ?
289
295
  AND target.stash_root = ?
290
- GROUP BY gf.entry_id
296
+ GROUP BY gf.file_path
291
297
  ORDER BY shared DESC, gf.file_path ASC
292
298
  LIMIT ?`)
293
- .all(targetEntryId, stashRoot, effectiveLimit);
299
+ .all(filePath, stashRoot, effectiveLimit);
294
300
  }
295
301
  catch {
296
302
  return [];
297
303
  }
298
304
  if (candidateRows.length === 0)
299
305
  return [];
300
- const candidateIds = candidateRows.map((r) => r.entry_id);
301
- const placeholders = candidateIds.map(() => "?").join(",");
306
+ const candidatePaths = candidateRows.map((r) => r.file_path);
307
+ const placeholders = candidatePaths.map(() => "?").join(",");
302
308
  // Pull the shared entity names (joined by normalized casing) for display.
303
309
  const sharedRows = db
304
- .prepare(`SELECT e.entry_id AS entry_id, e.entity AS entity
310
+ .prepare(`SELECT e.file_path AS file_path, e.entity AS entity
305
311
  FROM graph_file_entities e
306
312
  JOIN graph_file_entities target
307
313
  ON target.stash_root = e.stash_root
308
314
  AND target.entity_norm = e.entity_norm
309
- WHERE e.entry_id IN (${placeholders})
310
- AND target.entry_id = ?
315
+ WHERE e.file_path IN (${placeholders})
316
+ AND e.stash_root = ?
317
+ AND target.file_path = ?
311
318
  AND target.stash_root = ?`)
312
- .all(...candidateIds, targetEntryId, stashRoot);
313
- const sharedByEntry = new Map();
319
+ .all(...candidatePaths, stashRoot, filePath, stashRoot);
320
+ const sharedByPath = new Map();
314
321
  for (const row of sharedRows) {
315
- let bucket = sharedByEntry.get(row.entry_id);
322
+ let bucket = sharedByPath.get(row.file_path);
316
323
  if (!bucket) {
317
324
  bucket = new Set();
318
- sharedByEntry.set(row.entry_id, bucket);
325
+ sharedByPath.set(row.file_path, bucket);
319
326
  }
320
327
  bucket.add(row.entity);
321
328
  }
322
329
  // Relation count for each candidate (relations where either endpoint
323
330
  // matches one of the shared entities).
324
- const relationCountByEntry = new Map();
331
+ const relationCountByPath = new Map();
325
332
  const relationRows = db
326
- .prepare(`SELECT entry_id, from_entity, to_entity
333
+ .prepare(`SELECT file_path, from_entity, to_entity
327
334
  FROM graph_file_relations
328
- WHERE entry_id IN (${placeholders})`)
329
- .all(...candidateIds);
335
+ WHERE file_path IN (${placeholders})
336
+ AND stash_root = ?`)
337
+ .all(...candidatePaths, stashRoot);
330
338
  for (const row of relationRows) {
331
- const shared = sharedByEntry.get(row.entry_id);
339
+ const shared = sharedByPath.get(row.file_path);
332
340
  if (!shared)
333
341
  continue;
334
342
  if (shared.has(row.from_entity) || shared.has(row.to_entity)) {
335
- relationCountByEntry.set(row.entry_id, (relationCountByEntry.get(row.entry_id) ?? 0) + 1);
343
+ relationCountByPath.set(row.file_path, (relationCountByPath.get(row.file_path) ?? 0) + 1);
336
344
  }
337
345
  }
338
346
  // Optional: ref lookup via entries.entry_key. entry_key is stored as
339
347
  // `${stash_dir}:${type}:${name}` — strip the stash-dir prefix to get the
340
- // user-facing `type:name`.
341
- const refByEntryId = new Map();
348
+ // user-facing `type:name`. Resolve by (stash_dir, file_path) now that the
349
+ // graph rows are no longer keyed on entries.id.
350
+ const refByPath = new Map();
342
351
  try {
343
352
  const entryRows = db
344
- .prepare(`SELECT id, entry_key, stash_dir FROM entries WHERE id IN (${placeholders})`)
345
- .all(...candidateIds);
353
+ .prepare(`SELECT entry_key, stash_dir, file_path FROM entries
354
+ WHERE file_path IN (${placeholders}) AND stash_dir = ?`)
355
+ .all(...candidatePaths, stashRoot);
346
356
  for (const row of entryRows) {
347
357
  const ref = stripStashPrefix(row.entry_key, row.stash_dir);
348
358
  if (ref)
349
- refByEntryId.set(row.id, ref);
359
+ refByPath.set(row.file_path, ref);
350
360
  }
351
361
  }
352
362
  catch {
353
363
  /* ignore — refs are best-effort */
354
364
  }
355
365
  return candidateRows.map((row) => {
356
- const sharedSet = sharedByEntry.get(row.entry_id) ?? new Set();
366
+ const sharedSet = sharedByPath.get(row.file_path) ?? new Set();
357
367
  const sharedEntities = [...sharedSet].sort((a, b) => a.localeCompare(b));
358
- const ref = refByEntryId.get(row.entry_id);
368
+ const ref = refByPath.get(row.file_path);
359
369
  return {
360
370
  ...(ref ? { ref } : {}),
361
371
  path: row.file_path,
362
372
  type: row.file_type,
363
373
  sharedEntities,
364
- relationCount: relationCountByEntry.get(row.entry_id) ?? 0,
374
+ relationCount: relationCountByPath.get(row.file_path) ?? 0,
365
375
  };
366
376
  });
367
377
  }