akm-cli 0.9.1 → 0.9.2-alpha.2

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 (350) hide show
  1. package/CHANGELOG.md +103 -28
  2. package/README.md +3 -1
  3. package/SECURITY.md +1 -1
  4. package/STABILITY.md +1 -1
  5. package/dist/akm +2 -2
  6. package/dist/akm-migrate +2 -2
  7. package/dist/assets/hints/cli-hints-full.md +14 -9
  8. package/dist/assets/improve-strategies/proactive-maintenance.json +1 -1
  9. package/dist/assets/improve-strategies/reflect-distill.json +1 -1
  10. package/dist/assets/models.json +35 -0
  11. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +3 -4
  12. package/dist/assets/stash-skeleton/facts/conventions/organization.md +1 -3
  13. package/dist/assets/tasks/core/extract.yml +6 -5
  14. package/dist/assets/tasks/core/improve.yml +6 -5
  15. package/dist/assets/tasks/core/index-refresh.yml +6 -5
  16. package/dist/assets/tasks/core/sync.yml +6 -5
  17. package/dist/assets/tasks/core/version-check.yml +6 -5
  18. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +6 -5
  19. package/dist/assets/tasks/improve/akm-improve-catchup.yml +6 -5
  20. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +6 -5
  21. package/dist/assets/tasks/improve/akm-improve-frequent.yml +6 -5
  22. package/dist/assets/tasks/improve/akm-improve-nightly.yml +6 -5
  23. package/dist/cli/confirm.js +2 -2
  24. package/dist/cli/parse-args.js +3 -24
  25. package/dist/cli/retired-commands.js +1 -1
  26. package/dist/cli/shared.js +2 -2
  27. package/dist/cli.js +11 -9
  28. package/dist/commands/agent/agent-dispatch.js +55 -89
  29. package/dist/commands/agent/contribute-cli.js +12 -45
  30. package/dist/commands/command/builtin-action.js +32 -0
  31. package/dist/commands/command/command-cli.js +99 -0
  32. package/dist/commands/command/command-execution.js +308 -0
  33. package/dist/commands/command/execution-source-loader.js +176 -0
  34. package/dist/commands/command/portable-template.js +60 -0
  35. package/dist/commands/config-cli.js +10 -4
  36. package/dist/commands/env/env.js +4 -2
  37. package/dist/commands/feedback-cli.js +1 -1
  38. package/dist/commands/health/checks.js +241 -29
  39. package/dist/commands/health/html-report.js +0 -14
  40. package/dist/commands/health/report-view-model.js +0 -1
  41. package/dist/commands/health/surfaces.js +6 -7
  42. package/dist/commands/health/types.js +0 -2
  43. package/dist/commands/health.js +63 -18
  44. package/dist/commands/improve/collapse-detector.js +5 -6
  45. package/dist/commands/improve/consolidate.js +251 -214
  46. package/dist/commands/improve/distill/promote-memory.js +71 -34
  47. package/dist/commands/improve/distill/quality-gate.js +17 -5
  48. package/dist/commands/improve/distill.js +232 -155
  49. package/dist/commands/improve/eligibility.js +112 -79
  50. package/dist/commands/improve/execution.js +57 -0
  51. package/dist/commands/improve/extract-cli.js +5 -5
  52. package/dist/commands/improve/extract-prompt.js +64 -22
  53. package/dist/commands/improve/extract.js +608 -360
  54. package/dist/commands/improve/improve-strategies.js +43 -14
  55. package/dist/commands/improve/improve.js +249 -29
  56. package/dist/commands/improve/loop-stages.js +11 -17
  57. package/dist/commands/improve/memory/memory-contradiction-detect.js +90 -66
  58. package/dist/commands/improve/outcome-loop.js +22 -38
  59. package/dist/commands/improve/planner.js +134 -0
  60. package/dist/commands/improve/preparation.js +730 -409
  61. package/dist/commands/improve/reflect.js +386 -223
  62. package/dist/commands/improve/run-context.js +3 -4
  63. package/dist/commands/improve/salience.js +6 -58
  64. package/dist/commands/improve/session-asset.js +12 -12
  65. package/dist/commands/lint/index.js +101 -29
  66. package/dist/commands/migrate-cli.js +11 -69
  67. package/dist/commands/migration-tool.js +6 -9
  68. package/dist/commands/models-cli.js +27 -0
  69. package/dist/commands/proposal/drain.js +258 -186
  70. package/dist/commands/proposal/proposal-cli.js +32 -10
  71. package/dist/commands/proposal/proposal.js +2 -5
  72. package/dist/commands/proposal/propose.js +192 -172
  73. package/dist/commands/proposal/repository.js +54 -91
  74. package/dist/commands/proposal/validators/proposal-validators.js +9 -7
  75. package/dist/commands/read/curate.js +53 -22
  76. package/dist/commands/read/registry-search.js +25 -9
  77. package/dist/commands/read/remember-cli.js +14 -2
  78. package/dist/commands/read/search.js +10 -4
  79. package/dist/commands/read/show.js +139 -153
  80. package/dist/commands/registry-cli.js +16 -7
  81. package/dist/commands/remember.js +33 -18
  82. package/dist/commands/sources/add-cli.js +19 -178
  83. package/dist/commands/sources/bundle-cli.js +15 -3
  84. package/dist/commands/sources/dangerous-env-audit.js +135 -0
  85. package/dist/commands/sources/info.js +2 -1
  86. package/dist/commands/sources/installed-stashes.js +901 -177
  87. package/dist/commands/sources/schema-repair.js +174 -95
  88. package/dist/commands/sources/self-update.js +30 -74
  89. package/dist/commands/sources/source-add.js +3 -5
  90. package/dist/commands/sources/sources-cli.js +2 -15
  91. package/dist/commands/sources/update-transaction.js +220 -0
  92. package/dist/commands/tasks/tasks-cli.js +3 -3
  93. package/dist/commands/tasks/tasks.js +736 -317
  94. package/dist/commands/workflow-cli.js +2 -2
  95. package/dist/core/adapter/adapters/agent-skills-adapter.js +3 -0
  96. package/dist/core/adapter/adapters/akm-adapter.js +85 -35
  97. package/dist/core/adapter/adapters/akm-lint.js +54 -39
  98. package/dist/core/adapter/adapters/akm-metadata.js +45 -45
  99. package/dist/core/adapter/adapters/akm-task-adapter.js +32 -49
  100. package/dist/core/adapter/adapters/akm-workflow-adapter.js +38 -23
  101. package/dist/core/adapter/adapters/dotenv-adapter.js +30 -1
  102. package/dist/core/adapter/adapters/generic-files-adapter.js +11 -0
  103. package/dist/core/adapter/adapters/index.js +0 -9
  104. package/dist/core/adapter/adapters/llm-wiki-adapter.js +4 -0
  105. package/dist/core/adapter/adapters/okf-adapter.js +4 -0
  106. package/dist/core/adapter/adapters/opencode-adapter.js +5 -8
  107. package/dist/core/adapter/adapters/tool-dir-shared.js +63 -6
  108. package/dist/core/adapter/adapters/website-snapshot-adapter.js +4 -0
  109. package/dist/core/adapter/execution-source.js +308 -0
  110. package/dist/core/adapter/recognize-match.js +36 -13
  111. package/dist/core/adapter/registry.js +0 -9
  112. package/dist/core/asset/stash-meta.js +94 -4
  113. package/dist/core/common.js +6 -11
  114. package/dist/core/config/config-io.js +3 -3
  115. package/dist/core/config/config-schema.js +18 -40
  116. package/dist/core/config/config-sources.js +11 -21
  117. package/dist/core/config/config-walker.js +31 -13
  118. package/dist/core/config/config.js +23 -26
  119. package/dist/core/config/schema/engines.js +8 -7
  120. package/dist/core/config/schema/improve-processes.js +29 -5
  121. package/dist/core/config/schema/index-config.js +0 -27
  122. package/dist/core/config/schema/primitives.js +1 -23
  123. package/dist/core/config/schema/sources-bundles.js +13 -16
  124. package/dist/core/errors.js +2 -0
  125. package/dist/core/events.js +68 -32
  126. package/dist/core/extra-params.js +1 -0
  127. package/dist/core/improve-result.js +315 -0
  128. package/dist/core/lesson-lint.js +0 -6
  129. package/dist/core/maintenance-barrier.js +4 -4
  130. package/dist/core/network-policy.js +152 -0
  131. package/dist/core/paths.js +1 -1
  132. package/dist/core/recognition-util.js +4 -4
  133. package/dist/core/registry-url.js +456 -0
  134. package/dist/core/state/migrations.js +161 -47
  135. package/dist/core/state-db.js +453 -80
  136. package/dist/core/system-error.js +32 -0
  137. package/dist/core/time.js +2 -12
  138. package/dist/core/write-source.js +0 -18
  139. package/dist/execution/directory-identity.js +52 -0
  140. package/dist/execution/executable-identity.js +107 -0
  141. package/dist/execution/guarded-source.js +398 -0
  142. package/dist/execution/json.js +95 -0
  143. package/dist/{commands/health/types-session-log.js → execution/limits.js} +2 -1
  144. package/dist/execution/record.js +55 -0
  145. package/dist/execution/resolved-request.js +730 -0
  146. package/dist/execution/source.js +320 -0
  147. package/dist/indexer/bundle-identity-guard.js +5 -4
  148. package/dist/indexer/db/graph-db.js +33 -0
  149. package/dist/indexer/graph/graph-boost.js +3 -4
  150. package/dist/indexer/graph/graph-extraction.js +562 -373
  151. package/dist/indexer/index-written-assets.js +78 -39
  152. package/dist/indexer/indexer.js +471 -432
  153. package/dist/indexer/installations.js +6 -0
  154. package/dist/indexer/lookup/adapter-concept-owner.js +283 -0
  155. package/dist/indexer/materialize-embeddings.js +155 -0
  156. package/dist/indexer/passes/memory-inference.js +227 -174
  157. package/dist/indexer/passes/metadata.js +263 -118
  158. package/dist/indexer/scan/doc-to-entry.js +7 -10
  159. package/dist/indexer/scan/drain-dir.js +51 -23
  160. package/dist/indexer/search/db-search.js +156 -50
  161. package/dist/indexer/search/fts-query.js +40 -40
  162. package/dist/indexer/search/ranking.js +36 -1
  163. package/dist/indexer/search/search-attribution.js +3 -1
  164. package/dist/indexer/search/search-fields.js +23 -14
  165. package/dist/indexer/search/search-hit-enrichers.js +1 -1
  166. package/dist/indexer/search/search-source.js +7 -16
  167. package/dist/indexer/search/semantic-status.js +10 -1
  168. package/dist/indexer/usage/show-usage.js +105 -0
  169. package/dist/indexer/usage/usage-events.js +7 -2
  170. package/dist/indexer/walk/matchers.js +40 -10
  171. package/dist/indexer/walk/path-resolver.js +5 -2
  172. package/dist/indexer/walk/walker.js +20 -2
  173. package/dist/integrations/agent/builder-shared.js +3 -6
  174. package/dist/integrations/agent/conversation-fallback.js +16 -0
  175. package/dist/integrations/agent/engine-resolution.js +87 -87
  176. package/dist/integrations/agent/execution-cascade.js +566 -0
  177. package/dist/integrations/agent/execution-definitions.js +211 -0
  178. package/dist/integrations/agent/execution-lowering.js +811 -0
  179. package/dist/integrations/agent/execution-preparation.js +67 -0
  180. package/dist/integrations/agent/index.js +0 -2
  181. package/dist/integrations/agent/inline-execution.js +74 -0
  182. package/dist/integrations/agent/model-map.js +515 -0
  183. package/dist/integrations/agent/persona-fallback.js +30 -0
  184. package/dist/integrations/agent/request-lowering.js +186 -0
  185. package/dist/integrations/agent/runner-dispatch.js +230 -37
  186. package/dist/integrations/agent/runner.js +12 -83
  187. package/dist/integrations/harnesses/aider/agent-builder.js +8 -0
  188. package/dist/integrations/harnesses/aider/index.js +0 -1
  189. package/dist/integrations/harnesses/amazonq/agent-builder.js +8 -0
  190. package/dist/integrations/harnesses/amazonq/index.js +0 -1
  191. package/dist/integrations/harnesses/claude/agent-builder.js +14 -1
  192. package/dist/integrations/harnesses/claude/index.js +1 -5
  193. package/dist/integrations/harnesses/claude/session-log.js +3 -33
  194. package/dist/integrations/harnesses/codex/agent-builder.js +8 -0
  195. package/dist/integrations/harnesses/codex/index.js +0 -1
  196. package/dist/integrations/harnesses/copilot/agent-builder.js +8 -0
  197. package/dist/integrations/harnesses/copilot/index.js +0 -1
  198. package/dist/integrations/harnesses/gemini/agent-builder.js +8 -0
  199. package/dist/integrations/harnesses/gemini/index.js +0 -1
  200. package/dist/integrations/harnesses/index.js +4 -44
  201. package/dist/integrations/harnesses/opencode/agent-builder.js +16 -9
  202. package/dist/integrations/harnesses/opencode/index.js +0 -2
  203. package/dist/integrations/harnesses/opencode/session-log.js +14 -204
  204. package/dist/integrations/harnesses/opencode-sdk/harness.js +12 -1
  205. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +40 -42
  206. package/dist/integrations/harnesses/openhands/agent-builder.js +8 -0
  207. package/dist/integrations/harnesses/openhands/index.js +0 -1
  208. package/dist/integrations/harnesses/pi/agent-builder.js +8 -0
  209. package/dist/integrations/harnesses/pi/index.js +0 -1
  210. package/dist/integrations/harnesses/shared.js +0 -1
  211. package/dist/integrations/harnesses/types.js +1 -3
  212. package/dist/integrations/lockfile.js +82 -79
  213. package/dist/integrations/session-logs/index.js +6 -17
  214. package/dist/integrations/session-logs/provider-base.js +1 -29
  215. package/dist/llm/client.js +10 -5
  216. package/dist/llm/embedder.js +6 -7
  217. package/dist/llm/embedders/local.js +37 -88
  218. package/dist/llm/embedders/types.js +1 -1
  219. package/dist/llm/graph-extract.js +75 -50
  220. package/dist/llm/index-passes.js +43 -5
  221. package/dist/llm/memory-infer.js +8 -6
  222. package/dist/llm/metadata-enhance.js +5 -3
  223. package/dist/llm/structured-call.js +122 -25
  224. package/dist/output/format-exempt.js +1 -1
  225. package/dist/output/render-registry.js +0 -16
  226. package/dist/output/renderers.js +12 -7
  227. package/dist/output/shapes/curate.js +1 -0
  228. package/dist/output/shapes/helpers.js +10 -2
  229. package/dist/output/shapes/passthrough.js +2 -0
  230. package/dist/output/text/command-format.js +31 -33
  231. package/dist/output/text/health-format.js +1 -29
  232. package/dist/output/text/migrate.js +6 -56
  233. package/dist/output/text/proposal-format.js +16 -1
  234. package/dist/output/text/workflow-format.js +16 -0
  235. package/dist/registry/network.js +279 -0
  236. package/dist/registry/pinned-request-helper.js +247 -0
  237. package/dist/registry/pinned-transport.js +717 -0
  238. package/dist/registry/providers/skills-sh.js +18 -6
  239. package/dist/registry/providers/static-index.js +20 -7
  240. package/dist/registry/resolve.js +53 -28
  241. package/dist/scripts/akm-migrate-node.js +19334 -52269
  242. package/dist/scripts/akm-migrate.js +19270 -51612
  243. package/dist/setup/registry-stash-loader.js +64 -20
  244. package/dist/setup/semantic-assets.js +9 -34
  245. package/dist/setup/setup.js +12 -30
  246. package/dist/setup/source-identity.js +17 -0
  247. package/dist/setup/steps/sources.js +36 -15
  248. package/dist/setup/steps/tasks.js +39 -11
  249. package/dist/sources/providers/git-provider.js +3 -3
  250. package/dist/sources/providers/npm.js +2 -2
  251. package/dist/sources/providers/provider-utils.js +4 -3
  252. package/dist/sources/providers/website.js +11 -7
  253. package/dist/sources/snapshot-fetchers/host-guard.js +9 -136
  254. package/dist/sources/snapshot-fetchers/website-ingest.js +25 -109
  255. package/dist/sources/website-url.js +73 -0
  256. package/dist/storage/engines/sqlite-migrations.js +81 -26
  257. package/dist/storage/managed-db.js +27 -24
  258. package/dist/storage/repositories/events-repository.js +3 -0
  259. package/dist/storage/repositories/index-connection.js +42 -10
  260. package/dist/storage/repositories/index-entries-repository.js +203 -229
  261. package/dist/storage/repositories/index-entry-mapper.js +8 -12
  262. package/dist/storage/repositories/index-entry-schema.js +255 -0
  263. package/dist/storage/repositories/index-fts-repository.js +64 -71
  264. package/dist/storage/repositories/index-llm-cache-repository.js +8 -13
  265. package/dist/storage/repositories/index-meta-repository.js +0 -11
  266. package/dist/storage/repositories/index-schema.js +74 -350
  267. package/dist/storage/repositories/index-utility-repository.js +12 -17
  268. package/dist/storage/repositories/index-vec-repository.js +56 -7
  269. package/dist/storage/repositories/proposals-repository.js +4 -127
  270. package/dist/storage/repositories/registry-cache.js +2 -1
  271. package/dist/storage/repositories/task-history-repository.js +20 -40
  272. package/dist/storage/repositories/workflow-runs-repository.js +228 -129
  273. package/dist/storage/sqlite-read-snapshot.js +148 -0
  274. package/dist/tasks/backends/cron.js +170 -42
  275. package/dist/tasks/backends/index.js +1 -1
  276. package/dist/tasks/backends/launchd.js +787 -202
  277. package/dist/tasks/backends/schtasks.js +282 -83
  278. package/dist/tasks/embedded.js +7 -7
  279. package/dist/tasks/frozen-script.js +50 -0
  280. package/dist/tasks/resolve-akm-bin.js +5 -1
  281. package/dist/tasks/runner.js +239 -251
  282. package/dist/tasks/runtime-v3.js +281 -0
  283. package/dist/tasks/scheduler-binding.js +272 -0
  284. package/dist/tasks/scheduler-invocation.js +57 -43
  285. package/dist/tasks/scheduler-sync.js +654 -0
  286. package/dist/tasks/source-v3.js +752 -0
  287. package/dist/tasks/standalone-script-entry.js +5 -0
  288. package/dist/tasks/task-id.js +29 -0
  289. package/dist/workflows/authoring/authoring.js +15 -32
  290. package/dist/workflows/exec/dispatch-redaction.js +14 -8
  291. package/dist/workflows/exec/exec-unit.js +7 -28
  292. package/dist/workflows/exec/frozen-judge.js +57 -89
  293. package/dist/workflows/exec/lowering-notices.js +23 -0
  294. package/dist/workflows/exec/native-executor.js +301 -458
  295. package/dist/workflows/exec/param-secrets.js +4 -3
  296. package/dist/workflows/exec/run-workflow.js +26 -32
  297. package/dist/workflows/exec/step-work.js +105 -109
  298. package/dist/workflows/exec/unit-dispatch.js +103 -27
  299. package/dist/workflows/exec/unit-writer.js +3 -3
  300. package/dist/workflows/exec/worktree.js +2 -2
  301. package/dist/workflows/ir/compile.js +86 -72
  302. package/dist/workflows/ir/environment-v4.js +328 -0
  303. package/dist/workflows/ir/freeze-v4.js +122 -0
  304. package/dist/workflows/ir/plan-hash.js +13 -7
  305. package/dist/workflows/ir/schema-v4.js +525 -0
  306. package/dist/workflows/ir/schema.js +25 -284
  307. package/dist/workflows/ir/source-freeze-v4.js +506 -0
  308. package/dist/workflows/parser.js +27 -24
  309. package/dist/workflows/program/schema.js +1 -2
  310. package/dist/workflows/renderer.js +42 -29
  311. package/dist/workflows/resource-limits.js +4 -5
  312. package/dist/workflows/runtime/agent-identity.js +11 -13
  313. package/dist/workflows/runtime/plan-classifier.js +8 -8
  314. package/dist/workflows/runtime/runs.js +27 -43
  315. package/dist/workflows/runtime/workflow-asset-loader.js +45 -205
  316. package/dist/workflows/source-files.js +373 -0
  317. package/dist/workflows/source-ir/compile.js +196 -0
  318. package/dist/workflows/source-ir/github-yaml.js +577 -0
  319. package/dist/workflows/source-ir/ordering.js +38 -0
  320. package/dist/workflows/source-ir/program.js +50 -0
  321. package/dist/workflows/source-ir/result.js +26 -0
  322. package/dist/workflows/source-ir/schema.js +772 -0
  323. package/dist/workflows/source-ir/semantics.js +242 -0
  324. package/dist/workflows/source-ir/uses.js +14 -0
  325. package/docs/README.md +2 -0
  326. package/docs/migration/README.md +3 -1
  327. package/docs/migration/release-notes/0.9.2.md +55 -0
  328. package/docs/migration/release-notes/README.md +5 -0
  329. package/docs/migration/v0.8-to-v0.9.md +76 -1077
  330. package/docs/migration/v0.9.0-troubleshooting.md +104 -516
  331. package/docs/migration/v0.9.1-to-v0.9.2.md +150 -0
  332. package/docs/reference/README.md +1 -0
  333. package/docs/reference/cli.md +230 -98
  334. package/docs/reference/configuration.md +159 -36
  335. package/docs/reference/data-and-telemetry.md +19 -1
  336. package/docs/reference/supported-formats.md +23 -3
  337. package/docs/reference/tasks.md +182 -0
  338. package/docs/reference/workflow-schema.md +91 -40
  339. package/docs/reference/workflows.md +33 -6
  340. package/package.json +10 -6
  341. package/schemas/akm-config.json +372 -224
  342. package/schemas/akm-task.json +324 -80
  343. package/schemas/akm-workflow.json +6 -9
  344. package/dist/core/migration-operation.js +0 -75
  345. package/dist/integrations/agent/model-aliases.js +0 -74
  346. package/dist/tasks/parser.js +0 -380
  347. package/dist/tasks/schema.js +0 -123
  348. package/dist/tasks/validator.js +0 -80
  349. package/dist/workflows/ir/freeze.js +0 -320
  350. package/dist/workflows/runtime/document-cache.js +0 -13
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * Centralizes the one canonical `entries` SELECT column list and the
8
8
  * JSON-parse-guarded row → {@link DbIndexedEntry} mapping that several queries
9
- * used to reimplement. Corrupt `entry_json` rows are skipped (warn once) rather
9
+ * used to reimplement. Corrupt `document_json` rows are skipped (warn once) rather
10
10
  * than crashing the caller.
11
11
  *
12
12
  * Relocated from `src/indexer/db/entry-mapper.ts` (WI-5a): it now imports the
@@ -23,36 +23,32 @@ import { warn } from "../../core/warn.js";
23
23
  * Durable identity columns are surfaced here so every mapped entry carries
24
24
  * current indexed provenance.
25
25
  */
26
- export const ENTRY_COLUMNS = "id, entry_key, dir_path, file_path, stash_dir, entry_json, search_text, item_ref, concept_id, bundle_id, adapter_id";
26
+ export const ENTRY_COLUMNS = "id, item_ref, bundle_id, component_id, concept_id, adapter_id, type, file_path, content_hash, document_json, search_text";
27
27
  /**
28
- * Map one raw `entries` row to a {@link DbIndexedEntry}, parsing `entry_json`.
28
+ * Map one raw `entries` row to a {@link DbIndexedEntry}, parsing `document_json`.
29
29
  * Returns `null` (and warns, tagged with `context`) when the JSON is corrupt so
30
30
  * callers can skip the row instead of crashing.
31
31
  */
32
32
  export function rowToIndexedEntry(row, context) {
33
33
  let entry;
34
34
  try {
35
- entry = JSON.parse(row.entry_json);
35
+ entry = JSON.parse(row.document_json);
36
36
  }
37
37
  catch {
38
- warn(`[db] ${context}: skipping entry id=${row.id} — corrupt entry_json`);
39
- return null;
40
- }
41
- if (!row.item_ref || !row.concept_id || !row.bundle_id || !row.adapter_id) {
42
- warn(`[db] ${context}: skipping entry id=${row.id} — missing indexed provenance`);
38
+ warn(`[db] ${context}: skipping entry id=${row.id} — corrupt document_json`);
43
39
  return null;
44
40
  }
45
41
  return {
46
42
  id: row.id,
47
- entryKey: row.entry_key,
48
- dirPath: row.dir_path,
49
43
  filePath: row.file_path,
50
- stashDir: row.stash_dir,
51
44
  entry,
52
45
  searchText: row.search_text,
53
46
  itemRef: row.item_ref,
54
47
  conceptId: row.concept_id,
55
48
  bundleId: row.bundle_id,
49
+ componentId: row.component_id,
56
50
  adapterId: row.adapter_id,
51
+ type: row.type,
52
+ ...(row.content_hash ? { contentHash: row.content_hash } : {}),
57
53
  };
58
54
  }
@@ -0,0 +1,255 @@
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
+ * The single canonical contract for the derived `entries` generation.
6
+ *
7
+ * `index.db` is regenerable, so callers compare one normalized PRAGMA
8
+ * fingerprint and discard any generation that differs. This module owns both
9
+ * the DDL and its expected fingerprint so schema creation, writable opens,
10
+ * serving preflights, and read-only evaluator tooling cannot drift into
11
+ * separate definitions of "current".
12
+ */
13
+ export const CANONICAL_INDEX_DB_VERSION = 22;
14
+ export const CANONICAL_ENTRY_SCHEMA_SQL = `
15
+ CREATE TABLE IF NOT EXISTS entries (
16
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
17
+ item_ref TEXT NOT NULL UNIQUE,
18
+ bundle_id TEXT NOT NULL,
19
+ component_id TEXT NOT NULL,
20
+ concept_id TEXT NOT NULL,
21
+ adapter_id TEXT NOT NULL,
22
+ type TEXT NOT NULL,
23
+ file_path TEXT NOT NULL,
24
+ content_hash TEXT,
25
+ document_json TEXT NOT NULL,
26
+ search_text TEXT NOT NULL,
27
+ derived_from TEXT
28
+ );
29
+
30
+ CREATE INDEX IF NOT EXISTS idx_entries_bundle ON entries(bundle_id);
31
+ CREATE INDEX IF NOT EXISTS idx_entries_type ON entries(type);
32
+ CREATE INDEX IF NOT EXISTS idx_entries_file_path ON entries(file_path);
33
+ CREATE INDEX IF NOT EXISTS idx_entries_derived_from ON entries(derived_from);
34
+ `;
35
+ const CANONICAL_ENTRY_SCHEMA_FINGERPRINT = {
36
+ tableSql: "CREATE TABLE entries ( id INTEGER PRIMARY KEY AUTOINCREMENT, item_ref TEXT NOT NULL UNIQUE, bundle_id TEXT NOT NULL, component_id TEXT NOT NULL, concept_id TEXT NOT NULL, adapter_id TEXT NOT NULL, type TEXT NOT NULL, file_path TEXT NOT NULL, content_hash TEXT, document_json TEXT NOT NULL, search_text TEXT NOT NULL, derived_from TEXT )",
37
+ sqliteSequenceTable: true,
38
+ sqliteSequenceValid: true,
39
+ columns: [
40
+ { cid: 0, name: "id", type: "INTEGER", notNull: 0, defaultValue: null, primaryKeyPosition: 1, hidden: 0 },
41
+ {
42
+ cid: 1,
43
+ name: "item_ref",
44
+ type: "TEXT",
45
+ notNull: 1,
46
+ defaultValue: null,
47
+ primaryKeyPosition: 0,
48
+ hidden: 0,
49
+ },
50
+ {
51
+ cid: 2,
52
+ name: "bundle_id",
53
+ type: "TEXT",
54
+ notNull: 1,
55
+ defaultValue: null,
56
+ primaryKeyPosition: 0,
57
+ hidden: 0,
58
+ },
59
+ {
60
+ cid: 3,
61
+ name: "component_id",
62
+ type: "TEXT",
63
+ notNull: 1,
64
+ defaultValue: null,
65
+ primaryKeyPosition: 0,
66
+ hidden: 0,
67
+ },
68
+ {
69
+ cid: 4,
70
+ name: "concept_id",
71
+ type: "TEXT",
72
+ notNull: 1,
73
+ defaultValue: null,
74
+ primaryKeyPosition: 0,
75
+ hidden: 0,
76
+ },
77
+ {
78
+ cid: 5,
79
+ name: "adapter_id",
80
+ type: "TEXT",
81
+ notNull: 1,
82
+ defaultValue: null,
83
+ primaryKeyPosition: 0,
84
+ hidden: 0,
85
+ },
86
+ { cid: 6, name: "type", type: "TEXT", notNull: 1, defaultValue: null, primaryKeyPosition: 0, hidden: 0 },
87
+ {
88
+ cid: 7,
89
+ name: "file_path",
90
+ type: "TEXT",
91
+ notNull: 1,
92
+ defaultValue: null,
93
+ primaryKeyPosition: 0,
94
+ hidden: 0,
95
+ },
96
+ {
97
+ cid: 8,
98
+ name: "content_hash",
99
+ type: "TEXT",
100
+ notNull: 0,
101
+ defaultValue: null,
102
+ primaryKeyPosition: 0,
103
+ hidden: 0,
104
+ },
105
+ {
106
+ cid: 9,
107
+ name: "document_json",
108
+ type: "TEXT",
109
+ notNull: 1,
110
+ defaultValue: null,
111
+ primaryKeyPosition: 0,
112
+ hidden: 0,
113
+ },
114
+ {
115
+ cid: 10,
116
+ name: "search_text",
117
+ type: "TEXT",
118
+ notNull: 1,
119
+ defaultValue: null,
120
+ primaryKeyPosition: 0,
121
+ hidden: 0,
122
+ },
123
+ {
124
+ cid: 11,
125
+ name: "derived_from",
126
+ type: "TEXT",
127
+ notNull: 0,
128
+ defaultValue: null,
129
+ primaryKeyPosition: 0,
130
+ hidden: 0,
131
+ },
132
+ ],
133
+ indexes: [
134
+ {
135
+ name: "idx_entries_bundle",
136
+ unique: 0,
137
+ origin: "c",
138
+ partial: 0,
139
+ columns: [
140
+ { sequence: 0, cid: 2, name: "bundle_id", descending: 0, collation: "BINARY", key: 1 },
141
+ { sequence: 1, cid: -1, name: null, descending: 0, collation: "BINARY", key: 0 },
142
+ ],
143
+ },
144
+ {
145
+ name: "idx_entries_derived_from",
146
+ unique: 0,
147
+ origin: "c",
148
+ partial: 0,
149
+ columns: [
150
+ { sequence: 0, cid: 11, name: "derived_from", descending: 0, collation: "BINARY", key: 1 },
151
+ { sequence: 1, cid: -1, name: null, descending: 0, collation: "BINARY", key: 0 },
152
+ ],
153
+ },
154
+ {
155
+ name: "idx_entries_file_path",
156
+ unique: 0,
157
+ origin: "c",
158
+ partial: 0,
159
+ columns: [
160
+ { sequence: 0, cid: 7, name: "file_path", descending: 0, collation: "BINARY", key: 1 },
161
+ { sequence: 1, cid: -1, name: null, descending: 0, collation: "BINARY", key: 0 },
162
+ ],
163
+ },
164
+ {
165
+ name: "idx_entries_type",
166
+ unique: 0,
167
+ origin: "c",
168
+ partial: 0,
169
+ columns: [
170
+ { sequence: 0, cid: 6, name: "type", descending: 0, collation: "BINARY", key: 1 },
171
+ { sequence: 1, cid: -1, name: null, descending: 0, collation: "BINARY", key: 0 },
172
+ ],
173
+ },
174
+ {
175
+ name: "sqlite_autoindex_entries_1",
176
+ unique: 1,
177
+ origin: "u",
178
+ partial: 0,
179
+ columns: [
180
+ { sequence: 0, cid: 1, name: "item_ref", descending: 0, collation: "BINARY", key: 1 },
181
+ { sequence: 1, cid: -1, name: null, descending: 0, collation: "BINARY", key: 0 },
182
+ ],
183
+ },
184
+ ],
185
+ };
186
+ function sqlString(value) {
187
+ return `'${value.replaceAll("'", "''")}'`;
188
+ }
189
+ function normalizeSchemaSql(value) {
190
+ if (value == null)
191
+ return null;
192
+ return value.replace(/\s+/g, " ").trim();
193
+ }
194
+ export function readEntrySchemaFingerprint(db) {
195
+ const tableRow = db.prepare("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'entries'").get();
196
+ const sqliteSequenceTable = db.prepare("SELECT 1 AS present FROM sqlite_master WHERE type = 'table' AND name = 'sqlite_sequence'").get() !=
197
+ null;
198
+ const maxId = Number(db.prepare("SELECT COALESCE(MAX(id), 0) AS maxId FROM entries").get().maxId);
199
+ const sequenceRow = sqliteSequenceTable
200
+ ? db.prepare("SELECT seq FROM sqlite_sequence WHERE name = 'entries'").get()
201
+ : undefined;
202
+ const sqliteSequenceValid = sqliteSequenceTable &&
203
+ (maxId === 0 ? sequenceRow == null || Number(sequenceRow.seq) >= 0 : Number(sequenceRow?.seq) >= maxId);
204
+ const columns = db.prepare("PRAGMA table_xinfo(entries)").all().map((column) => ({
205
+ cid: Number(column.cid),
206
+ name: column.name,
207
+ type: column.type.trim().toUpperCase(),
208
+ notNull: Number(column.notnull),
209
+ defaultValue: column.dflt_value === null ? null : String(column.dflt_value),
210
+ primaryKeyPosition: Number(column.pk),
211
+ hidden: Number(column.hidden),
212
+ }));
213
+ const indexes = db.prepare("PRAGMA index_list(entries)").all()
214
+ .map((index) => ({
215
+ name: index.name,
216
+ unique: Number(index.unique),
217
+ origin: index.origin,
218
+ partial: Number(index.partial),
219
+ columns: db.prepare(`PRAGMA index_xinfo(${sqlString(index.name)})`).all()
220
+ .sort((left, right) => Number(left.seqno) - Number(right.seqno))
221
+ .map((column) => ({
222
+ sequence: Number(column.seqno),
223
+ cid: Number(column.cid),
224
+ name: column.name,
225
+ descending: Number(column.desc),
226
+ collation: column.coll,
227
+ key: Number(column.key),
228
+ })),
229
+ }))
230
+ .sort((left, right) => left.name.localeCompare(right.name));
231
+ return {
232
+ tableSql: normalizeSchemaSql(tableRow?.sql),
233
+ sqliteSequenceTable,
234
+ sqliteSequenceValid,
235
+ columns,
236
+ indexes,
237
+ };
238
+ }
239
+ export function hasCanonicalEntrySchema(db) {
240
+ try {
241
+ return JSON.stringify(readEntrySchemaFingerprint(db)) === JSON.stringify(CANONICAL_ENTRY_SCHEMA_FINGERPRINT);
242
+ }
243
+ catch {
244
+ return false;
245
+ }
246
+ }
247
+ export function isCanonicalIndexGeneration(db) {
248
+ try {
249
+ const row = db.prepare("SELECT value FROM index_meta WHERE key = 'version'").get();
250
+ return row?.value === String(CANONICAL_INDEX_DB_VERSION) && hasCanonicalEntrySchema(db);
251
+ }
252
+ catch {
253
+ return false;
254
+ }
255
+ }
@@ -2,39 +2,69 @@
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
- * `index.db` FTS5 search + rebuild repository.
5
+ * `index.db` FTS5 search + materialization repository.
6
6
  *
7
- * Owns the `entries_fts` full-text query path and the incremental/full FTS
8
- * rebuild.
7
+ * Owns the `entries_fts` full-text query path, per-entry projections, and the
8
+ * explicit full recovery rebuild.
9
9
  */
10
10
  import { warn } from "../../core/warn.js";
11
- import { buildPrefixQuery, sanitizeFtsQuery } from "../../indexer/search/fts-query.js";
11
+ import { buildLexicalQueryPlan } from "../../indexer/search/fts-query.js";
12
12
  import { buildSearchFields } from "../../indexer/search/search-fields.js";
13
13
  import { SQLITE_CHUNK_SIZE } from "./index-sql.js";
14
+ const INSERT_FTS_SQL = "INSERT INTO entries_fts (entry_id, name, description, tags, hints, content) VALUES (?, ?, ?, ?, ?, ?)";
15
+ const ftsMutationStatementsByDb = new WeakMap();
16
+ function getFtsMutationStatements(db) {
17
+ const existing = ftsMutationStatementsByDb.get(db);
18
+ if (existing)
19
+ return existing;
20
+ const statements = {
21
+ deleteOne: db.prepare("DELETE FROM entries_fts WHERE entry_id = ?"),
22
+ insert: db.prepare(INSERT_FTS_SQL),
23
+ };
24
+ ftsMutationStatementsByDb.set(db, statements);
25
+ return statements;
26
+ }
27
+ /** Replace one entry's derived FTS projection inside the caller's transaction. */
28
+ export function replaceFtsEntry(db, entryId, entry) {
29
+ const fields = buildSearchFields(entry);
30
+ const statements = getFtsMutationStatements(db);
31
+ statements.deleteOne.run(entryId);
32
+ statements.insert.run(entryId, fields.name, fields.description, fields.tags, fields.hints, fields.content);
33
+ }
34
+ /** Delete derived FTS projections for canonical entries that are being removed. */
35
+ export function deleteFtsEntries(db, entryIds) {
36
+ for (let i = 0; i < entryIds.length; i += SQLITE_CHUNK_SIZE) {
37
+ const chunk = entryIds.slice(i, i + SQLITE_CHUNK_SIZE);
38
+ const placeholders = chunk.map(() => "?").join(",");
39
+ db.prepare(`DELETE FROM entries_fts WHERE entry_id IN (${placeholders})`).run(...chunk);
40
+ }
41
+ }
14
42
  export function searchFts(db, query, limit, entryType, excludeTypes) {
15
- const ftsQuery = sanitizeFtsQuery(query);
16
- if (!ftsQuery)
43
+ const plan = buildLexicalQueryPlan(query);
44
+ if (!plan.exact)
17
45
  return [];
18
46
  // Try the exact AND query first
19
- const exactResults = runFtsQuery(db, ftsQuery, limit, entryType, excludeTypes);
47
+ const exactResults = runFtsQuery(db, plan.exact, "exact", limit, entryType, excludeTypes);
20
48
  if (exactResults.length > 0)
21
49
  return exactResults;
22
- // Exact match returned zero results — try prefix fallback.
23
- // Append FTS5 `*` suffix to each token that is >= 3 characters long.
24
- // Short tokens (1-2 chars) are excluded from prefix expansion because
25
- // they produce too many false positives.
26
- const prefixQuery = buildPrefixQuery(ftsQuery);
27
- if (!prefixQuery)
28
- return [];
29
- return runFtsQuery(db, prefixQuery, limit, entryType, excludeTypes);
50
+ if (plan.exactPrefix) {
51
+ const prefixResults = runFtsQuery(db, plan.exactPrefix, "prefix", limit, entryType, excludeTypes);
52
+ if (prefixResults.length > 0)
53
+ return prefixResults;
54
+ }
55
+ // One measured relaxation only after both conjunctive forms miss. This is
56
+ // still the same FTS table, BM25 weights, candidate collection, and
57
+ // downstream ranker merely an OR candidate query for sentence-shaped
58
+ // input whose filler terms prevented a strict hit.
59
+ return plan.relaxed ? runFtsQuery(db, plan.relaxed, "relaxed", limit, entryType, excludeTypes) : [];
30
60
  }
31
- function runFtsQuery(db, ftsQuery, limit, entryType, excludeTypes) {
61
+ function runFtsQuery(db, ftsQuery, lexicalMatch, limit, entryType, excludeTypes) {
32
62
  // #627 — exclude-type clause. Only applies on the untyped ('any') path; an
33
63
  // explicit include filter (entryType) already narrows to a single type, so
34
64
  // exclusion is redundant there. An empty list skips the clause entirely
35
65
  // (never emit `NOT IN ()`, which is a SQL error / always-false).
36
66
  const excludes = excludeTypes && excludeTypes.length > 0 ? excludeTypes : [];
37
- // The typed and untyped paths differ ONLY by one WHERE clause (an entry_type
67
+ // The typed and untyped paths differ only by one `type` WHERE clause
38
68
  // equality vs. an optional NOT IN exclusion) and their param order — the
39
69
  // SELECT/JOIN/ORDER/LIMIT is shared, so build it once. Join on integer
40
70
  // entry_id directly (no CAST; we store integer). bm25() per-column weights:
@@ -42,16 +72,16 @@ function runFtsQuery(db, ftsQuery, limit, entryType, excludeTypes) {
42
72
  let filterClause;
43
73
  let params;
44
74
  if (entryType && entryType !== "any") {
45
- filterClause = "AND e.entry_type = ?";
75
+ filterClause = "AND e.type = ?";
46
76
  params = [ftsQuery, entryType, limit];
47
77
  }
48
78
  else {
49
- filterClause = excludes.length > 0 ? `AND e.entry_type NOT IN (${excludes.map(() => "?").join(", ")})` : "";
79
+ filterClause = excludes.length > 0 ? `AND e.type NOT IN (${excludes.map(() => "?").join(", ")})` : "";
50
80
  // Param order: MATCH, then the NOT IN values, then LIMIT.
51
81
  params = [ftsQuery, ...excludes, limit];
52
82
  }
53
83
  const sql = `
54
- SELECT e.id, e.file_path AS filePath, e.entry_json, e.search_text AS searchText,
84
+ SELECT e.id, e.file_path AS filePath, e.document_json AS documentJson, e.search_text AS searchText,
55
85
  e.item_ref AS itemRef, e.bundle_id AS bundleId, e.concept_id AS conceptId, e.adapter_id AS adapterId,
56
86
  bm25(entries_fts, 0, 10.0, 5.0, 3.0, 2.0, 1.0) AS bm25Score
57
87
  FROM entries_fts f
@@ -68,10 +98,10 @@ function runFtsQuery(db, ftsQuery, limit, entryType, excludeTypes) {
68
98
  for (const row of rows) {
69
99
  let entry;
70
100
  try {
71
- entry = JSON.parse(row.entry_json);
101
+ entry = JSON.parse(row.documentJson);
72
102
  }
73
103
  catch {
74
- warn(`[db] searchFts: skipping entry id=${row.id} — corrupt entry_json`);
104
+ warn(`[db] searchFts: skipping entry id=${row.id} — corrupt document_json`);
75
105
  continue;
76
106
  }
77
107
  results.push({
@@ -84,6 +114,7 @@ function runFtsQuery(db, ftsQuery, limit, entryType, excludeTypes) {
84
114
  bundleId: row.bundleId,
85
115
  conceptId: row.conceptId,
86
116
  adapterId: row.adapterId,
117
+ lexicalMatch,
87
118
  });
88
119
  }
89
120
  return results;
@@ -94,55 +125,26 @@ function runFtsQuery(db, ftsQuery, limit, entryType, excludeTypes) {
94
125
  }
95
126
  }
96
127
  /**
97
- * Rebuild the FTS5 search index.
98
- *
99
- * `incremental` (default `false`): when true, only rebuild rows that
100
- * `upsertEntry` marked dirty since the last `rebuildFts` call. The full path
101
- * (default) wipes `entries_fts` and re-inserts every row from `entries` —
102
- * appropriate for `akm index --full` and version-upgrade rebuilds.
103
- *
104
- * Both paths are wrapped in a single transaction so the FTS table is never
105
- * left in a half-rebuilt state.
128
+ * Explicitly rebuild the complete FTS5 projection from canonical entries.
129
+ * Ordinary entry mutations do not call this: `upsertEntry` and the delete
130
+ * operations publish their FTS state in the same transaction as `entries`.
131
+ * This remains a recovery/schema-verification primitive for regenerable
132
+ * `index.db` state.
106
133
  *
107
134
  * Skipped corrupt-JSON rows are aggregated into one warning instead of
108
135
  * spamming stderr per-entry.
109
136
  */
110
- export function rebuildFts(db, options) {
111
- const incremental = options?.incremental === true;
137
+ export function rebuildFts(db) {
112
138
  db.transaction(() => {
113
- let rows;
114
- if (incremental) {
115
- // Read the dirty queue and join against entries to get the JSON.
116
- // Then drop the matching rows from entries_fts so the INSERT below
117
- // doesn't double-up. The dirty list is drained at the end.
118
- rows = db
119
- .prepare(`SELECT e.id AS id, e.entry_json AS entry_json
120
- FROM entries_fts_dirty d
121
- JOIN entries e ON e.id = d.entry_id`)
122
- .all();
123
- if (rows.length === 0)
124
- return;
125
- const ids = rows.map((r) => r.id);
126
- // Delete only the dirty FTS rows — chunk to stay under
127
- // SQLITE_MAX_VARIABLE_NUMBER on large dirty queues.
128
- for (let i = 0; i < ids.length; i += SQLITE_CHUNK_SIZE) {
129
- const chunk = ids.slice(i, i + SQLITE_CHUNK_SIZE);
130
- const placeholders = chunk.map(() => "?").join(",");
131
- db.prepare(`DELETE FROM entries_fts WHERE entry_id IN (${placeholders})`).run(...chunk);
132
- }
133
- }
134
- else {
135
- // Full path: wipe and re-read every row.
136
- db.exec("DELETE FROM entries_fts");
137
- rows = db.prepare("SELECT id, entry_json FROM entries").all();
138
- }
139
- const insertStmt = db.prepare("INSERT INTO entries_fts (entry_id, name, description, tags, hints, content) VALUES (?, ?, ?, ?, ?, ?)");
139
+ db.exec("DELETE FROM entries_fts");
140
+ const rows = db.prepare("SELECT id, document_json FROM entries").all();
141
+ const insertStmt = db.prepare(INSERT_FTS_SQL);
140
142
  let skipped = 0;
141
143
  for (const row of rows) {
142
144
  let entry;
143
145
  let fields;
144
146
  try {
145
- entry = JSON.parse(row.entry_json);
147
+ entry = JSON.parse(row.document_json);
146
148
  fields = buildSearchFields(entry);
147
149
  }
148
150
  catch {
@@ -152,16 +154,7 @@ export function rebuildFts(db, options) {
152
154
  insertStmt.run(row.id, fields.name, fields.description, fields.tags, fields.hints, fields.content);
153
155
  }
154
156
  if (skipped > 0) {
155
- warn(`[db] rebuildFts: skipped ${skipped} entr${skipped === 1 ? "y" : "ies"} with invalid entry_json`);
157
+ warn(`[db] rebuildFts: skipped ${skipped} entr${skipped === 1 ? "y" : "ies"} with invalid document_json`);
156
158
  }
157
- // Always drain the dirty queue — both paths converge here. The
158
- // incremental path drains it because we just consumed every dirty row;
159
- // the full path drains it because a full rebuild covers everything the
160
- // dirty list tracks. The table is guaranteed to exist (created by
161
- // ensureSchema()).
162
- //
163
- // BUG-L1: previously the if/else arms ran identical statements — the
164
- // duplication has been collapsed.
165
- db.exec("DELETE FROM entries_fts_dirty");
166
159
  })();
167
160
  }
@@ -7,7 +7,6 @@
7
7
  * Owns the raw SQL for `llm_enrichment_cache` — the body-hash-keyed cache that
8
8
  * lets `akm index --enrich` skip the LLM call when a file's body is unchanged.
9
9
  */
10
- import { bestEffort } from "../../core/best-effort.js";
11
10
  import { sha256Hex } from "../../runtime.js";
12
11
  import { SQLITE_CHUNK_SIZE } from "./index-sql.js";
13
12
  /**
@@ -83,20 +82,16 @@ export function upsertLlmCacheEntry(db, assetRef, bodyHash, resultJson, cacheVar
83
82
  * `entries` table. Should be called during the cleanup phase of each index
84
83
  * run to prevent the cache from growing unboundedly as assets are removed.
85
84
  *
86
- * The join uses a LIKE match against the entries `file_path` column because
87
- * graph/memory cache refs are absolute file paths, while enrichment cache
88
- * refs are entry_key strings — we preserve any entry that still has a
89
- * corresponding row in either the entries table (by entry_key) or that
90
- * matches a live file_path.
85
+ * Graph/memory cache refs are absolute file paths, while metadata-enrichment
86
+ * refs use canonical `item_ref`; preserve a cache row that matches either
87
+ * current identity.
91
88
  */
92
89
  export function clearStaleCacheEntries(db) {
93
- bestEffort(() => {
94
- db.exec(`
95
- DELETE FROM llm_enrichment_cache
96
- WHERE asset_ref NOT IN (SELECT file_path FROM entries)
97
- AND asset_ref NOT IN (SELECT entry_key FROM entries)
98
- `);
99
- }, "llm_enrichment_cache may not exist in very old DBs opened without ensureSchema");
90
+ db.exec(`
91
+ DELETE FROM llm_enrichment_cache
92
+ WHERE asset_ref NOT IN (SELECT file_path FROM entries)
93
+ AND asset_ref NOT IN (SELECT item_ref FROM entries)
94
+ `);
100
95
  }
101
96
  /**
102
97
  * Compute a stable SHA-256 hex digest of a UTF-8 string. Used as the body_hash
@@ -1,14 +1,6 @@
1
1
  // This Source Code Form is subject to the terms of the Mozilla Public
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
- /**
5
- * `index.db` meta + per-directory index-state repository.
6
- *
7
- * Owns the raw SQL for `index_meta` (the key/value stamp table) and
8
- * `index_dir_state` (incremental-index bookkeeping). Lives in the storage
9
- * layer, not the indexer, so the storage layer owns these primitives.
10
- */
11
- import path from "node:path";
12
4
  // ── Meta helpers ────────────────────────────────────────────────────────────
13
5
  export function getMeta(db, key) {
14
6
  const row = db.prepare("SELECT value FROM index_meta WHERE key = ?").get(key);
@@ -55,6 +47,3 @@ export function upsertIndexDirState(db, state) {
55
47
  export function deleteIndexDirState(db, dirPath) {
56
48
  db.prepare("DELETE FROM index_dir_state WHERE dir_path = ?").run(dirPath);
57
49
  }
58
- export function deleteIndexDirStatesByStashDir(db, stashDir) {
59
- db.prepare("DELETE FROM index_dir_state WHERE dir_path = ? OR dir_path LIKE ?").run(stashDir, `${stashDir}${path.sep}%`);
60
- }