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,548 +0,0 @@
1
- /**
2
- * Ranking regression tests for akm search system.
3
- *
4
- * Uses the shared `ranking-baseline` fixture under
5
- * tests/fixtures/stashes/ranking-baseline/ to validate search ranking
6
- * invariants: score differentiation, exact name matching, type ranking,
7
- * fuzzy/prefix matching, score preservation, and provider merge behavior.
8
- *
9
- * The fixture stash is materialised once in beforeAll via the shared
10
- * `loadFixtureStash` helper, then re-indexed in place through the internal
11
- * indexer DB API so all tests share the same index.
12
- */
13
- import { afterAll, beforeAll, describe, expect, test } from "bun:test";
14
- import fs from "node:fs";
15
- import os from "node:os";
16
- import path from "node:path";
17
- import { akmSearch } from "../src/commands/search";
18
- import { saveConfig } from "../src/core/config";
19
- import { getDbPath } from "../src/core/paths";
20
- import { closeDatabase, openDatabase, rebuildFts, setMeta, upsertEntry } from "../src/indexer/db";
21
- import { buildSearchText } from "../src/indexer/search-fields";
22
- import { loadFixtureStash } from "./fixtures/stashes/load";
23
- // Local test helper — mirrors the pre-v1 mergeStashHits logic that was removed
24
- // from production code when the OpenViking provider was dropped (Phase 1).
25
- function mergeStashHits(localHits, additionalHits, limit) {
26
- if (additionalHits.length === 0)
27
- return localHits.slice(0, limit);
28
- const localKeys = new Set();
29
- for (const h of localHits)
30
- localKeys.add(h.path ?? h.ref ?? h.name);
31
- const providerOnly = additionalHits.filter((h) => !localKeys.has(h.path ?? h.ref ?? h.name));
32
- return [...localHits, ...providerOnly].sort((a, b) => (b.score ?? 0) - (a.score ?? 0)).slice(0, limit);
33
- }
34
- // ── Fixture path ────────────────────────────────────────────────────────────
35
- let FIXTURE_STASH;
36
- let fixtureCleanup;
37
- // ── Temp directory tracking ─────────────────────────────────────────────────
38
- const createdTmpDirs = [];
39
- function createTmpDir(prefix = "akm-ranking-") {
40
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
41
- createdTmpDirs.push(dir);
42
- return dir;
43
- }
44
- // ── Environment isolation ───────────────────────────────────────────────────
45
- let originalXdgCacheHome;
46
- let originalXdgConfigHome;
47
- let originalAkmStashDir;
48
- let testCacheDir;
49
- let testConfigDir;
50
- beforeAll(async () => {
51
- originalXdgCacheHome = process.env.XDG_CACHE_HOME;
52
- originalXdgConfigHome = process.env.XDG_CONFIG_HOME;
53
- originalAkmStashDir = process.env.AKM_STASH_DIR;
54
- testCacheDir = createTmpDir("akm-ranking-cache-");
55
- testConfigDir = createTmpDir("akm-ranking-config-");
56
- // Materialise the shared ranking-baseline fixture into a tmp dir. This
57
- // test rebuilds the index from scratch via `buildFixtureIndex()` below
58
- // against its own XDG_CACHE_HOME, so we skip the helper's `akm index`
59
- // spawn (~200-300ms saved per run).
60
- const loaded = loadFixtureStash("ranking-baseline", { skipIndex: true });
61
- FIXTURE_STASH = loaded.stashDir;
62
- fixtureCleanup = loaded.cleanup;
63
- process.env.XDG_CACHE_HOME = testCacheDir;
64
- process.env.XDG_CONFIG_HOME = testConfigDir;
65
- process.env.AKM_STASH_DIR = FIXTURE_STASH;
66
- saveConfig({
67
- semanticSearchMode: "off",
68
- sources: [{ type: "filesystem", path: FIXTURE_STASH }],
69
- registries: [],
70
- });
71
- buildFixtureIndex();
72
- });
73
- afterAll(() => {
74
- if (originalXdgCacheHome === undefined)
75
- delete process.env.XDG_CACHE_HOME;
76
- else
77
- process.env.XDG_CACHE_HOME = originalXdgCacheHome;
78
- if (originalXdgConfigHome === undefined)
79
- delete process.env.XDG_CONFIG_HOME;
80
- else
81
- process.env.XDG_CONFIG_HOME = originalXdgConfigHome;
82
- if (originalAkmStashDir === undefined)
83
- delete process.env.AKM_STASH_DIR;
84
- else
85
- process.env.AKM_STASH_DIR = originalAkmStashDir;
86
- fixtureCleanup?.();
87
- for (const dir of createdTmpDirs) {
88
- fs.rmSync(dir, { recursive: true, force: true });
89
- }
90
- });
91
- // ── Index builder ───────────────────────────────────────────────────────────
92
- /**
93
- * Walk the fixture stash, read .stash.json files, and index all entries
94
- * directly into the SQLite database.
95
- */
96
- function buildFixtureIndex() {
97
- const dbPath = getDbPath();
98
- const db = openDatabase(dbPath);
99
- try {
100
- const stashJsonPaths = findStashJsonFiles(FIXTURE_STASH);
101
- for (const stashJsonPath of stashJsonPaths) {
102
- const dirPath = path.dirname(stashJsonPath);
103
- const raw = JSON.parse(fs.readFileSync(stashJsonPath, "utf8"));
104
- if (!raw || !Array.isArray(raw.entries))
105
- continue;
106
- const stash = { entries: raw.entries };
107
- for (const entry of stash.entries) {
108
- const entryPath = entry.filename ? path.join(dirPath, entry.filename) : dirPath;
109
- const entryKey = `${FIXTURE_STASH}:${entry.type}:${entry.name}`;
110
- const searchText = buildSearchText(entry);
111
- let entryWithSize = entry;
112
- try {
113
- const size = fs.statSync(entryPath).size;
114
- entryWithSize = { ...entry, fileSize: size };
115
- }
116
- catch {
117
- // File might not exist for some entries
118
- }
119
- upsertEntry(db, entryKey, dirPath, entryPath, FIXTURE_STASH, entryWithSize, searchText);
120
- }
121
- }
122
- rebuildFts(db);
123
- setMeta(db, "stashDir", FIXTURE_STASH);
124
- setMeta(db, "builtAt", new Date().toISOString());
125
- setMeta(db, "stashDirs", JSON.stringify([FIXTURE_STASH]));
126
- setMeta(db, "hasEmbeddings", "0");
127
- }
128
- finally {
129
- closeDatabase(db);
130
- }
131
- }
132
- function findStashJsonFiles(dir) {
133
- const results = [];
134
- const entries = fs.readdirSync(dir, { withFileTypes: true });
135
- for (const entry of entries) {
136
- const fullPath = path.join(dir, entry.name);
137
- if (entry.isDirectory()) {
138
- results.push(...findStashJsonFiles(fullPath));
139
- }
140
- else if (entry.name === ".stash.json") {
141
- results.push(fullPath);
142
- }
143
- }
144
- return results;
145
- }
146
- // ── Helpers ─────────────────────────────────────────────────────────────────
147
- async function search(query, limit = 20) {
148
- const result = await akmSearch({ query, source: "stash", limit });
149
- return result.hits.filter((h) => h.type !== "registry");
150
- }
151
- function findHit(hits, name) {
152
- return hits.find((h) => h.name === name);
153
- }
154
- /** Assert that a hit exists and return it (avoids non-null assertions). */
155
- function expectHit(hits, name) {
156
- const hit = findHit(hits, name);
157
- expect(hit).toBeDefined();
158
- // biome-ignore lint/style/noNonNullAssertion: guarded by expect above
159
- return hit;
160
- }
161
- /** Get the score of a hit, asserting it is defined. */
162
- function scoreOf(hit) {
163
- expect(hit.score).toBeDefined();
164
- return hit.score ?? 0;
165
- }
166
- function rankOf(hits, name) {
167
- const idx = hits.findIndex((h) => h.name === name);
168
- return idx === -1 ? Infinity : idx + 1; // 1-based rank
169
- }
170
- // ── Tests ───────────────────────────────────────────────────────────────────
171
- describe("Score differentiation", () => {
172
- test('"docker homelab" returns skill:docker-homelab in top 3', async () => {
173
- const hits = await search("docker homelab");
174
- expect(hits.length).toBeGreaterThanOrEqual(2);
175
- // docker-homelab should appear in the top results (within top 3)
176
- // Sub-references also contain "docker-homelab" in their name, so they
177
- // may rank highly on FTS name-field matching.
178
- const skillRank = rankOf(hits, "docker-homelab");
179
- expect(skillRank).toBeLessThanOrEqual(3);
180
- // The skill should have a meaningful score (not RRF-compressed)
181
- const skillHit = expectHit(hits, "docker-homelab");
182
- expect(scoreOf(skillHit)).toBeGreaterThan(0.5);
183
- });
184
- test('"docker" returns docker-homelab and docker-clean', async () => {
185
- const hits = await search("docker");
186
- expect(hits.length).toBeGreaterThanOrEqual(2);
187
- // Both docker-related assets should appear in results
188
- expectHit(hits, "docker-homelab");
189
- expectHit(hits, "docker-clean");
190
- });
191
- test('"svelte component" -> skill:svelte-components ranks #1, above sub-references', async () => {
192
- const hits = await search("svelte component");
193
- expect(hits.length).toBeGreaterThanOrEqual(1);
194
- const skillRank = rankOf(hits, "svelte-components");
195
- expect(skillRank).toBe(1);
196
- // Sub-reference should rank below the skill
197
- const refHit = findHit(hits, "svelte-components/references/web-components");
198
- if (refHit) {
199
- const refRank = rankOf(hits, "svelte-components/references/web-components");
200
- expect(refRank).toBeGreaterThan(skillRank);
201
- }
202
- });
203
- test('"code review" -> command or agent ranks above knowledge docs', async () => {
204
- const hits = await search("code review");
205
- expect(hits.length).toBeGreaterThanOrEqual(2);
206
- // Find the top-ranked actionable asset (skill, command, or agent)
207
- const actionableTypes = new Set(["skill", "command", "agent"]);
208
- const topActionable = hits.find((h) => actionableTypes.has(h.type));
209
- expect(topActionable).toBeDefined();
210
- // Find the top-ranked knowledge doc
211
- const topKnowledge = hits.find((h) => h.type === "knowledge");
212
- if (topKnowledge && topActionable) {
213
- const actionableRank = rankOf(hits, topActionable.name);
214
- const knowledgeRank = rankOf(hits, topKnowledge.name);
215
- expect(actionableRank).toBeLessThan(knowledgeRank);
216
- }
217
- });
218
- test('"mem0 search" -> script:mem0-search ranks #1', async () => {
219
- const hits = await search("mem0 search");
220
- expect(hits.length).toBeGreaterThanOrEqual(1);
221
- expect(rankOf(hits, "mem0-search")).toBe(1);
222
- });
223
- });
224
- describe("Exact/near-exact name matching", () => {
225
- test('"docker-homelab" (exact) -> skill:docker-homelab appears in top 3', async () => {
226
- const hits = await search("docker-homelab");
227
- expect(hits.length).toBeGreaterThanOrEqual(2);
228
- // The skill entry and its sub-references all contain "docker-homelab"
229
- // in their names. The skill gets a name-match boost but sub-references
230
- // also match on FTS name field. Verify the skill is in the top 3.
231
- const skillRank = rankOf(hits, "docker-homelab");
232
- expect(skillRank).toBeLessThanOrEqual(3);
233
- // The skill should have a strong score
234
- const skillHit = expectHit(hits, "docker-homelab");
235
- expect(scoreOf(skillHit)).toBeGreaterThan(0.5);
236
- });
237
- test('"mem0-search" (exact) -> script:mem0-search is #1', async () => {
238
- const hits = await search("mem0-search");
239
- expect(hits.length).toBeGreaterThanOrEqual(1);
240
- expect(hits[0].name).toBe("mem0-search");
241
- });
242
- test('"security-review" (exact) -> command:security-review is #1', async () => {
243
- const hits = await search("security-review");
244
- expect(hits.length).toBeGreaterThanOrEqual(1);
245
- expect(hits[0].name).toBe("security-review");
246
- expect(hits[0].type).toBe("command");
247
- });
248
- test('"k8s-deploy" (exact) -> skill:k8s-deploy is #1', async () => {
249
- const hits = await search("k8s-deploy");
250
- expect(hits.length).toBeGreaterThanOrEqual(1);
251
- expect(hits[0].name).toBe("k8s-deploy");
252
- expect(hits[0].type).toBe("skill");
253
- });
254
- test('"code-reviewer" (exact) -> agent:code-reviewer is #1', async () => {
255
- const hits = await search("code-reviewer");
256
- expect(hits.length).toBeGreaterThanOrEqual(1);
257
- expect(hits[0].name).toBe("code-reviewer");
258
- expect(hits[0].type).toBe("agent");
259
- });
260
- });
261
- describe("Type ranking", () => {
262
- test('for "deploy", skills/commands/scripts rank above knowledge docs', async () => {
263
- const hits = await search("deploy");
264
- expect(hits.length).toBeGreaterThanOrEqual(2);
265
- const actionableTypes = new Set(["skill", "command", "agent", "script"]);
266
- const topActionable = hits.find((h) => actionableTypes.has(h.type));
267
- const topKnowledge = hits.find((h) => h.type === "knowledge");
268
- expect(topActionable).toBeDefined();
269
- if (topKnowledge && topActionable) {
270
- expect(rankOf(hits, topActionable.name)).toBeLessThan(rankOf(hits, topKnowledge.name));
271
- }
272
- });
273
- test('for "review", agents/commands/skills rank above knowledge docs', async () => {
274
- const hits = await search("review");
275
- expect(hits.length).toBeGreaterThanOrEqual(2);
276
- const actionableTypes = new Set(["skill", "command", "agent"]);
277
- const topActionable = hits.find((h) => actionableTypes.has(h.type));
278
- const topKnowledge = hits.find((h) => h.type === "knowledge");
279
- expect(topActionable).toBeDefined();
280
- if (topKnowledge && topActionable) {
281
- expect(rankOf(hits, topActionable.name)).toBeLessThan(rankOf(hits, topKnowledge.name));
282
- }
283
- });
284
- });
285
- describe("Fuzzy/prefix matching", () => {
286
- test('"kube" finds k8s-deploy via alias', async () => {
287
- const hits = await search("kube");
288
- expectHit(hits, "k8s-deploy");
289
- });
290
- test('"dock" finds docker-homelab via prefix', async () => {
291
- const hits = await search("dock");
292
- expectHit(hits, "docker-homelab");
293
- });
294
- test('"incident" finds the runbook', async () => {
295
- const hits = await search("incident");
296
- expectHit(hits, "incident-response-runbook");
297
- });
298
- });
299
- describe("Score preservation (not RRF-flattened)", () => {
300
- test("top result score > 0.5 (not capped at 0.0164)", async () => {
301
- const hits = await search("docker homelab");
302
- expect(hits.length).toBeGreaterThanOrEqual(1);
303
- expect(scoreOf(hits[0])).toBeGreaterThan(0.5);
304
- });
305
- test("top result for exact name query has strong differentiation", async () => {
306
- // Use a query that uniquely targets one asset.
307
- // Per the locked v1 contract (CLAUDE.md / spec §9), SearchHit.score is
308
- // bounded in [0,1]. An exact-name match accumulates large additive
309
- // boosts that are clamped at the final emit step, so the top score for
310
- // a uniquely-matching exact-name query must reach the ceiling (1.0).
311
- const hits = await search("mem0 search");
312
- expect(hits.length).toBeGreaterThanOrEqual(1);
313
- const topScore = scoreOf(hits[0]);
314
- expect(topScore).toBe(1);
315
- // If there are additional results, the top should be at least as high.
316
- // Below-ceiling differentiation is asserted by the broader
317
- // "scores are monotonically decreasing" case below; here we just
318
- // confirm the top hit reaches the maximum.
319
- if (hits.length >= 2) {
320
- expect(topScore).toBeGreaterThanOrEqual(scoreOf(hits[1]));
321
- }
322
- });
323
- test("scores are monotonically decreasing", async () => {
324
- const hits = await search("docker");
325
- for (let i = 1; i < hits.length; i++) {
326
- const prev = hits[i - 1].score ?? 0;
327
- const curr = hits[i].score ?? 0;
328
- expect(prev).toBeGreaterThanOrEqual(curr);
329
- }
330
- });
331
- test("scores are not compressed to a narrow range", async () => {
332
- const hits = await search("docker");
333
- expect(hits.length).toBeGreaterThanOrEqual(3);
334
- const topScore = scoreOf(hits[0]);
335
- const lastScore = scoreOf(hits[hits.length - 1]);
336
- const range = topScore - lastScore;
337
- // Score range should be meaningful, not compressed to ~0.001 like RRF.
338
- // Per the locked v1 contract (CLAUDE.md / spec §9), scores are bounded
339
- // in [0,1] — multiple top hits on a popular query may all clamp to 1.0,
340
- // which collapses the visible top-end differential. The bottom of the
341
- // range still shows clear separation from the top, well above what RRF
342
- // would compress to (~0.001).
343
- expect(range).toBeGreaterThan(0.01);
344
- });
345
- });
346
- describe("Provider merge (score not destroyed)", () => {
347
- test("when additional provider hits exist, local scores are preserved", () => {
348
- const localHits = [
349
- {
350
- type: "skill",
351
- name: "local-skill-1",
352
- path: "/test/skills/local-1/SKILL.md",
353
- ref: "skill:local-skill-1",
354
- origin: null,
355
- score: 2.5,
356
- },
357
- {
358
- type: "command",
359
- name: "local-cmd-1",
360
- path: "/test/commands/local-1.md",
361
- ref: "command:local-cmd-1",
362
- origin: null,
363
- score: 1.8,
364
- },
365
- {
366
- type: "knowledge",
367
- name: "local-doc-1",
368
- path: "/test/knowledge/local-1.md",
369
- ref: "knowledge:local-doc-1",
370
- origin: null,
371
- score: 0.9,
372
- },
373
- ];
374
- const additionalHits = [
375
- {
376
- type: "skill",
377
- name: "remote-skill-1",
378
- path: "/remote/skills/remote-1/SKILL.md",
379
- ref: "skill:remote-skill-1",
380
- origin: "remote",
381
- score: 0.85,
382
- },
383
- ];
384
- const merged = mergeStashHits(localHits, additionalHits, 20);
385
- // Local hits should retain their original scores
386
- const mergedLocal1 = expectHit(merged, "local-skill-1");
387
- expect(mergedLocal1.score).toBe(2.5);
388
- const mergedLocal2 = expectHit(merged, "local-cmd-1");
389
- expect(mergedLocal2.score).toBe(1.8);
390
- });
391
- test("provider hits sort fairly by score alongside local hits", () => {
392
- const localHits = [
393
- {
394
- type: "skill",
395
- name: "local-high",
396
- path: "/test/skills/high/SKILL.md",
397
- ref: "skill:local-high",
398
- origin: null,
399
- score: 2.0,
400
- },
401
- {
402
- type: "skill",
403
- name: "local-low",
404
- path: "/test/skills/low/SKILL.md",
405
- ref: "skill:local-low",
406
- origin: null,
407
- score: 0.5,
408
- },
409
- ];
410
- const additionalHits = [
411
- {
412
- type: "skill",
413
- name: "remote-1",
414
- path: "/remote/skills/1/SKILL.md",
415
- ref: "skill:remote-1",
416
- origin: "remote",
417
- score: 1.0, // Normalized provider score between local-high and local-low
418
- },
419
- ];
420
- const merged = mergeStashHits(localHits, additionalHits, 20);
421
- // Provider hit keeps its original score and sorts by score
422
- const remoteRank = merged.findIndex((h) => h.name === "remote-1") + 1;
423
- const localHighRank = merged.findIndex((h) => h.name === "local-high") + 1;
424
- const localLowRank = merged.findIndex((h) => h.name === "local-low") + 1;
425
- // remote-1 (1.0) should rank between local-high (2.0) and local-low (0.5)
426
- expect(remoteRank).toBeGreaterThan(localHighRank);
427
- expect(remoteRank).toBeLessThan(localLowRank);
428
- });
429
- test("duplicate provider hits are deduplicated (local version wins)", () => {
430
- const localHits = [
431
- {
432
- type: "skill",
433
- name: "shared-skill",
434
- path: "/test/skills/shared/SKILL.md",
435
- ref: "skill:shared-skill",
436
- origin: null,
437
- score: 2.0,
438
- },
439
- ];
440
- const additionalHits = [
441
- {
442
- type: "skill",
443
- name: "shared-skill",
444
- path: "/test/skills/shared/SKILL.md", // Same path = duplicate
445
- ref: "skill:shared-skill",
446
- origin: "remote",
447
- score: 0.5,
448
- },
449
- ];
450
- const merged = mergeStashHits(localHits, additionalHits, 20);
451
- // Only one instance of the shared skill should appear
452
- const sharedHits = merged.filter((h) => h.name === "shared-skill");
453
- expect(sharedHits.length).toBe(1);
454
- // And it should have the local score
455
- expect(sharedHits[0].score).toBe(2.0);
456
- });
457
- test("merge preserves sort order by score descending", () => {
458
- const localHits = [
459
- { type: "skill", name: "a", path: "/a", ref: "skill:a", origin: null, score: 3.0 },
460
- { type: "skill", name: "b", path: "/b", ref: "skill:b", origin: null, score: 1.0 },
461
- ];
462
- const additionalHits = [
463
- { type: "skill", name: "c", path: "/c", ref: "skill:c", origin: "remote", score: 2.0 },
464
- ];
465
- const merged = mergeStashHits(localHits, additionalHits, 20);
466
- for (let i = 1; i < merged.length; i++) {
467
- const prev = merged[i - 1].score ?? 0;
468
- const curr = merged[i].score ?? 0;
469
- expect(prev).toBeGreaterThanOrEqual(curr);
470
- }
471
- });
472
- });
473
- describe("Cross-type search consistency", () => {
474
- test("searching for 'docker' returns docker-homelab and docker-clean", async () => {
475
- const hits = await search("docker");
476
- expect(hits.length).toBeGreaterThanOrEqual(2);
477
- const dockerNames = hits.map((h) => h.name);
478
- expect(dockerNames).toContain("docker-homelab");
479
- expect(dockerNames).toContain("docker-clean");
480
- });
481
- test("multi-word queries narrow results appropriately", async () => {
482
- const narrowHits = await search("deploy check");
483
- // The narrow query should return deploy-check at a high rank
484
- const deployCheckRank = rankOf(narrowHits, "deploy-check");
485
- expect(deployCheckRank).toBeLessThanOrEqual(3);
486
- });
487
- test("searching for svelte returns both skill and agent", async () => {
488
- const hits = await search("svelte");
489
- const svelteNames = hits.map((h) => h.name);
490
- expect(svelteNames).toContain("svelte-components");
491
- expect(svelteNames).toContain("svelte-expert");
492
- });
493
- test("searching for 'release' finds the release-manager command", async () => {
494
- const hits = await search("release");
495
- const releaseHit = expectHit(hits, "release-manager");
496
- expect(releaseHit.type).toBe("command");
497
- });
498
- });
499
- describe("Metadata signal strength", () => {
500
- test("skill with rich metadata appears in results for broad queries", async () => {
501
- // docker-homelab has rich tags, aliases, searchHints, and curated quality
502
- const hits = await search("container management");
503
- const skillHit = expectHit(hits, "docker-homelab");
504
- expect(scoreOf(skillHit)).toBeGreaterThan(0);
505
- });
506
- test("curated quality assets include the curated metadata boost reason", async () => {
507
- const hits = await search("kubernetes deploy");
508
- expect(hits.length).toBeGreaterThanOrEqual(1);
509
- const k8sHit = expectHit(hits, "k8s-deploy");
510
- expect(k8sHit.whyMatched).toContain("curated metadata boost");
511
- });
512
- test("searchHints contribute to matching", async () => {
513
- // "troubleshoot docker" is a search hint on docker-homelab
514
- const hits = await search("troubleshoot docker");
515
- const skillHit = expectHit(hits, "docker-homelab");
516
- expect(skillHit.whyMatched).toContain("matched searchHints");
517
- });
518
- test("aliases contribute to matching", async () => {
519
- // "docker-compose" is an alias for docker-homelab
520
- const hits = await search("docker compose");
521
- const skillHit = expectHit(hits, "docker-homelab");
522
- expect(skillHit.whyMatched).toContain("matched aliases");
523
- });
524
- test("tags contribute to matching", async () => {
525
- const hits = await search("homelab");
526
- const skillHit = expectHit(hits, "docker-homelab");
527
- expect(skillHit.whyMatched).toContain("matched tags");
528
- });
529
- });
530
- describe("Empty and edge case queries", () => {
531
- test("empty query returns all entries", async () => {
532
- const result = await akmSearch({ query: "", source: "stash" });
533
- expect(result.hits.length).toBeGreaterThan(0);
534
- });
535
- test("query with no matches returns empty results with tip", async () => {
536
- const result = await akmSearch({ query: "xyznonexistent123", source: "stash" });
537
- const hits = result.hits.filter((h) => h.type !== "registry");
538
- expect(hits.length).toBe(0);
539
- });
540
- test("single character query returns results when prefix matches", async () => {
541
- // Single char queries are too short for prefix expansion (< 3 chars)
542
- // but may still match on exact tokens
543
- const result = await akmSearch({ query: "k", source: "stash" });
544
- // This may or may not return results depending on FTS tokenizer behavior
545
- // The important thing is it does not crash
546
- expect(result).toBeDefined();
547
- });
548
- });