akm-cli 0.7.0 → 0.7.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 (327) hide show
  1. package/package.json +8 -8
  2. package/dist/tests/add-website-source.test.js +0 -119
  3. package/dist/tests/agent/agent-config-loader.test.js +0 -70
  4. package/dist/tests/agent/agent-config.test.js +0 -221
  5. package/dist/tests/agent/agent-detect.test.js +0 -100
  6. package/dist/tests/agent/agent-spawn.test.js +0 -234
  7. package/dist/tests/agent-output.test.js +0 -186
  8. package/dist/tests/architecture/agent-no-llm-sdk-guard.test.js +0 -103
  9. package/dist/tests/architecture/agent-spawn-seam.test.js +0 -193
  10. package/dist/tests/architecture/llm-stateless-seam.test.js +0 -112
  11. package/dist/tests/asset-ref.test.js +0 -192
  12. package/dist/tests/asset-registry.test.js +0 -103
  13. package/dist/tests/asset-spec.test.js +0 -241
  14. package/dist/tests/bench/attribution.test.js +0 -996
  15. package/dist/tests/bench/cleanup-sigint.test.js +0 -83
  16. package/dist/tests/bench/cleanup.js +0 -234
  17. package/dist/tests/bench/cleanup.test.js +0 -166
  18. package/dist/tests/bench/cli.js +0 -1018
  19. package/dist/tests/bench/cli.test.js +0 -445
  20. package/dist/tests/bench/compare.test.js +0 -556
  21. package/dist/tests/bench/corpus.js +0 -317
  22. package/dist/tests/bench/corpus.test.js +0 -258
  23. package/dist/tests/bench/doctor.js +0 -525
  24. package/dist/tests/bench/driver.js +0 -401
  25. package/dist/tests/bench/driver.test.js +0 -584
  26. package/dist/tests/bench/environment.js +0 -233
  27. package/dist/tests/bench/environment.test.js +0 -199
  28. package/dist/tests/bench/evolve-metrics.js +0 -179
  29. package/dist/tests/bench/evolve-metrics.test.js +0 -187
  30. package/dist/tests/bench/evolve.js +0 -647
  31. package/dist/tests/bench/evolve.test.js +0 -624
  32. package/dist/tests/bench/failure-modes.test.js +0 -349
  33. package/dist/tests/bench/feedback-integrity.test.js +0 -457
  34. package/dist/tests/bench/leakage.test.js +0 -228
  35. package/dist/tests/bench/learning-curve.test.js +0 -134
  36. package/dist/tests/bench/metrics.js +0 -2395
  37. package/dist/tests/bench/metrics.test.js +0 -1150
  38. package/dist/tests/bench/no-os-tmpdir-invariant.test.js +0 -43
  39. package/dist/tests/bench/opencode-config.js +0 -194
  40. package/dist/tests/bench/opencode-config.test.js +0 -370
  41. package/dist/tests/bench/report.js +0 -1885
  42. package/dist/tests/bench/report.test.js +0 -1038
  43. package/dist/tests/bench/run-config.js +0 -355
  44. package/dist/tests/bench/run-config.test.js +0 -298
  45. package/dist/tests/bench/run-curate-test.js +0 -32
  46. package/dist/tests/bench/run-failing-tasks.js +0 -56
  47. package/dist/tests/bench/run-full-bench.js +0 -51
  48. package/dist/tests/bench/run-items36-targeted.js +0 -69
  49. package/dist/tests/bench/run-nano-quick.js +0 -42
  50. package/dist/tests/bench/run-waveg-targeted.js +0 -62
  51. package/dist/tests/bench/runner.js +0 -699
  52. package/dist/tests/bench/runner.test.js +0 -958
  53. package/dist/tests/bench/search-bridge.test.js +0 -331
  54. package/dist/tests/bench/tmp.js +0 -131
  55. package/dist/tests/bench/trajectory.js +0 -116
  56. package/dist/tests/bench/trajectory.test.js +0 -127
  57. package/dist/tests/bench/verifier.js +0 -114
  58. package/dist/tests/bench/verifier.test.js +0 -118
  59. package/dist/tests/bench/workflow-evaluator.js +0 -557
  60. package/dist/tests/bench/workflow-evaluator.test.js +0 -421
  61. package/dist/tests/bench/workflow-spec.js +0 -345
  62. package/dist/tests/bench/workflow-spec.test.js +0 -363
  63. package/dist/tests/bench/workflow-trace.js +0 -472
  64. package/dist/tests/bench/workflow-trace.test.js +0 -254
  65. package/dist/tests/benchmark-search-quality.js +0 -536
  66. package/dist/tests/benchmark-suite.js +0 -1441
  67. package/dist/tests/capture-cli.test.js +0 -112
  68. package/dist/tests/cli-errors.test.js +0 -204
  69. package/dist/tests/commands/events.test.js +0 -370
  70. package/dist/tests/commands/history.test.js +0 -418
  71. package/dist/tests/commands/import.test.js +0 -103
  72. package/dist/tests/commands/proposal-cli.test.js +0 -209
  73. package/dist/tests/commands/reflect-propose-cli.test.js +0 -333
  74. package/dist/tests/commands/remember.test.js +0 -97
  75. package/dist/tests/commands/scope-flags.test.js +0 -300
  76. package/dist/tests/commands/search.test.js +0 -537
  77. package/dist/tests/commands/show-indexer-parity.test.js +0 -117
  78. package/dist/tests/commands/show.test.js +0 -294
  79. package/dist/tests/common.test.js +0 -266
  80. package/dist/tests/completions.test.js +0 -142
  81. package/dist/tests/config-cli.test.js +0 -193
  82. package/dist/tests/config-llm-features.test.js +0 -139
  83. package/dist/tests/config.test.js +0 -569
  84. package/dist/tests/contracts/migration-baseline.test.js +0 -43
  85. package/dist/tests/contracts/reflect-propose-envelope.test.js +0 -139
  86. package/dist/tests/contracts/spec-helpers.js +0 -46
  87. package/dist/tests/contracts/v1-spec-section-11-proposal-queue.test.js +0 -228
  88. package/dist/tests/contracts/v1-spec-section-12-agent-config.test.js +0 -56
  89. package/dist/tests/contracts/v1-spec-section-13-lesson-type.test.js +0 -34
  90. package/dist/tests/contracts/v1-spec-section-14-llm-features.test.js +0 -94
  91. package/dist/tests/contracts/v1-spec-section-4-1-asset-types.test.js +0 -39
  92. package/dist/tests/contracts/v1-spec-section-4-2-quality-rules.test.js +0 -44
  93. package/dist/tests/contracts/v1-spec-section-5-configuration.test.js +0 -47
  94. package/dist/tests/contracts/v1-spec-section-6-orchestration.test.js +0 -40
  95. package/dist/tests/contracts/v1-spec-section-7-module-layout.test.js +0 -58
  96. package/dist/tests/contracts/v1-spec-section-8-extension-points.test.js +0 -34
  97. package/dist/tests/contracts/v1-spec-section-9-4-cli-surface.test.js +0 -75
  98. package/dist/tests/contracts/v1-spec-section-9-7-llm-agent-boundary.test.js +0 -36
  99. package/dist/tests/core/write-source.test.js +0 -366
  100. package/dist/tests/curate-command.test.js +0 -87
  101. package/dist/tests/db-scoring.test.js +0 -201
  102. package/dist/tests/db.test.js +0 -654
  103. package/dist/tests/distill-cli-flag.test.js +0 -208
  104. package/dist/tests/distill.test.js +0 -515
  105. package/dist/tests/docker-install.test.js +0 -120
  106. package/dist/tests/e2e.test.js +0 -1419
  107. package/dist/tests/embedder.test.js +0 -340
  108. package/dist/tests/embedding-model-config.test.js +0 -379
  109. package/dist/tests/feedback-command.test.js +0 -172
  110. package/dist/tests/file-context.test.js +0 -552
  111. package/dist/tests/fixtures/scripts/git/summarize-diff.js +0 -9
  112. package/dist/tests/fixtures/scripts/lint/eslint-check.js +0 -7
  113. package/dist/tests/fixtures/stashes/load.js +0 -166
  114. package/dist/tests/fixtures/stashes/load.test.js +0 -97
  115. package/dist/tests/fixtures/stashes/ranking-baseline/scripts/mem0-search.js +0 -12
  116. package/dist/tests/frontmatter.test.js +0 -190
  117. package/dist/tests/fts-field-weighting.test.js +0 -254
  118. package/dist/tests/fuzzy-search.test.js +0 -230
  119. package/dist/tests/git-provider-clone.test.js +0 -45
  120. package/dist/tests/github.test.js +0 -161
  121. package/dist/tests/graph-boost-ranking.test.js +0 -305
  122. package/dist/tests/graph-extraction.test.js +0 -282
  123. package/dist/tests/helpers/usage-events.js +0 -8
  124. package/dist/tests/index-pass-llm.test.js +0 -161
  125. package/dist/tests/indexer.test.js +0 -570
  126. package/dist/tests/info-command.test.js +0 -166
  127. package/dist/tests/init.test.js +0 -69
  128. package/dist/tests/install-script.test.js +0 -246
  129. package/dist/tests/integration/agent-real-profile.test.js +0 -94
  130. package/dist/tests/issue-36-repro.test.js +0 -304
  131. package/dist/tests/issues-191-194.test.js +0 -160
  132. package/dist/tests/lesson-lint.test.js +0 -111
  133. package/dist/tests/llm-client.test.js +0 -115
  134. package/dist/tests/llm-feature-gate.test.js +0 -151
  135. package/dist/tests/llm.test.js +0 -139
  136. package/dist/tests/lockfile.test.js +0 -216
  137. package/dist/tests/manifest.test.js +0 -205
  138. package/dist/tests/markdown.test.js +0 -126
  139. package/dist/tests/matchers-unit.test.js +0 -189
  140. package/dist/tests/memory-inference.test.js +0 -299
  141. package/dist/tests/merge-scoring.test.js +0 -136
  142. package/dist/tests/metadata.test.js +0 -313
  143. package/dist/tests/migration-help.test.js +0 -89
  144. package/dist/tests/origin-resolve.test.js +0 -124
  145. package/dist/tests/output-baseline.test.js +0 -218
  146. package/dist/tests/output-shapes-unit.test.js +0 -478
  147. package/dist/tests/parallel-search.test.js +0 -272
  148. package/dist/tests/parameter-metadata.test.js +0 -365
  149. package/dist/tests/paths.test.js +0 -177
  150. package/dist/tests/progressive-disclosure.test.js +0 -280
  151. package/dist/tests/proposals.test.js +0 -279
  152. package/dist/tests/proposed-quality.test.js +0 -271
  153. package/dist/tests/provider-registry.test.js +0 -32
  154. package/dist/tests/ranking-regression.test.js +0 -548
  155. package/dist/tests/reflect-propose.test.js +0 -455
  156. package/dist/tests/registry-build-index.test.js +0 -394
  157. package/dist/tests/registry-cli.test.js +0 -290
  158. package/dist/tests/registry-index-v2.test.js +0 -430
  159. package/dist/tests/registry-install.test.js +0 -728
  160. package/dist/tests/registry-providers/parity.test.js +0 -189
  161. package/dist/tests/registry-providers/skills-sh.test.js +0 -309
  162. package/dist/tests/registry-providers/static-index.test.js +0 -238
  163. package/dist/tests/registry-resolve.test.js +0 -126
  164. package/dist/tests/registry-search.test.js +0 -923
  165. package/dist/tests/remember-frontmatter.test.js +0 -378
  166. package/dist/tests/remember-unit.test.js +0 -123
  167. package/dist/tests/ripgrep-install.test.js +0 -251
  168. package/dist/tests/ripgrep-resolve.test.js +0 -108
  169. package/dist/tests/ripgrep.test.js +0 -163
  170. package/dist/tests/save-command.test.js +0 -94
  171. package/dist/tests/save-trust-qa-fixes.test.js +0 -270
  172. package/dist/tests/scoring-pipeline.test.js +0 -648
  173. package/dist/tests/search-include-proposed-cli.test.js +0 -118
  174. package/dist/tests/self-update.test.js +0 -442
  175. package/dist/tests/semantic-search-e2e.test.js +0 -512
  176. package/dist/tests/semantic-status.test.js +0 -471
  177. package/dist/tests/setup-run.integration.js +0 -877
  178. package/dist/tests/setup-wizard.test.js +0 -198
  179. package/dist/tests/setup.test.js +0 -131
  180. package/dist/tests/source-add.test.js +0 -11
  181. package/dist/tests/source-clone.test.js +0 -254
  182. package/dist/tests/source-manage.test.js +0 -366
  183. package/dist/tests/source-providers/filesystem.test.js +0 -82
  184. package/dist/tests/source-providers/git.test.js +0 -252
  185. package/dist/tests/source-providers/website.test.js +0 -128
  186. package/dist/tests/source-qa-fixes.test.js +0 -286
  187. package/dist/tests/source-registry.test.js +0 -350
  188. package/dist/tests/source-resolve.test.js +0 -100
  189. package/dist/tests/source-source.test.js +0 -281
  190. package/dist/tests/source.test.js +0 -533
  191. package/dist/tests/tar-utils-scan.test.js +0 -73
  192. package/dist/tests/toggle-components.test.js +0 -73
  193. package/dist/tests/usage-telemetry.test.js +0 -265
  194. package/dist/tests/utility-scoring.test.js +0 -558
  195. package/dist/tests/vault-load-error.test.js +0 -78
  196. package/dist/tests/vault-qa-fixes.test.js +0 -194
  197. package/dist/tests/vault.test.js +0 -429
  198. package/dist/tests/vector-search.test.js +0 -608
  199. package/dist/tests/walker.test.js +0 -252
  200. package/dist/tests/wave2-cluster-bc.test.js +0 -228
  201. package/dist/tests/wave2-cluster-d.test.js +0 -180
  202. package/dist/tests/wave2-cluster-e.test.js +0 -179
  203. package/dist/tests/wiki-qa-fixes.test.js +0 -270
  204. package/dist/tests/wiki.test.js +0 -529
  205. package/dist/tests/workflow-cli.test.js +0 -271
  206. package/dist/tests/workflow-markdown.test.js +0 -171
  207. package/dist/tests/workflow-path-escape.test.js +0 -132
  208. package/dist/tests/workflow-qa-fixes.test.js +0 -395
  209. package/dist/tests/workflows/indexer-rejection.test.js +0 -213
  210. /package/dist/{src/cli.js → cli.js} +0 -0
  211. /package/dist/{src/commands → commands}/completions.js +0 -0
  212. /package/dist/{src/commands → commands}/config-cli.js +0 -0
  213. /package/dist/{src/commands → commands}/curate.js +0 -0
  214. /package/dist/{src/commands → commands}/distill.js +0 -0
  215. /package/dist/{src/commands → commands}/events.js +0 -0
  216. /package/dist/{src/commands → commands}/history.js +0 -0
  217. /package/dist/{src/commands → commands}/info.js +0 -0
  218. /package/dist/{src/commands → commands}/init.js +0 -0
  219. /package/dist/{src/commands → commands}/install-audit.js +0 -0
  220. /package/dist/{src/commands → commands}/installed-stashes.js +0 -0
  221. /package/dist/{src/commands → commands}/migration-help.js +0 -0
  222. /package/dist/{src/commands → commands}/proposal.js +0 -0
  223. /package/dist/{src/commands → commands}/propose.js +0 -0
  224. /package/dist/{src/commands → commands}/reflect.js +0 -0
  225. /package/dist/{src/commands → commands}/registry-search.js +0 -0
  226. /package/dist/{src/commands → commands}/remember.js +0 -0
  227. /package/dist/{src/commands → commands}/search.js +0 -0
  228. /package/dist/{src/commands → commands}/self-update.js +0 -0
  229. /package/dist/{src/commands → commands}/show.js +0 -0
  230. /package/dist/{src/commands → commands}/source-add.js +0 -0
  231. /package/dist/{src/commands → commands}/source-clone.js +0 -0
  232. /package/dist/{src/commands → commands}/source-manage.js +0 -0
  233. /package/dist/{src/commands → commands}/vault.js +0 -0
  234. /package/dist/{src/core → core}/asset-ref.js +0 -0
  235. /package/dist/{src/core → core}/asset-registry.js +0 -0
  236. /package/dist/{src/core → core}/asset-spec.js +0 -0
  237. /package/dist/{src/core → core}/common.js +0 -0
  238. /package/dist/{src/core → core}/config.js +0 -0
  239. /package/dist/{src/core → core}/errors.js +0 -0
  240. /package/dist/{src/core → core}/events.js +0 -0
  241. /package/dist/{src/core → core}/frontmatter.js +0 -0
  242. /package/dist/{src/core → core}/lesson-lint.js +0 -0
  243. /package/dist/{src/core → core}/markdown.js +0 -0
  244. /package/dist/{src/core → core}/paths.js +0 -0
  245. /package/dist/{src/core → core}/proposals.js +0 -0
  246. /package/dist/{src/core → core}/warn.js +0 -0
  247. /package/dist/{src/core → core}/write-source.js +0 -0
  248. /package/dist/{src/indexer → indexer}/db-search.js +0 -0
  249. /package/dist/{src/indexer → indexer}/db.js +0 -0
  250. /package/dist/{src/indexer → indexer}/file-context.js +0 -0
  251. /package/dist/{src/indexer → indexer}/graph-boost.js +0 -0
  252. /package/dist/{src/indexer → indexer}/graph-extraction.js +0 -0
  253. /package/dist/{src/indexer → indexer}/indexer.js +0 -0
  254. /package/dist/{src/indexer → indexer}/manifest.js +0 -0
  255. /package/dist/{src/indexer → indexer}/matchers.js +0 -0
  256. /package/dist/{src/indexer → indexer}/memory-inference.js +0 -0
  257. /package/dist/{src/indexer → indexer}/metadata.js +0 -0
  258. /package/dist/{src/indexer → indexer}/search-fields.js +0 -0
  259. /package/dist/{src/indexer → indexer}/search-source.js +0 -0
  260. /package/dist/{src/indexer → indexer}/semantic-status.js +0 -0
  261. /package/dist/{src/indexer → indexer}/usage-events.js +0 -0
  262. /package/dist/{src/indexer → indexer}/walker.js +0 -0
  263. /package/dist/{src/integrations → integrations}/agent/config.js +0 -0
  264. /package/dist/{src/integrations → integrations}/agent/detect.js +0 -0
  265. /package/dist/{src/integrations → integrations}/agent/index.js +0 -0
  266. /package/dist/{src/integrations → integrations}/agent/profiles.js +0 -0
  267. /package/dist/{src/integrations → integrations}/agent/prompts.js +0 -0
  268. /package/dist/{src/integrations → integrations}/agent/spawn.js +0 -0
  269. /package/dist/{src/integrations → integrations}/github.js +0 -0
  270. /package/dist/{src/integrations → integrations}/lockfile.js +0 -0
  271. /package/dist/{src/llm → llm}/client.js +0 -0
  272. /package/dist/{src/llm → llm}/embedder.js +0 -0
  273. /package/dist/{src/llm → llm}/embedders/cache.js +0 -0
  274. /package/dist/{src/llm → llm}/embedders/local.js +0 -0
  275. /package/dist/{src/llm → llm}/embedders/remote.js +0 -0
  276. /package/dist/{src/llm → llm}/embedders/types.js +0 -0
  277. /package/dist/{src/llm → llm}/feature-gate.js +0 -0
  278. /package/dist/{src/llm → llm}/graph-extract.js +0 -0
  279. /package/dist/{src/llm → llm}/index-passes.js +0 -0
  280. /package/dist/{src/llm → llm}/memory-infer.js +0 -0
  281. /package/dist/{src/llm → llm}/metadata-enhance.js +0 -0
  282. /package/dist/{src/output → output}/cli-hints.js +0 -0
  283. /package/dist/{src/output → output}/context.js +0 -0
  284. /package/dist/{src/output → output}/renderers.js +0 -0
  285. /package/dist/{src/output → output}/shapes.js +0 -0
  286. /package/dist/{src/output → output}/text.js +0 -0
  287. /package/dist/{src/registry → registry}/build-index.js +0 -0
  288. /package/dist/{src/registry → registry}/create-provider-registry.js +0 -0
  289. /package/dist/{src/registry → registry}/factory.js +0 -0
  290. /package/dist/{src/registry → registry}/origin-resolve.js +0 -0
  291. /package/dist/{src/registry → registry}/providers/index.js +0 -0
  292. /package/dist/{src/registry → registry}/providers/skills-sh.js +0 -0
  293. /package/dist/{src/registry → registry}/providers/static-index.js +0 -0
  294. /package/dist/{src/registry → registry}/providers/types.js +0 -0
  295. /package/dist/{src/registry → registry}/resolve.js +0 -0
  296. /package/dist/{src/registry → registry}/types.js +0 -0
  297. /package/dist/{src/setup → setup}/detect.js +0 -0
  298. /package/dist/{src/setup → setup}/ripgrep-install.js +0 -0
  299. /package/dist/{src/setup → setup}/ripgrep-resolve.js +0 -0
  300. /package/dist/{src/setup → setup}/setup.js +0 -0
  301. /package/dist/{src/setup → setup}/steps.js +0 -0
  302. /package/dist/{src/sources → sources}/include.js +0 -0
  303. /package/dist/{src/sources → sources}/provider-factory.js +0 -0
  304. /package/dist/{src/sources → sources}/provider.js +0 -0
  305. /package/dist/{src/sources → sources}/providers/filesystem.js +0 -0
  306. /package/dist/{src/sources → sources}/providers/git.js +0 -0
  307. /package/dist/{src/sources → sources}/providers/index.js +0 -0
  308. /package/dist/{src/sources → sources}/providers/install-types.js +0 -0
  309. /package/dist/{src/sources → sources}/providers/npm.js +0 -0
  310. /package/dist/{src/sources → sources}/providers/provider-utils.js +0 -0
  311. /package/dist/{src/sources → sources}/providers/sync-from-ref.js +0 -0
  312. /package/dist/{src/sources → sources}/providers/tar-utils.js +0 -0
  313. /package/dist/{src/sources → sources}/providers/website.js +0 -0
  314. /package/dist/{src/sources → sources}/resolve.js +0 -0
  315. /package/dist/{src/sources → sources}/types.js +0 -0
  316. /package/dist/{src/templates → templates}/wiki-templates.js +0 -0
  317. /package/dist/{src/version.js → version.js} +0 -0
  318. /package/dist/{src/wiki → wiki}/wiki.js +0 -0
  319. /package/dist/{src/workflows → workflows}/authoring.js +0 -0
  320. /package/dist/{src/workflows → workflows}/cli.js +0 -0
  321. /package/dist/{src/workflows → workflows}/db.js +0 -0
  322. /package/dist/{src/workflows → workflows}/document-cache.js +0 -0
  323. /package/dist/{src/workflows → workflows}/parser.js +0 -0
  324. /package/dist/{src/workflows → workflows}/renderer.js +0 -0
  325. /package/dist/{src/workflows → workflows}/runs.js +0 -0
  326. /package/dist/{src/workflows → workflows}/schema.js +0 -0
  327. /package/dist/{src/workflows → workflows}/validator.js +0 -0
@@ -1,654 +0,0 @@
1
- import { afterAll, afterEach, beforeEach, describe, expect, test } from "bun:test";
2
- import fs from "node:fs";
3
- import os from "node:os";
4
- import path from "node:path";
5
- import { closeDatabase, DB_VERSION, deleteEntriesByDir, getAllEntries, getEntriesByDir, getEntryById, getEntryCount, getMeta, isVecAvailable, openDatabase, rebuildFts, searchFts, searchVec, setMeta, upsertEmbedding, upsertEntry, } from "../src/indexer/db";
6
- // ── Temp directory management ───────────────────────────────────────────────
7
- const createdTmpDirs = [];
8
- function tmpDir(label = "db") {
9
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), `akm-${label}-`));
10
- createdTmpDirs.push(dir);
11
- return dir;
12
- }
13
- function tmpDbPath(label = "db") {
14
- const dir = tmpDir(label);
15
- return path.join(dir, "test.db");
16
- }
17
- afterAll(() => {
18
- for (const dir of createdTmpDirs) {
19
- fs.rmSync(dir, { recursive: true, force: true });
20
- }
21
- });
22
- // ── Environment isolation ───────────────────────────────────────────────────
23
- const savedEnv = {};
24
- beforeEach(() => {
25
- savedEnv.XDG_CACHE_HOME = process.env.XDG_CACHE_HOME;
26
- savedEnv.XDG_CONFIG_HOME = process.env.XDG_CONFIG_HOME;
27
- process.env.XDG_CACHE_HOME = tmpDir("cache");
28
- process.env.XDG_CONFIG_HOME = tmpDir("config");
29
- });
30
- afterEach(() => {
31
- for (const [key, val] of Object.entries(savedEnv)) {
32
- if (val === undefined) {
33
- delete process.env[key];
34
- }
35
- else {
36
- process.env[key] = val;
37
- }
38
- }
39
- });
40
- // ── Helpers ─────────────────────────────────────────────────────────────────
41
- function makeEntry(overrides) {
42
- return {
43
- description: "A test entry",
44
- ...overrides,
45
- };
46
- }
47
- function insertTestEntry(db, key, opts) {
48
- const type = opts?.type ?? "script";
49
- const entry = makeEntry({ name: key, type, description: opts?.description ?? `Description for ${key}` });
50
- return upsertEntry(db, key, opts?.dirPath ?? "/test/dir", opts?.filePath ?? `/test/dir/${key}.ts`, opts?.stashDir ?? "/test/stash", entry, opts?.searchText ?? `${key} ${entry.description}`);
51
- }
52
- // ── Section 1.1: Schema ────────────────────────────────────────────────────
53
- describe("Schema", () => {
54
- test("openDatabase creates schema with correct version", () => {
55
- const dbPath = tmpDbPath();
56
- const db = openDatabase(dbPath);
57
- try {
58
- expect(getMeta(db, "version")).toBe(String(DB_VERSION));
59
- }
60
- finally {
61
- closeDatabase(db);
62
- }
63
- });
64
- test("openDatabase with mismatched version drops and recreates tables", () => {
65
- const dbPath = tmpDbPath();
66
- // Open and insert some data, then tamper with the version
67
- let db = openDatabase(dbPath);
68
- insertTestEntry(db, "old-entry");
69
- expect(getEntryCount(db)).toBe(1);
70
- setMeta(db, "version", "0");
71
- closeDatabase(db);
72
- // Reopen — should detect mismatch, drop tables, recreate
73
- db = openDatabase(dbPath);
74
- try {
75
- expect(getMeta(db, "version")).toBe(String(DB_VERSION));
76
- expect(getEntryCount(db)).toBe(0);
77
- }
78
- finally {
79
- closeDatabase(db);
80
- }
81
- });
82
- test("openDatabase creates FTS5 table", () => {
83
- const dbPath = tmpDbPath();
84
- const db = openDatabase(dbPath);
85
- try {
86
- const row = db.prepare("SELECT name FROM sqlite_master WHERE name = 'entries_fts'").get();
87
- expect(row).toBeDefined();
88
- expect(row?.name).toBe("entries_fts");
89
- }
90
- finally {
91
- closeDatabase(db);
92
- }
93
- });
94
- test("isVecAvailable returns true when sqlite-vec is installed", () => {
95
- const dbPath = tmpDbPath();
96
- const db = openDatabase(dbPath);
97
- try {
98
- expect(isVecAvailable(db)).toBe(true);
99
- }
100
- finally {
101
- closeDatabase(db);
102
- }
103
- });
104
- test("embeddingDim is stored and triggers vec table recreation", () => {
105
- const dbPath = tmpDbPath();
106
- let db = openDatabase(dbPath, { embeddingDim: 512 });
107
- try {
108
- if (isVecAvailable(db)) {
109
- expect(getMeta(db, "embeddingDim")).toBe("512");
110
- }
111
- }
112
- finally {
113
- closeDatabase(db);
114
- }
115
- // Reopen with a different dimension
116
- db = openDatabase(dbPath, { embeddingDim: 768 });
117
- try {
118
- if (isVecAvailable(db)) {
119
- expect(getMeta(db, "embeddingDim")).toBe("768");
120
- }
121
- }
122
- finally {
123
- closeDatabase(db);
124
- }
125
- });
126
- });
127
- // ── Section 1.2: Entry CRUD ────────────────────────────────────────────────
128
- describe("Entry CRUD", () => {
129
- test("upsertEntry inserts a new entry and returns its id", () => {
130
- const db = openDatabase(tmpDbPath());
131
- try {
132
- const id = insertTestEntry(db, "my-tool");
133
- expect(id).toBeGreaterThan(0);
134
- expect(getEntryCount(db)).toBe(1);
135
- }
136
- finally {
137
- closeDatabase(db);
138
- }
139
- });
140
- test("upsertEntry updates on conflict (same entry_key)", () => {
141
- const db = openDatabase(tmpDbPath());
142
- try {
143
- insertTestEntry(db, "my-tool", { description: "original description" });
144
- expect(getEntryCount(db)).toBe(1);
145
- // Upsert with updated description
146
- insertTestEntry(db, "my-tool", { description: "updated description" });
147
- expect(getEntryCount(db)).toBe(1);
148
- // Verify the entry reflects the update
149
- const entries = getAllEntries(db);
150
- expect(entries).toHaveLength(1);
151
- expect(entries[0].entry.description).toBe("updated description");
152
- }
153
- finally {
154
- closeDatabase(db);
155
- }
156
- });
157
- test("getEntryById returns the entry or undefined", () => {
158
- const db = openDatabase(tmpDbPath());
159
- try {
160
- const id = insertTestEntry(db, "fetch-tool", { description: "Fetches data" });
161
- const result = getEntryById(db, id);
162
- expect(result).toBeDefined();
163
- expect(result?.entry.name).toBe("fetch-tool");
164
- expect(result?.entry.description).toBe("Fetches data");
165
- expect(result?.filePath).toBe("/test/dir/fetch-tool.ts");
166
- // Non-existent ID
167
- const missing = getEntryById(db, 99999);
168
- expect(missing).toBeUndefined();
169
- }
170
- finally {
171
- closeDatabase(db);
172
- }
173
- });
174
- test("getEntriesByDir returns entries for a directory", () => {
175
- const db = openDatabase(tmpDbPath());
176
- try {
177
- insertTestEntry(db, "tool-a", { dirPath: "/project/alpha" });
178
- insertTestEntry(db, "tool-b", { dirPath: "/project/alpha" });
179
- insertTestEntry(db, "tool-c", { dirPath: "/project/beta" });
180
- const alphaEntries = getEntriesByDir(db, "/project/alpha");
181
- expect(alphaEntries).toHaveLength(2);
182
- const keys = alphaEntries.map((e) => e.entryKey).sort();
183
- expect(keys).toEqual(["tool-a", "tool-b"]);
184
- const betaEntries = getEntriesByDir(db, "/project/beta");
185
- expect(betaEntries).toHaveLength(1);
186
- expect(betaEntries[0].entryKey).toBe("tool-c");
187
- }
188
- finally {
189
- closeDatabase(db);
190
- }
191
- });
192
- test("getAllEntries returns all entries", () => {
193
- const db = openDatabase(tmpDbPath());
194
- try {
195
- insertTestEntry(db, "entry-1");
196
- insertTestEntry(db, "entry-2");
197
- insertTestEntry(db, "entry-3");
198
- const all = getAllEntries(db);
199
- expect(all).toHaveLength(3);
200
- }
201
- finally {
202
- closeDatabase(db);
203
- }
204
- });
205
- test("getAllEntries with type filter", () => {
206
- const db = openDatabase(tmpDbPath());
207
- try {
208
- insertTestEntry(db, "script-1", { type: "script" });
209
- insertTestEntry(db, "script-2", { type: "script" });
210
- insertTestEntry(db, "skill-1", { type: "skill" });
211
- const scripts = getAllEntries(db, "script");
212
- expect(scripts).toHaveLength(2);
213
- for (const t of scripts) {
214
- expect(t.entry.type).toBe("script");
215
- }
216
- const skills = getAllEntries(db, "skill");
217
- expect(skills).toHaveLength(1);
218
- expect(skills[0].entry.type).toBe("skill");
219
- }
220
- finally {
221
- closeDatabase(db);
222
- }
223
- });
224
- test("deleteEntriesByDir removes entries", () => {
225
- const db = openDatabase(tmpDbPath());
226
- try {
227
- insertTestEntry(db, "del-1", { dirPath: "/to-delete" });
228
- insertTestEntry(db, "del-2", { dirPath: "/to-delete" });
229
- insertTestEntry(db, "keep-1", { dirPath: "/to-keep" });
230
- expect(getEntryCount(db)).toBe(3);
231
- deleteEntriesByDir(db, "/to-delete");
232
- expect(getEntryCount(db)).toBe(1);
233
- const remaining = getAllEntries(db);
234
- expect(remaining[0].entryKey).toBe("keep-1");
235
- }
236
- finally {
237
- closeDatabase(db);
238
- }
239
- });
240
- });
241
- // ── Section 1.3: FTS search ────────────────────────────────────────────────
242
- describe("FTS search", () => {
243
- test("searchFts returns results ranked by BM25", () => {
244
- const db = openDatabase(tmpDbPath());
245
- try {
246
- insertTestEntry(db, "deploy-tool", {
247
- description: "Deploy applications to production servers",
248
- searchText: "deploy deploy deploy applications production servers deployment",
249
- });
250
- insertTestEntry(db, "infra-tool", {
251
- description: "Cloud infrastructure for deploy pipelines",
252
- searchText: "cloud infrastructure management scaling networking deploy pipelines automation",
253
- });
254
- rebuildFts(db);
255
- const results = searchFts(db, "deploy", 10);
256
- expect(results.length).toBe(2);
257
- expect(results[0].entry.name).toBe("deploy-tool");
258
- expect(results[1].entry.name).toBe("infra-tool");
259
- }
260
- finally {
261
- closeDatabase(db);
262
- }
263
- });
264
- test("searchFts with type filter", () => {
265
- const db = openDatabase(tmpDbPath());
266
- try {
267
- insertTestEntry(db, "build-script", {
268
- type: "script",
269
- description: "Build the project",
270
- searchText: "build project compilation",
271
- });
272
- insertTestEntry(db, "build-skill", {
273
- type: "skill",
274
- description: "Build pipeline skill",
275
- searchText: "build pipeline skill compilation",
276
- });
277
- rebuildFts(db);
278
- const scriptResults = searchFts(db, "build", 10, "script");
279
- expect(scriptResults).toHaveLength(1);
280
- expect(scriptResults[0].entry.type).toBe("script");
281
- const allResults = searchFts(db, "build", 10);
282
- expect(allResults).toHaveLength(2);
283
- }
284
- finally {
285
- closeDatabase(db);
286
- }
287
- });
288
- test("searchFts sanitizes query tokens", () => {
289
- const db = openDatabase(tmpDbPath());
290
- try {
291
- insertTestEntry(db, "hello-tool", {
292
- description: "hello world 123 greeting",
293
- searchText: "hello world 123 greeting",
294
- });
295
- rebuildFts(db);
296
- // Should not throw a SQL error despite special characters
297
- const results = searchFts(db, "hello! world@123", 10);
298
- expect(results[0].entry.name).toBe("hello-tool");
299
- // "hello" and "world" and "123" are valid tokens after sanitization
300
- expect(results.length).toBeGreaterThanOrEqual(1);
301
- }
302
- finally {
303
- closeDatabase(db);
304
- }
305
- });
306
- test("searchFts returns empty for garbage query", () => {
307
- const db = openDatabase(tmpDbPath());
308
- try {
309
- insertTestEntry(db, "some-tool", { searchText: "some useful tool" });
310
- rebuildFts(db);
311
- const results = searchFts(db, "!@#$%", 10);
312
- expect(results).toEqual([]);
313
- }
314
- finally {
315
- closeDatabase(db);
316
- }
317
- });
318
- // ── T5: sanitizeFtsQuery edge cases ──────────────────────────────────────
319
- // sanitizeFtsQuery is private, so we test it indirectly through searchFts.
320
- test("query that becomes empty after sanitization returns no results", () => {
321
- const db = openDatabase(tmpDbPath());
322
- try {
323
- insertTestEntry(db, "target", { searchText: "some useful content" });
324
- rebuildFts(db);
325
- // "! @" contains only non-alphanumeric chars; after sanitization all
326
- // tokens are stripped, leaving an empty FTS query.
327
- const results = searchFts(db, "! @", 10);
328
- expect(results).toEqual([]);
329
- }
330
- finally {
331
- closeDatabase(db);
332
- }
333
- });
334
- test("query with only 1-character tokens returns no results when content has no matching single-char terms", () => {
335
- const db = openDatabase(tmpDbPath());
336
- try {
337
- insertTestEntry(db, "abc-tool", { searchText: "alpha bravo charlie" });
338
- rebuildFts(db);
339
- // "a b c" — single-char tokens are passed to FTS5 but don't match
340
- // "alpha", "bravo", "charlie" because FTS5 doesn't do prefix matching.
341
- const results = searchFts(db, "a b c", 10);
342
- expect(results).toEqual([]);
343
- }
344
- finally {
345
- closeDatabase(db);
346
- }
347
- });
348
- test("FTS5 syntax injection is neutralized", () => {
349
- const db = openDatabase(tmpDbPath());
350
- try {
351
- insertTestEntry(db, "foo-tool", { description: "foo bar baz", searchText: "foo bar baz" });
352
- insertTestEntry(db, "bar-tool", { description: "bar qux quux", searchText: "bar qux quux" });
353
- rebuildFts(db);
354
- // "NEAR(foo, bar)" is raw FTS5 syntax that should be sanitized.
355
- // After sanitization, syntax chars and NEAR are stripped, leaving
356
- // tokens "foo" "bar" (implicit AND) — should not throw and should
357
- // return matches containing both foo and bar.
358
- const results = searchFts(db, "NEAR(foo, bar)", 10);
359
- expect(results.length).toBeGreaterThanOrEqual(1);
360
- // foo-tool has both "foo" and "bar" in its search text
361
- const names = results.map((r) => r.entry.name);
362
- expect(names).toContain("foo-tool");
363
- }
364
- finally {
365
- closeDatabase(db);
366
- }
367
- });
368
- test("normal multi-word query returns correct results", () => {
369
- const db = openDatabase(tmpDbPath());
370
- try {
371
- insertTestEntry(db, "deploy-prod", {
372
- description: "deploy application production servers",
373
- searchText: "deploy application production servers",
374
- });
375
- insertTestEntry(db, "test-runner", {
376
- description: "test runner unit integration",
377
- searchText: "test runner unit integration",
378
- });
379
- rebuildFts(db);
380
- const results = searchFts(db, "deploy production", 10);
381
- expect(results).toHaveLength(1);
382
- expect(results[0].entry.name).toBe("deploy-prod");
383
- }
384
- finally {
385
- closeDatabase(db);
386
- }
387
- });
388
- test("rebuildFts synchronizes FTS with entries table", () => {
389
- const db = openDatabase(tmpDbPath());
390
- try {
391
- insertTestEntry(db, "alpha", { description: "alpha functionality", searchText: "alpha functionality" });
392
- insertTestEntry(db, "beta", { description: "beta functionality", searchText: "beta functionality" });
393
- insertTestEntry(db, "gamma", { description: "gamma functionality", searchText: "gamma functionality" });
394
- rebuildFts(db);
395
- const alphaResults = searchFts(db, "alpha", 10);
396
- expect(alphaResults).toHaveLength(1);
397
- expect(alphaResults[0].entry.name).toBe("alpha");
398
- const allResults = searchFts(db, "functionality", 10);
399
- expect(allResults).toHaveLength(3);
400
- }
401
- finally {
402
- closeDatabase(db);
403
- }
404
- });
405
- });
406
- // ── Section 1.4: Meta helpers ──────────────────────────────────────────────
407
- describe("Meta helpers", () => {
408
- test("getMeta returns undefined for missing key", () => {
409
- const db = openDatabase(tmpDbPath());
410
- try {
411
- const val = getMeta(db, "nonexistent-key");
412
- expect(val).toBeUndefined();
413
- }
414
- finally {
415
- closeDatabase(db);
416
- }
417
- });
418
- test("setMeta and getMeta round-trip", () => {
419
- const db = openDatabase(tmpDbPath());
420
- try {
421
- setMeta(db, "test-key", "test-value");
422
- expect(getMeta(db, "test-key")).toBe("test-value");
423
- }
424
- finally {
425
- closeDatabase(db);
426
- }
427
- });
428
- test("setMeta overwrites existing key", () => {
429
- const db = openDatabase(tmpDbPath());
430
- try {
431
- setMeta(db, "overwrite-key", "first");
432
- expect(getMeta(db, "overwrite-key")).toBe("first");
433
- setMeta(db, "overwrite-key", "second");
434
- expect(getMeta(db, "overwrite-key")).toBe("second");
435
- }
436
- finally {
437
- closeDatabase(db);
438
- }
439
- });
440
- });
441
- // ── Section 1.5: Vector / Embedding integration ────────────────────────────
442
- describe("Vector / Embedding integration", () => {
443
- test("openDatabase creates vec table when extension available", () => {
444
- const dbPath = tmpDbPath();
445
- const db = openDatabase(dbPath);
446
- try {
447
- expect(isVecAvailable(db)).toBe(true);
448
- const row = db.prepare("SELECT name FROM sqlite_master WHERE name = 'entries_vec'").get();
449
- expect(row).toBeDefined();
450
- expect(row?.name).toBe("entries_vec");
451
- }
452
- finally {
453
- closeDatabase(db);
454
- }
455
- });
456
- test("upsertEmbedding stores and searchVec retrieves by similarity", () => {
457
- const dbPath = tmpDbPath();
458
- const db = openDatabase(dbPath, { embeddingDim: 4 });
459
- try {
460
- expect(isVecAvailable(db)).toBe(true);
461
- // Insert two entries with distinct embeddings
462
- const id1 = insertTestEntry(db, "vec-tool-1", { searchText: "deployment" });
463
- const id2 = insertTestEntry(db, "vec-tool-2", { searchText: "testing" });
464
- // Embedding vectors: tool-1 points "north", tool-2 points "east"
465
- upsertEmbedding(db, id1, [1, 0, 0, 0]);
466
- upsertEmbedding(db, id2, [0, 1, 0, 0]);
467
- // Query close to tool-1's embedding
468
- const results = searchVec(db, [0.9, 0.1, 0, 0], 10);
469
- expect(results.length).toBe(2);
470
- // tool-1 should be the closest (smallest distance)
471
- expect(results[0].id).toBe(id1);
472
- expect(results[0].distance).toBeLessThan(results[1].distance);
473
- }
474
- finally {
475
- closeDatabase(db);
476
- }
477
- });
478
- test("upsertEmbedding overwrites existing embedding for same entry", () => {
479
- const dbPath = tmpDbPath();
480
- const db = openDatabase(dbPath, { embeddingDim: 4 });
481
- try {
482
- const id = insertTestEntry(db, "vec-update", { searchText: "update test" });
483
- upsertEmbedding(db, id, [1, 0, 0, 0]);
484
- let results = searchVec(db, [1, 0, 0, 0], 10);
485
- expect(results.length).toBe(1);
486
- expect(results[0].distance).toBeCloseTo(0, 2);
487
- // Overwrite with a completely different direction
488
- upsertEmbedding(db, id, [0, 0, 0, 1]);
489
- results = searchVec(db, [0, 0, 0, 1], 10);
490
- expect(results.length).toBe(1);
491
- expect(results[0].distance).toBeCloseTo(0, 2);
492
- // Original direction should now be far
493
- results = searchVec(db, [1, 0, 0, 0], 10);
494
- expect(results[0].distance).toBeGreaterThan(1);
495
- }
496
- finally {
497
- closeDatabase(db);
498
- }
499
- });
500
- test("searchVec respects k limit", () => {
501
- const dbPath = tmpDbPath();
502
- const db = openDatabase(dbPath, { embeddingDim: 4 });
503
- try {
504
- // Insert 5 entries with embeddings
505
- for (let i = 0; i < 5; i++) {
506
- const id = insertTestEntry(db, `vec-k-${i}`, { searchText: `entry ${i}` });
507
- const vec = [0, 0, 0, 0];
508
- vec[i % 4] = 1;
509
- upsertEmbedding(db, id, vec);
510
- }
511
- const results = searchVec(db, [1, 0, 0, 0], 2);
512
- expect(results.length).toBe(2);
513
- }
514
- finally {
515
- closeDatabase(db);
516
- }
517
- });
518
- test("deleteEntriesByDir also removes vec rows", () => {
519
- const dbPath = tmpDbPath();
520
- const db = openDatabase(dbPath, { embeddingDim: 4 });
521
- try {
522
- const id1 = insertTestEntry(db, "vec-del-1", { dirPath: "/del-dir", searchText: "delete me" });
523
- const id2 = insertTestEntry(db, "vec-del-2", { dirPath: "/keep-dir", searchText: "keep me" });
524
- upsertEmbedding(db, id1, [1, 0, 0, 0]);
525
- upsertEmbedding(db, id2, [0, 1, 0, 0]);
526
- // Before delete, both should be searchable
527
- let results = searchVec(db, [0.5, 0.5, 0, 0], 10);
528
- expect(results.length).toBe(2);
529
- deleteEntriesByDir(db, "/del-dir");
530
- // After delete, only the kept entry should remain
531
- results = searchVec(db, [0.5, 0.5, 0, 0], 10);
532
- expect(results.length).toBe(1);
533
- expect(results[0].id).toBe(id2);
534
- }
535
- finally {
536
- closeDatabase(db);
537
- }
538
- });
539
- test("embeddingDim change recreates vec table and clears old embeddings", () => {
540
- const dbPath = tmpDbPath();
541
- // Open with dim=4 and insert an embedding
542
- let db = openDatabase(dbPath, { embeddingDim: 4 });
543
- const id = insertTestEntry(db, "dim-change", { searchText: "dimension test" });
544
- upsertEmbedding(db, id, [1, 0, 0, 0]);
545
- let results = searchVec(db, [1, 0, 0, 0], 10);
546
- expect(results.length).toBe(1);
547
- closeDatabase(db);
548
- // Reopen with dim=8 — vec table should be recreated, old embeddings gone
549
- db = openDatabase(dbPath, { embeddingDim: 8 });
550
- try {
551
- expect(getMeta(db, "embeddingDim")).toBe("8");
552
- // Old embedding was dim=4 and table was recreated for dim=8, so no results
553
- results = searchVec(db, [1, 0, 0, 0, 0, 0, 0, 0], 10);
554
- expect(results.length).toBe(0);
555
- }
556
- finally {
557
- closeDatabase(db);
558
- }
559
- });
560
- });
561
- // ── Incremental rebuildFts (#177 perf finding) ──────────────────────────────
562
- describe("rebuildFts incremental", () => {
563
- function makeEntry(name, description = "") {
564
- return {
565
- name,
566
- type: "skill",
567
- description,
568
- filename: `${name}.md`,
569
- };
570
- }
571
- function ftsCount(db) {
572
- const row = db.prepare("SELECT COUNT(*) AS cnt FROM entries_fts").get();
573
- return row?.cnt ?? 0;
574
- }
575
- function dirtyCount(db) {
576
- try {
577
- const row = db.prepare("SELECT COUNT(*) AS cnt FROM entries_fts_dirty").get();
578
- return row?.cnt ?? 0;
579
- }
580
- catch {
581
- return 0;
582
- }
583
- }
584
- test("upsertEntry marks rows dirty; incremental rebuild only re-indexes them", () => {
585
- const db = openDatabase(tmpDbPath("inc-fts"));
586
- try {
587
- upsertEntry(db, "k1", "/d", "/d/k1.md", "/stash", makeEntry("alpha", "first"), "alpha first");
588
- upsertEntry(db, "k2", "/d", "/d/k2.md", "/stash", makeEntry("bravo", "second"), "bravo second");
589
- upsertEntry(db, "k3", "/d", "/d/k3.md", "/stash", makeEntry("charlie", "third"), "charlie third");
590
- rebuildFts(db, { incremental: false });
591
- expect(ftsCount(db)).toBe(3);
592
- expect(dirtyCount(db)).toBe(0);
593
- // Touch only one entry — its row should be the only dirty one.
594
- upsertEntry(db, "k2", "/d", "/d/k2.md", "/stash", makeEntry("bravo", "second-updated"), "bravo second-updated");
595
- expect(dirtyCount(db)).toBe(1);
596
- rebuildFts(db, { incremental: true });
597
- expect(ftsCount(db)).toBe(3);
598
- expect(dirtyCount(db)).toBe(0);
599
- const hits = db
600
- .prepare("SELECT entry_id FROM entries_fts WHERE entries_fts MATCH ?")
601
- .all(`"second-updated"`);
602
- expect(hits.length).toBe(1);
603
- }
604
- finally {
605
- closeDatabase(db);
606
- }
607
- });
608
- test("incremental rebuild with empty dirty queue is a no-op", () => {
609
- const db = openDatabase(tmpDbPath("inc-fts-empty"));
610
- try {
611
- upsertEntry(db, "k1", "/d", "/d/k1.md", "/stash", makeEntry("alpha"), "alpha");
612
- rebuildFts(db, { incremental: false });
613
- expect(ftsCount(db)).toBe(1);
614
- expect(dirtyCount(db)).toBe(0);
615
- rebuildFts(db, { incremental: true });
616
- expect(ftsCount(db)).toBe(1);
617
- }
618
- finally {
619
- closeDatabase(db);
620
- }
621
- });
622
- test("full rebuild also drains the dirty queue", () => {
623
- const db = openDatabase(tmpDbPath("inc-fts-full"));
624
- try {
625
- upsertEntry(db, "k1", "/d", "/d/k1.md", "/stash", makeEntry("alpha"), "alpha");
626
- expect(dirtyCount(db)).toBe(1);
627
- rebuildFts(db, { incremental: false });
628
- expect(ftsCount(db)).toBe(1);
629
- expect(dirtyCount(db)).toBe(0);
630
- }
631
- finally {
632
- closeDatabase(db);
633
- }
634
- });
635
- test("deleteEntriesByDir purges FTS rows + dirty markers immediately", () => {
636
- const db = openDatabase(tmpDbPath("inc-fts-del"));
637
- try {
638
- upsertEntry(db, "k1", "/d", "/d/k1.md", "/stash", makeEntry("alpha"), "alpha");
639
- upsertEntry(db, "k2", "/d", "/d/k2.md", "/stash", makeEntry("bravo"), "bravo");
640
- rebuildFts(db, { incremental: false });
641
- expect(ftsCount(db)).toBe(2);
642
- upsertEntry(db, "k1", "/d", "/d/k1.md", "/stash", makeEntry("alpha", "updated"), "alpha updated");
643
- expect(dirtyCount(db)).toBe(1);
644
- deleteEntriesByDir(db, "/d");
645
- expect(ftsCount(db)).toBe(0);
646
- expect(dirtyCount(db)).toBe(0);
647
- rebuildFts(db, { incremental: true });
648
- expect(ftsCount(db)).toBe(0);
649
- }
650
- finally {
651
- closeDatabase(db);
652
- }
653
- });
654
- });