akm-cli 0.7.0-rc1 → 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 (314) hide show
  1. package/dist/{src/cli.js → cli.js} +100 -16
  2. package/dist/{src/commands → commands}/config-cli.js +42 -0
  3. package/dist/{src/commands → commands}/history.js +78 -7
  4. package/dist/{src/commands → commands}/registry-search.js +69 -6
  5. package/dist/{src/commands → commands}/search.js +30 -3
  6. package/dist/{src/commands → commands}/show.js +29 -0
  7. package/dist/{src/commands → commands}/source-add.js +5 -1
  8. package/dist/{src/commands → commands}/source-manage.js +7 -1
  9. package/dist/{src/core → core}/config.js +28 -0
  10. package/dist/{src/indexer → indexer}/db-search.js +1 -0
  11. package/dist/{src/indexer → indexer}/indexer.js +16 -2
  12. package/dist/{src/indexer → indexer}/matchers.js +1 -1
  13. package/dist/{src/indexer → indexer}/search-source.js +4 -2
  14. package/dist/{src/integrations → integrations}/agent/profiles.js +1 -1
  15. package/dist/{src/integrations → integrations}/agent/spawn.js +67 -16
  16. package/dist/{src/integrations → integrations}/github.js +9 -3
  17. package/dist/{src/llm → llm}/embedders/remote.js +37 -3
  18. package/dist/{src/output → output}/cli-hints.js +15 -2
  19. package/dist/{src/output → output}/renderers.js +3 -1
  20. package/dist/{src/output → output}/shapes.js +8 -1
  21. package/dist/{src/output → output}/text.js +156 -3
  22. package/dist/{src/registry → registry}/build-index.js +5 -4
  23. package/dist/{src/registry → registry}/providers/static-index.js +3 -1
  24. package/dist/{src/setup → setup}/setup.js +9 -0
  25. package/dist/{src/wiki → wiki}/wiki.js +54 -6
  26. package/dist/{src/workflows → workflows}/runs.js +37 -3
  27. package/package.json +8 -8
  28. package/dist/tests/add-website-source.test.js +0 -119
  29. package/dist/tests/agent/agent-config-loader.test.js +0 -70
  30. package/dist/tests/agent/agent-config.test.js +0 -221
  31. package/dist/tests/agent/agent-detect.test.js +0 -100
  32. package/dist/tests/agent/agent-spawn.test.js +0 -234
  33. package/dist/tests/agent-output.test.js +0 -186
  34. package/dist/tests/architecture/agent-no-llm-sdk-guard.test.js +0 -103
  35. package/dist/tests/architecture/agent-spawn-seam.test.js +0 -193
  36. package/dist/tests/architecture/llm-stateless-seam.test.js +0 -112
  37. package/dist/tests/asset-ref.test.js +0 -192
  38. package/dist/tests/asset-registry.test.js +0 -103
  39. package/dist/tests/asset-spec.test.js +0 -241
  40. package/dist/tests/bench/attribution.test.js +0 -995
  41. package/dist/tests/bench/cleanup-sigint.test.js +0 -83
  42. package/dist/tests/bench/cleanup.js +0 -203
  43. package/dist/tests/bench/cleanup.test.js +0 -166
  44. package/dist/tests/bench/cli.js +0 -683
  45. package/dist/tests/bench/cli.test.js +0 -177
  46. package/dist/tests/bench/compare.test.js +0 -556
  47. package/dist/tests/bench/corpus.js +0 -314
  48. package/dist/tests/bench/corpus.test.js +0 -258
  49. package/dist/tests/bench/driver.js +0 -346
  50. package/dist/tests/bench/driver.test.js +0 -443
  51. package/dist/tests/bench/evolve-metrics.js +0 -179
  52. package/dist/tests/bench/evolve-metrics.test.js +0 -187
  53. package/dist/tests/bench/evolve.js +0 -580
  54. package/dist/tests/bench/evolve.test.js +0 -616
  55. package/dist/tests/bench/failure-modes.test.js +0 -300
  56. package/dist/tests/bench/feedback-integrity.test.js +0 -456
  57. package/dist/tests/bench/leakage.test.js +0 -125
  58. package/dist/tests/bench/learning-curve.test.js +0 -133
  59. package/dist/tests/bench/metrics.js +0 -2319
  60. package/dist/tests/bench/metrics.test.js +0 -1144
  61. package/dist/tests/bench/no-os-tmpdir-invariant.test.js +0 -43
  62. package/dist/tests/bench/report.js +0 -1821
  63. package/dist/tests/bench/report.test.js +0 -989
  64. package/dist/tests/bench/runner.js +0 -536
  65. package/dist/tests/bench/runner.test.js +0 -958
  66. package/dist/tests/bench/search-bridge.test.js +0 -331
  67. package/dist/tests/bench/tmp.js +0 -41
  68. package/dist/tests/bench/trajectory.js +0 -116
  69. package/dist/tests/bench/trajectory.test.js +0 -127
  70. package/dist/tests/bench/verifier.js +0 -109
  71. package/dist/tests/bench/verifier.test.js +0 -118
  72. package/dist/tests/bench/workflow-evaluator.js +0 -557
  73. package/dist/tests/bench/workflow-evaluator.test.js +0 -421
  74. package/dist/tests/bench/workflow-spec.js +0 -358
  75. package/dist/tests/bench/workflow-spec.test.js +0 -363
  76. package/dist/tests/bench/workflow-trace.js +0 -438
  77. package/dist/tests/bench/workflow-trace.test.js +0 -254
  78. package/dist/tests/benchmark-search-quality.js +0 -536
  79. package/dist/tests/benchmark-suite.js +0 -1441
  80. package/dist/tests/capture-cli.test.js +0 -112
  81. package/dist/tests/cli-errors.test.js +0 -203
  82. package/dist/tests/commands/events.test.js +0 -370
  83. package/dist/tests/commands/history.test.js +0 -223
  84. package/dist/tests/commands/import.test.js +0 -103
  85. package/dist/tests/commands/proposal-cli.test.js +0 -209
  86. package/dist/tests/commands/reflect-propose-cli.test.js +0 -333
  87. package/dist/tests/commands/remember.test.js +0 -97
  88. package/dist/tests/commands/scope-flags.test.js +0 -300
  89. package/dist/tests/commands/search.test.js +0 -537
  90. package/dist/tests/commands/show-indexer-parity.test.js +0 -117
  91. package/dist/tests/commands/show.test.js +0 -294
  92. package/dist/tests/common.test.js +0 -266
  93. package/dist/tests/completions.test.js +0 -142
  94. package/dist/tests/config-cli.test.js +0 -193
  95. package/dist/tests/config-llm-features.test.js +0 -139
  96. package/dist/tests/config.test.js +0 -544
  97. package/dist/tests/contracts/migration-baseline.test.js +0 -43
  98. package/dist/tests/contracts/reflect-propose-envelope.test.js +0 -139
  99. package/dist/tests/contracts/spec-helpers.js +0 -46
  100. package/dist/tests/contracts/v1-spec-section-11-proposal-queue.test.js +0 -228
  101. package/dist/tests/contracts/v1-spec-section-12-agent-config.test.js +0 -56
  102. package/dist/tests/contracts/v1-spec-section-13-lesson-type.test.js +0 -34
  103. package/dist/tests/contracts/v1-spec-section-14-llm-features.test.js +0 -94
  104. package/dist/tests/contracts/v1-spec-section-4-1-asset-types.test.js +0 -39
  105. package/dist/tests/contracts/v1-spec-section-4-2-quality-rules.test.js +0 -44
  106. package/dist/tests/contracts/v1-spec-section-5-configuration.test.js +0 -47
  107. package/dist/tests/contracts/v1-spec-section-6-orchestration.test.js +0 -40
  108. package/dist/tests/contracts/v1-spec-section-7-module-layout.test.js +0 -58
  109. package/dist/tests/contracts/v1-spec-section-8-extension-points.test.js +0 -34
  110. package/dist/tests/contracts/v1-spec-section-9-4-cli-surface.test.js +0 -75
  111. package/dist/tests/contracts/v1-spec-section-9-7-llm-agent-boundary.test.js +0 -36
  112. package/dist/tests/core/write-source.test.js +0 -366
  113. package/dist/tests/curate-command.test.js +0 -87
  114. package/dist/tests/db-scoring.test.js +0 -201
  115. package/dist/tests/db.test.js +0 -654
  116. package/dist/tests/distill-cli-flag.test.js +0 -208
  117. package/dist/tests/distill.test.js +0 -515
  118. package/dist/tests/docker-install.test.js +0 -120
  119. package/dist/tests/e2e.test.js +0 -1398
  120. package/dist/tests/embedder.test.js +0 -340
  121. package/dist/tests/embedding-model-config.test.js +0 -379
  122. package/dist/tests/feedback-command.test.js +0 -172
  123. package/dist/tests/file-context.test.js +0 -552
  124. package/dist/tests/fixtures/scripts/git/summarize-diff.js +0 -9
  125. package/dist/tests/fixtures/scripts/lint/eslint-check.js +0 -7
  126. package/dist/tests/fixtures/stashes/load.js +0 -166
  127. package/dist/tests/fixtures/stashes/load.test.js +0 -88
  128. package/dist/tests/fixtures/stashes/ranking-baseline/scripts/mem0-search.js +0 -12
  129. package/dist/tests/frontmatter.test.js +0 -190
  130. package/dist/tests/fts-field-weighting.test.js +0 -254
  131. package/dist/tests/fuzzy-search.test.js +0 -230
  132. package/dist/tests/git-provider-clone.test.js +0 -45
  133. package/dist/tests/github.test.js +0 -161
  134. package/dist/tests/graph-boost-ranking.test.js +0 -305
  135. package/dist/tests/graph-extraction.test.js +0 -282
  136. package/dist/tests/helpers/usage-events.js +0 -8
  137. package/dist/tests/index-pass-llm.test.js +0 -161
  138. package/dist/tests/indexer.test.js +0 -559
  139. package/dist/tests/info-command.test.js +0 -166
  140. package/dist/tests/init.test.js +0 -69
  141. package/dist/tests/install-script.test.js +0 -246
  142. package/dist/tests/integration/agent-real-profile.test.js +0 -94
  143. package/dist/tests/issue-36-repro.test.js +0 -304
  144. package/dist/tests/issues-191-194.test.js +0 -160
  145. package/dist/tests/lesson-lint.test.js +0 -111
  146. package/dist/tests/llm-client.test.js +0 -115
  147. package/dist/tests/llm-feature-gate.test.js +0 -151
  148. package/dist/tests/llm.test.js +0 -139
  149. package/dist/tests/lockfile.test.js +0 -216
  150. package/dist/tests/manifest.test.js +0 -205
  151. package/dist/tests/markdown.test.js +0 -126
  152. package/dist/tests/matchers-unit.test.js +0 -189
  153. package/dist/tests/memory-inference.test.js +0 -299
  154. package/dist/tests/merge-scoring.test.js +0 -136
  155. package/dist/tests/metadata.test.js +0 -313
  156. package/dist/tests/migration-help.test.js +0 -89
  157. package/dist/tests/origin-resolve.test.js +0 -124
  158. package/dist/tests/output-baseline.test.js +0 -217
  159. package/dist/tests/output-shapes-unit.test.js +0 -476
  160. package/dist/tests/parallel-search.test.js +0 -272
  161. package/dist/tests/parameter-metadata.test.js +0 -365
  162. package/dist/tests/paths.test.js +0 -177
  163. package/dist/tests/progressive-disclosure.test.js +0 -280
  164. package/dist/tests/proposals.test.js +0 -279
  165. package/dist/tests/proposed-quality.test.js +0 -271
  166. package/dist/tests/provider-registry.test.js +0 -32
  167. package/dist/tests/ranking-regression.test.js +0 -548
  168. package/dist/tests/reflect-propose.test.js +0 -455
  169. package/dist/tests/registry-build-index.test.js +0 -378
  170. package/dist/tests/registry-cli.test.js +0 -290
  171. package/dist/tests/registry-index-v2.test.js +0 -430
  172. package/dist/tests/registry-install.test.js +0 -728
  173. package/dist/tests/registry-providers/parity.test.js +0 -189
  174. package/dist/tests/registry-providers/skills-sh.test.js +0 -309
  175. package/dist/tests/registry-providers/static-index.test.js +0 -204
  176. package/dist/tests/registry-resolve.test.js +0 -126
  177. package/dist/tests/registry-search.test.js +0 -723
  178. package/dist/tests/remember-frontmatter.test.js +0 -380
  179. package/dist/tests/remember-unit.test.js +0 -123
  180. package/dist/tests/ripgrep-install.test.js +0 -251
  181. package/dist/tests/ripgrep-resolve.test.js +0 -108
  182. package/dist/tests/ripgrep.test.js +0 -163
  183. package/dist/tests/save-command.test.js +0 -94
  184. package/dist/tests/save-trust-qa-fixes.test.js +0 -270
  185. package/dist/tests/scoring-pipeline.test.js +0 -648
  186. package/dist/tests/search-include-proposed-cli.test.js +0 -118
  187. package/dist/tests/self-update.test.js +0 -442
  188. package/dist/tests/semantic-search-e2e.test.js +0 -512
  189. package/dist/tests/semantic-status.test.js +0 -471
  190. package/dist/tests/setup-run.integration.js +0 -877
  191. package/dist/tests/setup-wizard.test.js +0 -198
  192. package/dist/tests/setup.test.js +0 -131
  193. package/dist/tests/source-add.test.js +0 -11
  194. package/dist/tests/source-clone.test.js +0 -254
  195. package/dist/tests/source-manage.test.js +0 -366
  196. package/dist/tests/source-providers/filesystem.test.js +0 -82
  197. package/dist/tests/source-providers/git.test.js +0 -252
  198. package/dist/tests/source-providers/website.test.js +0 -128
  199. package/dist/tests/source-qa-fixes.test.js +0 -268
  200. package/dist/tests/source-registry.test.js +0 -350
  201. package/dist/tests/source-resolve.test.js +0 -100
  202. package/dist/tests/source-source.test.js +0 -221
  203. package/dist/tests/source.test.js +0 -533
  204. package/dist/tests/tar-utils-scan.test.js +0 -73
  205. package/dist/tests/toggle-components.test.js +0 -73
  206. package/dist/tests/usage-telemetry.test.js +0 -265
  207. package/dist/tests/utility-scoring.test.js +0 -558
  208. package/dist/tests/vault-load-error.test.js +0 -78
  209. package/dist/tests/vault-qa-fixes.test.js +0 -194
  210. package/dist/tests/vault.test.js +0 -429
  211. package/dist/tests/vector-search.test.js +0 -608
  212. package/dist/tests/walker.test.js +0 -252
  213. package/dist/tests/wave2-cluster-bc.test.js +0 -228
  214. package/dist/tests/wave2-cluster-d.test.js +0 -180
  215. package/dist/tests/wave2-cluster-e.test.js +0 -179
  216. package/dist/tests/wiki-qa-fixes.test.js +0 -270
  217. package/dist/tests/wiki.test.js +0 -529
  218. package/dist/tests/workflow-cli.test.js +0 -271
  219. package/dist/tests/workflow-markdown.test.js +0 -171
  220. package/dist/tests/workflow-path-escape.test.js +0 -132
  221. package/dist/tests/workflow-qa-fixes.test.js +0 -377
  222. package/dist/tests/workflows/indexer-rejection.test.js +0 -213
  223. /package/dist/{src/commands → commands}/completions.js +0 -0
  224. /package/dist/{src/commands → commands}/curate.js +0 -0
  225. /package/dist/{src/commands → commands}/distill.js +0 -0
  226. /package/dist/{src/commands → commands}/events.js +0 -0
  227. /package/dist/{src/commands → commands}/info.js +0 -0
  228. /package/dist/{src/commands → commands}/init.js +0 -0
  229. /package/dist/{src/commands → commands}/install-audit.js +0 -0
  230. /package/dist/{src/commands → commands}/installed-stashes.js +0 -0
  231. /package/dist/{src/commands → commands}/migration-help.js +0 -0
  232. /package/dist/{src/commands → commands}/proposal.js +0 -0
  233. /package/dist/{src/commands → commands}/propose.js +0 -0
  234. /package/dist/{src/commands → commands}/reflect.js +0 -0
  235. /package/dist/{src/commands → commands}/remember.js +0 -0
  236. /package/dist/{src/commands → commands}/self-update.js +0 -0
  237. /package/dist/{src/commands → commands}/source-clone.js +0 -0
  238. /package/dist/{src/commands → commands}/vault.js +0 -0
  239. /package/dist/{src/core → core}/asset-ref.js +0 -0
  240. /package/dist/{src/core → core}/asset-registry.js +0 -0
  241. /package/dist/{src/core → core}/asset-spec.js +0 -0
  242. /package/dist/{src/core → core}/common.js +0 -0
  243. /package/dist/{src/core → core}/errors.js +0 -0
  244. /package/dist/{src/core → core}/events.js +0 -0
  245. /package/dist/{src/core → core}/frontmatter.js +0 -0
  246. /package/dist/{src/core → core}/lesson-lint.js +0 -0
  247. /package/dist/{src/core → core}/markdown.js +0 -0
  248. /package/dist/{src/core → core}/paths.js +0 -0
  249. /package/dist/{src/core → core}/proposals.js +0 -0
  250. /package/dist/{src/core → core}/warn.js +0 -0
  251. /package/dist/{src/core → core}/write-source.js +0 -0
  252. /package/dist/{src/indexer → indexer}/db.js +0 -0
  253. /package/dist/{src/indexer → indexer}/file-context.js +0 -0
  254. /package/dist/{src/indexer → indexer}/graph-boost.js +0 -0
  255. /package/dist/{src/indexer → indexer}/graph-extraction.js +0 -0
  256. /package/dist/{src/indexer → indexer}/manifest.js +0 -0
  257. /package/dist/{src/indexer → indexer}/memory-inference.js +0 -0
  258. /package/dist/{src/indexer → indexer}/metadata.js +0 -0
  259. /package/dist/{src/indexer → indexer}/search-fields.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/prompts.js +0 -0
  267. /package/dist/{src/integrations → integrations}/lockfile.js +0 -0
  268. /package/dist/{src/llm → llm}/client.js +0 -0
  269. /package/dist/{src/llm → llm}/embedder.js +0 -0
  270. /package/dist/{src/llm → llm}/embedders/cache.js +0 -0
  271. /package/dist/{src/llm → llm}/embedders/local.js +0 -0
  272. /package/dist/{src/llm → llm}/embedders/types.js +0 -0
  273. /package/dist/{src/llm → llm}/feature-gate.js +0 -0
  274. /package/dist/{src/llm → llm}/graph-extract.js +0 -0
  275. /package/dist/{src/llm → llm}/index-passes.js +0 -0
  276. /package/dist/{src/llm → llm}/memory-infer.js +0 -0
  277. /package/dist/{src/llm → llm}/metadata-enhance.js +0 -0
  278. /package/dist/{src/output → output}/context.js +0 -0
  279. /package/dist/{src/registry → registry}/create-provider-registry.js +0 -0
  280. /package/dist/{src/registry → registry}/factory.js +0 -0
  281. /package/dist/{src/registry → registry}/origin-resolve.js +0 -0
  282. /package/dist/{src/registry → registry}/providers/index.js +0 -0
  283. /package/dist/{src/registry → registry}/providers/skills-sh.js +0 -0
  284. /package/dist/{src/registry → registry}/providers/types.js +0 -0
  285. /package/dist/{src/registry → registry}/resolve.js +0 -0
  286. /package/dist/{src/registry → registry}/types.js +0 -0
  287. /package/dist/{src/setup → setup}/detect.js +0 -0
  288. /package/dist/{src/setup → setup}/ripgrep-install.js +0 -0
  289. /package/dist/{src/setup → setup}/ripgrep-resolve.js +0 -0
  290. /package/dist/{src/setup → setup}/steps.js +0 -0
  291. /package/dist/{src/sources → sources}/include.js +0 -0
  292. /package/dist/{src/sources → sources}/provider-factory.js +0 -0
  293. /package/dist/{src/sources → sources}/provider.js +0 -0
  294. /package/dist/{src/sources → sources}/providers/filesystem.js +0 -0
  295. /package/dist/{src/sources → sources}/providers/git.js +0 -0
  296. /package/dist/{src/sources → sources}/providers/index.js +0 -0
  297. /package/dist/{src/sources → sources}/providers/install-types.js +0 -0
  298. /package/dist/{src/sources → sources}/providers/npm.js +0 -0
  299. /package/dist/{src/sources → sources}/providers/provider-utils.js +0 -0
  300. /package/dist/{src/sources → sources}/providers/sync-from-ref.js +0 -0
  301. /package/dist/{src/sources → sources}/providers/tar-utils.js +0 -0
  302. /package/dist/{src/sources → sources}/providers/website.js +0 -0
  303. /package/dist/{src/sources → sources}/resolve.js +0 -0
  304. /package/dist/{src/sources → sources}/types.js +0 -0
  305. /package/dist/{src/templates → templates}/wiki-templates.js +0 -0
  306. /package/dist/{src/version.js → version.js} +0 -0
  307. /package/dist/{src/workflows → workflows}/authoring.js +0 -0
  308. /package/dist/{src/workflows → workflows}/cli.js +0 -0
  309. /package/dist/{src/workflows → workflows}/db.js +0 -0
  310. /package/dist/{src/workflows → workflows}/document-cache.js +0 -0
  311. /package/dist/{src/workflows → workflows}/parser.js +0 -0
  312. /package/dist/{src/workflows → workflows}/renderer.js +0 -0
  313. /package/dist/{src/workflows → workflows}/schema.js +0 -0
  314. /package/dist/{src/workflows → workflows}/validator.js +0 -0
@@ -1,272 +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 { akmSearch } from "../src/commands/search";
6
- import { saveConfig } from "../src/core/config";
7
- import { closeDatabase, openDatabase, rebuildFts, searchFts, setMeta, upsertEmbedding, upsertEntry, } from "../src/indexer/db";
8
- import { akmIndex } from "../src/indexer/indexer";
9
- import { clearEmbeddingCache } from "../src/llm/embedder";
10
- // ── Temp directory management ───────────────────────────────────────────────
11
- const createdTmpDirs = [];
12
- function createTmpDir(prefix = "akm-parallel-") {
13
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
14
- createdTmpDirs.push(dir);
15
- return dir;
16
- }
17
- afterAll(() => {
18
- for (const dir of createdTmpDirs) {
19
- fs.rmSync(dir, { recursive: true, force: true });
20
- }
21
- });
22
- // ── Helpers ─────────────────────────────────────────────────────────────────
23
- function writeFile(filePath, content = "") {
24
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
25
- fs.writeFileSync(filePath, content);
26
- }
27
- function tmpStash() {
28
- const dir = createTmpDir("akm-parallel-stash-");
29
- for (const sub of ["skills", "commands", "agents", "knowledge", "scripts"]) {
30
- fs.mkdirSync(path.join(dir, sub), { recursive: true });
31
- }
32
- return dir;
33
- }
34
- function tmpDbPath(label = "parallel") {
35
- const dir = createTmpDir(`akm-${label}-`);
36
- return path.join(dir, "test.db");
37
- }
38
- function makeEntry(overrides) {
39
- return {
40
- description: "A test entry",
41
- ...overrides,
42
- };
43
- }
44
- function insertTestEntry(db, key, opts) {
45
- const type = opts?.type ?? "script";
46
- const entry = makeEntry({ name: key, type, description: opts?.description ?? `Description for ${key}` });
47
- return upsertEntry(db, key, opts?.dirPath ?? "/test/dir", opts?.filePath ?? `/test/dir/${key}.ts`, opts?.stashDir ?? "/test/stash", entry, opts?.searchText ?? `${key} ${entry.description}`);
48
- }
49
- async function buildTestIndex(stashDir, files) {
50
- for (const [relPath, content] of Object.entries(files)) {
51
- const fullPath = path.join(stashDir, relPath);
52
- fs.mkdirSync(path.dirname(fullPath), { recursive: true });
53
- fs.writeFileSync(fullPath, content);
54
- }
55
- process.env.AKM_STASH_DIR = stashDir;
56
- saveConfig({ semanticSearchMode: "off" });
57
- await akmIndex({ stashDir, full: true });
58
- }
59
- // ── Environment isolation ───────────────────────────────────────────────────
60
- const originalXdgCacheHome = process.env.XDG_CACHE_HOME;
61
- const originalXdgConfigHome = process.env.XDG_CONFIG_HOME;
62
- const originalAkmStashDir = process.env.AKM_STASH_DIR;
63
- let testCacheDir = "";
64
- let testConfigDir = "";
65
- beforeEach(() => {
66
- testCacheDir = createTmpDir("akm-parallel-cache-");
67
- testConfigDir = createTmpDir("akm-parallel-config-");
68
- process.env.XDG_CACHE_HOME = testCacheDir;
69
- process.env.XDG_CONFIG_HOME = testConfigDir;
70
- clearEmbeddingCache();
71
- });
72
- afterEach(() => {
73
- if (originalXdgCacheHome === undefined) {
74
- delete process.env.XDG_CACHE_HOME;
75
- }
76
- else {
77
- process.env.XDG_CACHE_HOME = originalXdgCacheHome;
78
- }
79
- if (originalXdgConfigHome === undefined) {
80
- delete process.env.XDG_CONFIG_HOME;
81
- }
82
- else {
83
- process.env.XDG_CONFIG_HOME = originalXdgConfigHome;
84
- }
85
- if (originalAkmStashDir === undefined) {
86
- delete process.env.AKM_STASH_DIR;
87
- }
88
- else {
89
- process.env.AKM_STASH_DIR = originalAkmStashDir;
90
- }
91
- if (testCacheDir) {
92
- fs.rmSync(testCacheDir, { recursive: true, force: true });
93
- testCacheDir = "";
94
- }
95
- if (testConfigDir) {
96
- fs.rmSync(testConfigDir, { recursive: true, force: true });
97
- testConfigDir = "";
98
- }
99
- });
100
- // ── Test 1: Search results identical to sequential execution ────────────────
101
- describe("Parallel search: result parity", () => {
102
- test("FTS-only search results are identical with parallel execution", async () => {
103
- const stashDir = tmpStash();
104
- writeFile(path.join(stashDir, "scripts", "deploy", "deploy.sh"), "#!/bin/bash\necho deploy\n");
105
- writeFile(path.join(stashDir, "scripts", "deploy", ".stash.json"), JSON.stringify({
106
- entries: [
107
- {
108
- name: "deploy",
109
- type: "script",
110
- description: "Deploy application to production servers",
111
- tags: ["deploy", "production"],
112
- filename: "deploy.sh",
113
- },
114
- ],
115
- }));
116
- writeFile(path.join(stashDir, "scripts", "test", "test.sh"), "#!/bin/bash\necho test\n");
117
- writeFile(path.join(stashDir, "scripts", "test", ".stash.json"), JSON.stringify({
118
- entries: [
119
- {
120
- name: "test-runner",
121
- type: "script",
122
- description: "Run test suite for deployment validation",
123
- tags: ["test", "deploy"],
124
- filename: "test.sh",
125
- },
126
- ],
127
- }));
128
- await buildTestIndex(stashDir, {});
129
- // Run the same query twice and verify identical results
130
- const result1 = await akmSearch({ query: "deploy", source: "local" });
131
- const result2 = await akmSearch({ query: "deploy", source: "local" });
132
- const localHits1 = result1.hits.filter((h) => h.type !== "registry");
133
- const localHits2 = result2.hits.filter((h) => h.type !== "registry");
134
- expect(localHits1.length).toBeGreaterThan(0);
135
- expect(localHits1.length).toBe(localHits2.length);
136
- for (let i = 0; i < localHits1.length; i++) {
137
- expect(localHits1[i].name).toBe(localHits2[i].name);
138
- expect(localHits1[i].score).toBe(localHits2[i].score);
139
- expect(localHits1[i].ref).toBe(localHits2[i].ref);
140
- }
141
- });
142
- });
143
- // ── Test 2: Embedding cache ─────────────────────────────────────────────────
144
- describe("Embedding cache", () => {
145
- test("clearEmbeddingCache is callable without error", () => {
146
- // Verify the exported function exists and can be called
147
- expect(() => clearEmbeddingCache()).not.toThrow();
148
- });
149
- test("clearEmbeddingCache is idempotent and does not throw on repeated calls", () => {
150
- clearEmbeddingCache();
151
- clearEmbeddingCache();
152
- // Verify idempotence: calling clear multiple times should never throw
153
- expect(() => clearEmbeddingCache()).not.toThrow();
154
- });
155
- });
156
- // ── Test 3: Search works when vector search is unavailable ──────────────────
157
- describe("Parallel search: vector unavailable", () => {
158
- test("search returns FTS results when no embeddings exist in DB", async () => {
159
- const stashDir = tmpStash();
160
- writeFile(path.join(stashDir, "scripts", "lint", "lint.sh"), "#!/bin/bash\necho lint\n");
161
- writeFile(path.join(stashDir, "scripts", "lint", ".stash.json"), JSON.stringify({
162
- entries: [
163
- {
164
- name: "lint",
165
- type: "script",
166
- description: "Lint source code for errors and style violations",
167
- filename: "lint.sh",
168
- },
169
- ],
170
- }));
171
- await buildTestIndex(stashDir, {});
172
- const result = await akmSearch({ query: "lint", source: "local" });
173
- const localHits = result.hits.filter((h) => h.type !== "registry");
174
- expect(localHits.length).toBeGreaterThanOrEqual(1);
175
- const lintHit = localHits.find((h) => h.name === "lint");
176
- expect(lintHit).toBeDefined();
177
- expect(lintHit?.score).toBeGreaterThan(0);
178
- // With semanticSearchMode disabled, should use FTS ranking
179
- expect(lintHit?.whyMatched).toContain("fts bm25 relevance");
180
- });
181
- });
182
- // ── Test 4: Search works when FTS returns empty ─────────────────────────────
183
- describe("Parallel search: FTS empty", () => {
184
- test("search returns empty when FTS has no matches and no vec", async () => {
185
- const stashDir = tmpStash();
186
- writeFile(path.join(stashDir, "scripts", "alpha", "alpha.sh"), "#!/bin/bash\necho alpha\n");
187
- writeFile(path.join(stashDir, "scripts", "alpha", ".stash.json"), JSON.stringify({
188
- entries: [
189
- {
190
- name: "alpha",
191
- type: "script",
192
- description: "Alpha tool for testing",
193
- filename: "alpha.sh",
194
- },
195
- ],
196
- }));
197
- await buildTestIndex(stashDir, {});
198
- // Query for something that won't match any FTS tokens
199
- const result = await akmSearch({ query: "zzzznonexistent", source: "local" });
200
- const localHits = result.hits.filter((h) => h.type !== "registry");
201
- // Should return 0 results without crashing
202
- expect(localHits.length).toBe(0);
203
- });
204
- test("search with DB having vec entries but FTS empty returns vec-only results", async () => {
205
- // This test uses the low-level DB API to set up a scenario where
206
- // FTS has no matches but vec does (simulating semantic-only match)
207
- const dbPath = tmpDbPath("fts-empty");
208
- const db = openDatabase(dbPath, { embeddingDim: 4 });
209
- try {
210
- const id = insertTestEntry(db, "semantic-tool", {
211
- description: "A tool found only via semantic similarity",
212
- searchText: "vector embedding similarity neural network",
213
- stashDir: "/test/stash",
214
- dirPath: "/test/dir",
215
- filePath: "/test/dir/semantic-tool.ts",
216
- });
217
- upsertEmbedding(db, id, [1, 0, 0, 0]);
218
- setMeta(db, "hasEmbeddings", "1");
219
- setMeta(db, "stashDir", "/test/stash");
220
- rebuildFts(db);
221
- // Searching for "garbledftsquery" won't match FTS, but we verify
222
- // the search doesn't crash when FTS is empty
223
- const ftsResults = searchFts(db, "garbledftsquery", 10);
224
- expect(ftsResults).toHaveLength(0);
225
- }
226
- finally {
227
- closeDatabase(db);
228
- }
229
- });
230
- });
231
- // ── Test 5: Promise.all structure verification ──────────────────────────────
232
- describe("Parallel search: FTS result ordering", () => {
233
- test("FTS search returns results sorted by score descending (semanticSearchMode off)", async () => {
234
- // NOTE: Despite the original "hybrid" naming, this test runs with
235
- // semanticSearchMode: "off", so only FTS scoring is exercised. True hybrid
236
- // (FTS + vector) coverage lives in tests/vector-search.test.ts.
237
- const stashDir = tmpStash();
238
- writeFile(path.join(stashDir, "scripts", "build", "build.sh"), "#!/bin/bash\necho build\n");
239
- writeFile(path.join(stashDir, "scripts", "build", ".stash.json"), JSON.stringify({
240
- entries: [
241
- {
242
- name: "build",
243
- type: "script",
244
- description: "Build the project from source",
245
- tags: ["build", "compile"],
246
- filename: "build.sh",
247
- },
248
- ],
249
- }));
250
- writeFile(path.join(stashDir, "scripts", "compile", "compile.sh"), "#!/bin/bash\necho compile\n");
251
- writeFile(path.join(stashDir, "scripts", "compile", ".stash.json"), JSON.stringify({
252
- entries: [
253
- {
254
- name: "compile",
255
- type: "script",
256
- description: "Compile source code into binary artifacts",
257
- tags: ["compile"],
258
- filename: "compile.sh",
259
- },
260
- ],
261
- }));
262
- await buildTestIndex(stashDir, {});
263
- const result = await akmSearch({ query: "build compile", source: "local" });
264
- const localHits = result.hits.filter((h) => h.type !== "registry");
265
- // Both entries should be found
266
- expect(localHits.length).toBeGreaterThanOrEqual(1);
267
- // Results should be sorted by score descending
268
- for (let i = 1; i < localHits.length; i++) {
269
- expect(localHits[i - 1].score ?? 0).toBeGreaterThanOrEqual(localHits[i].score ?? 0);
270
- }
271
- });
272
- });
@@ -1,365 +0,0 @@
1
- import { 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 { getDbPath } from "../src/core/paths";
6
- import { closeDatabase, getAllEntries, openDatabase } from "../src/indexer/db";
7
- import { akmIndex } from "../src/indexer/indexer";
8
- import { extractCommandParameters, generateMetadataFlat } from "../src/indexer/metadata";
9
- import { buildSearchText } from "../src/indexer/search-fields";
10
- let testConfigDir = "";
11
- let testCacheDir = "";
12
- const originalXdgConfigHome = process.env.XDG_CONFIG_HOME;
13
- const originalXdgCacheHome = process.env.XDG_CACHE_HOME;
14
- beforeEach(() => {
15
- testConfigDir = fs.mkdtempSync(path.join(os.tmpdir(), "akm-param-config-"));
16
- testCacheDir = fs.mkdtempSync(path.join(os.tmpdir(), "akm-param-cache-"));
17
- process.env.XDG_CONFIG_HOME = testConfigDir;
18
- process.env.XDG_CACHE_HOME = testCacheDir;
19
- const dbPath = getDbPath();
20
- for (const f of [dbPath, `${dbPath}-wal`, `${dbPath}-shm`]) {
21
- try {
22
- fs.unlinkSync(f);
23
- }
24
- catch {
25
- /* ignore */
26
- }
27
- }
28
- });
29
- afterEach(() => {
30
- if (originalXdgConfigHome === undefined) {
31
- delete process.env.XDG_CONFIG_HOME;
32
- }
33
- else {
34
- process.env.XDG_CONFIG_HOME = originalXdgConfigHome;
35
- }
36
- if (originalXdgCacheHome === undefined) {
37
- delete process.env.XDG_CACHE_HOME;
38
- }
39
- else {
40
- process.env.XDG_CACHE_HOME = originalXdgCacheHome;
41
- }
42
- if (testConfigDir) {
43
- fs.rmSync(testConfigDir, { recursive: true, force: true });
44
- testConfigDir = "";
45
- }
46
- if (testCacheDir) {
47
- fs.rmSync(testCacheDir, { recursive: true, force: true });
48
- testCacheDir = "";
49
- }
50
- });
51
- function tmpStash() {
52
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), "akm-param-"));
53
- for (const sub of ["skills", "commands", "agents", "knowledge", "scripts"]) {
54
- fs.mkdirSync(path.join(dir, sub), { recursive: true });
55
- }
56
- return dir;
57
- }
58
- function writeFile(filePath, content = "") {
59
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
60
- fs.writeFileSync(filePath, content);
61
- }
62
- // ── Test 1: Commands with $1, $2 have parameters auto-extracted ──────────
63
- describe("command parameter extraction", () => {
64
- test("commands with $1 and $2 have parameters auto-extracted", async () => {
65
- const stashDir = tmpStash();
66
- writeFile(path.join(stashDir, "commands", "deploy.md"), '---\ndescription: "Deploy a Docker image"\n---\nDeploy $1 to environment $2\n');
67
- const result = await generateMetadataFlat(stashDir, [path.join(stashDir, "commands", "deploy.md")]);
68
- expect(result.entries.length).toBe(1);
69
- const entry = result.entries[0];
70
- expect(entry.parameters).toBeDefined();
71
- expect(entry.parameters?.length).toBe(2);
72
- expect(entry.parameters?.[0].name).toBe("$1");
73
- expect(entry.parameters?.[1].name).toBe("$2");
74
- });
75
- test("commands with $ARGUMENTS have parameters auto-extracted", async () => {
76
- const stashDir = tmpStash();
77
- writeFile(path.join(stashDir, "commands", "run.md"), '---\ndescription: "Run a command"\n---\nExecute the following with $ARGUMENTS\n');
78
- const result = await generateMetadataFlat(stashDir, [path.join(stashDir, "commands", "run.md")]);
79
- expect(result.entries.length).toBe(1);
80
- const entry = result.entries[0];
81
- expect(entry.parameters).toBeDefined();
82
- expect(entry.parameters?.some((p) => p.name === "ARGUMENTS")).toBe(true);
83
- });
84
- test("commands with {{named}} placeholders have parameters auto-extracted", async () => {
85
- const stashDir = tmpStash();
86
- writeFile(path.join(stashDir, "commands", "build.md"), '---\ndescription: "Build image"\n---\ndocker build -t {{image_name}} --platform {{platform}} .\n');
87
- const result = await generateMetadataFlat(stashDir, [path.join(stashDir, "commands", "build.md")]);
88
- expect(result.entries.length).toBe(1);
89
- const entry = result.entries[0];
90
- expect(entry.parameters).toBeDefined();
91
- expect(entry.parameters?.some((p) => p.name === "image_name")).toBe(true);
92
- expect(entry.parameters?.some((p) => p.name === "platform")).toBe(true);
93
- });
94
- });
95
- // ── Test 2: Scripts with @param JSDoc have parameters extracted ──────────
96
- describe("script @param extraction", () => {
97
- test("scripts with @param JSDoc have parameters extracted", async () => {
98
- const stashDir = tmpStash();
99
- writeFile(path.join(stashDir, "scripts", "deploy.ts"), [
100
- "/**",
101
- " * Deploy to production",
102
- " * @param name - The deployment name",
103
- " * @param environment - Target environment (staging or production)",
104
- " */",
105
- "console.log('deploy')",
106
- ].join("\n"));
107
- const result = await generateMetadataFlat(stashDir, [path.join(stashDir, "scripts", "deploy.ts")]);
108
- expect(result.entries.length).toBe(1);
109
- const entry = result.entries[0];
110
- expect(entry.parameters).toBeDefined();
111
- expect(entry.parameters?.length).toBe(2);
112
- expect(entry.parameters?.[0].name).toBe("name");
113
- expect(entry.parameters?.[0].description).toBe("The deployment name");
114
- expect(entry.parameters?.[1].name).toBe("environment");
115
- expect(entry.parameters?.[1].description).toBe("Target environment (staging or production)");
116
- });
117
- test("scripts with typed @param have type extracted", async () => {
118
- const stashDir = tmpStash();
119
- writeFile(path.join(stashDir, "scripts", "resize.ts"), [
120
- "/**",
121
- " * Resize an image",
122
- " * @param {string} filename - The image file path",
123
- " * @param {number} width - Target width in pixels",
124
- " */",
125
- "console.log('resize')",
126
- ].join("\n"));
127
- const result = await generateMetadataFlat(stashDir, [path.join(stashDir, "scripts", "resize.ts")]);
128
- expect(result.entries.length).toBe(1);
129
- const entry = result.entries[0];
130
- expect(entry.parameters).toBeDefined();
131
- expect(entry.parameters?.length).toBe(2);
132
- expect(entry.parameters?.[0].name).toBe("filename");
133
- expect(entry.parameters?.[0].type).toBe("string");
134
- expect(entry.parameters?.[0].description).toBe("The image file path");
135
- expect(entry.parameters?.[1].name).toBe("width");
136
- expect(entry.parameters?.[1].type).toBe("number");
137
- expect(entry.parameters?.[1].description).toBe("Target width in pixels");
138
- });
139
- test("bash scripts with # @param have parameters extracted", async () => {
140
- const stashDir = tmpStash();
141
- writeFile(path.join(stashDir, "scripts", "backup.sh"), [
142
- "#!/bin/bash",
143
- "# Backup database",
144
- "# @param source - The source database name",
145
- "# @param destination - The backup destination path",
146
- "echo 'backup'",
147
- ].join("\n"));
148
- const result = await generateMetadataFlat(stashDir, [path.join(stashDir, "scripts", "backup.sh")]);
149
- expect(result.entries.length).toBe(1);
150
- const entry = result.entries[0];
151
- expect(entry.parameters).toBeDefined();
152
- expect(entry.parameters?.length).toBe(2);
153
- expect(entry.parameters?.[0].name).toBe("source");
154
- expect(entry.parameters?.[0].description).toBe("The source database name");
155
- expect(entry.parameters?.[1].name).toBe("destination");
156
- expect(entry.parameters?.[1].description).toBe("The backup destination path");
157
- });
158
- });
159
- // ── Test 3: Frontmatter params key is parsed into parameters ─────────────
160
- describe("frontmatter params extraction", () => {
161
- test("frontmatter params key is parsed into parameters", async () => {
162
- const stashDir = tmpStash();
163
- writeFile(path.join(stashDir, "commands", "provision.md"), [
164
- "---",
165
- 'description: "Provision infrastructure"',
166
- "params:",
167
- " region: AWS region to deploy to",
168
- " instance_type: EC2 instance type",
169
- "---",
170
- "Provision $1 in $2",
171
- ].join("\n"));
172
- const result = await generateMetadataFlat(stashDir, [path.join(stashDir, "commands", "provision.md")]);
173
- expect(result.entries.length).toBe(1);
174
- const entry = result.entries[0];
175
- expect(entry.parameters).toBeDefined();
176
- expect(entry.parameters?.some((p) => p.name === "region" && p.description === "AWS region to deploy to")).toBe(true);
177
- expect(entry.parameters?.some((p) => p.name === "instance_type" && p.description === "EC2 instance type")).toBe(true);
178
- });
179
- });
180
- // ── Test 4: Parameter names are included in search text ──────────────────
181
- describe("parameter search text inclusion", () => {
182
- test("parameter names and descriptions are included in search text", () => {
183
- const entry = {
184
- name: "deploy",
185
- type: "command",
186
- description: "Deploy a service",
187
- parameters: [
188
- { name: "image_name", description: "Docker image name to deploy" },
189
- { name: "environment", description: "Target environment" },
190
- ],
191
- };
192
- const text = buildSearchText(entry);
193
- expect(text).toContain("image_name");
194
- expect(text).toContain("docker image name to deploy");
195
- expect(text).toContain("environment");
196
- expect(text).toContain("target environment");
197
- });
198
- });
199
- // ── Test 5: Assets without parameters have undefined parameters field ────
200
- describe("no parameters", () => {
201
- test("assets without parameters have undefined parameters field", async () => {
202
- const stashDir = tmpStash();
203
- writeFile(path.join(stashDir, "knowledge", "guide.md"), '---\ndescription: "A guide"\n---\n# Getting Started\nIntro.\n');
204
- const result = await generateMetadataFlat(stashDir, [path.join(stashDir, "knowledge", "guide.md")]);
205
- expect(result.entries.length).toBe(1);
206
- expect(result.entries[0].parameters).toBeUndefined();
207
- });
208
- });
209
- // ── Test 6: Multiple parameters are extracted in order ───────────────────
210
- describe("parameter ordering", () => {
211
- test("multiple parameters are extracted in order", async () => {
212
- const stashDir = tmpStash();
213
- writeFile(path.join(stashDir, "commands", "multi.md"), '---\ndescription: "Multi param command"\n---\nRun $1 then $2 then $3 with $ARGUMENTS\n');
214
- const result = await generateMetadataFlat(stashDir, [path.join(stashDir, "commands", "multi.md")]);
215
- expect(result.entries.length).toBe(1);
216
- const entry = result.entries[0];
217
- expect(entry.parameters).toBeDefined();
218
- // $ARGUMENTS comes first (matches existing extractParameters logic), then $1, $2, $3
219
- const names = entry.parameters?.map((p) => p.name);
220
- expect(names).toEqual(["ARGUMENTS", "$1", "$2", "$3"]);
221
- });
222
- });
223
- // ── Test 7: Parameter descriptions are captured ──────────────────────────
224
- describe("parameter descriptions", () => {
225
- test("JSDoc @param descriptions are captured accurately", async () => {
226
- const stashDir = tmpStash();
227
- writeFile(path.join(stashDir, "scripts", "transform.ts"), [
228
- "/**",
229
- " * Transform data",
230
- " * @param inputFile - Path to the input CSV file",
231
- " * @param outputFormat - Output format (json, xml, yaml)",
232
- " * @param verbose - Enable verbose logging",
233
- " */",
234
- "console.log('transform')",
235
- ].join("\n"));
236
- const result = await generateMetadataFlat(stashDir, [path.join(stashDir, "scripts", "transform.ts")]);
237
- expect(result.entries.length).toBe(1);
238
- const entry = result.entries[0];
239
- expect(entry.parameters).toBeDefined();
240
- expect(entry.parameters?.length).toBe(3);
241
- expect(entry.parameters?.[0]).toEqual({ name: "inputFile", description: "Path to the input CSV file" });
242
- expect(entry.parameters?.[1]).toEqual({ name: "outputFormat", description: "Output format (json, xml, yaml)" });
243
- expect(entry.parameters?.[2]).toEqual({ name: "verbose", description: "Enable verbose logging" });
244
- });
245
- });
246
- // ── Test 8: validateStashEntry round-trips parameters ────────────────────
247
- describe("validateStashEntry with parameters", () => {
248
- test("validateStashEntry preserves valid parameters", async () => {
249
- const { validateStashEntry } = await import("../src/indexer/metadata");
250
- const raw = {
251
- name: "test-cmd",
252
- type: "command",
253
- parameters: [
254
- { name: "image", type: "string", description: "Docker image", required: true, default: "latest" },
255
- { name: "count", type: "number", description: "Instance count" },
256
- ],
257
- };
258
- const entry = validateStashEntry(raw);
259
- expect(entry).not.toBeNull();
260
- expect(entry?.parameters).toBeDefined();
261
- expect(entry?.parameters?.length).toBe(2);
262
- expect(entry?.parameters?.[0].name).toBe("image");
263
- expect(entry?.parameters?.[0].type).toBe("string");
264
- expect(entry?.parameters?.[0].description).toBe("Docker image");
265
- expect(entry?.parameters?.[0].required).toBe(true);
266
- expect(entry?.parameters?.[0].default).toBe("latest");
267
- expect(entry?.parameters?.[1].name).toBe("count");
268
- expect(entry?.parameters?.[1].type).toBe("number");
269
- });
270
- test("validateStashEntry filters invalid parameter objects", async () => {
271
- const { validateStashEntry } = await import("../src/indexer/metadata");
272
- const raw = {
273
- name: "test-cmd",
274
- type: "command",
275
- parameters: [
276
- { name: "valid", description: "A valid param" },
277
- { notAName: "invalid" }, // missing name field
278
- "just a string", // not an object
279
- null,
280
- ],
281
- };
282
- const entry = validateStashEntry(raw);
283
- expect(entry).not.toBeNull();
284
- expect(entry?.parameters).toBeDefined();
285
- expect(entry?.parameters?.length).toBe(1);
286
- expect(entry?.parameters?.[0].name).toBe("valid");
287
- });
288
- });
289
- // ── Test 9: Full indexing pipeline includes parameters in search text ────
290
- describe("indexing pipeline with parameters", () => {
291
- test("indexed command entries include parameters in search text", async () => {
292
- const stashDir = tmpStash();
293
- writeFile(path.join(stashDir, "commands", "docker-deploy.md"), '---\ndescription: "Deploy Docker container"\n---\nDeploy $1 to $2 using {{registry_url}}\n');
294
- process.env.AKM_STASH_DIR = stashDir;
295
- await akmIndex({ stashDir });
296
- const db = openDatabase();
297
- const entries = getAllEntries(db, "command");
298
- expect(entries.length).toBe(1);
299
- // The search text stored in the DB should include parameter names
300
- const searchText = entries[0].searchText;
301
- expect(searchText).toContain("registry_url");
302
- closeDatabase(db);
303
- });
304
- });
305
- // ── Test 10: Knowledge articles should NOT have command parameters extracted ──
306
- describe("knowledge articles skip command parameter extraction", () => {
307
- test("knowledge article with {{variable}} should NOT have parameters extracted", async () => {
308
- const stashDir = tmpStash();
309
- writeFile(path.join(stashDir, "knowledge", "template-guide.md"), [
310
- "---",
311
- 'description: "Template syntax guide"',
312
- "---",
313
- "# Template Guide",
314
- "",
315
- "Use {{variable}} placeholders in your templates.",
316
- "For example: {{project_name}} and {{region}}.",
317
- ].join("\n"));
318
- const result = await generateMetadataFlat(stashDir, [path.join(stashDir, "knowledge", "template-guide.md")]);
319
- expect(result.entries.length).toBe(1);
320
- expect(result.entries[0].type).toBe("knowledge");
321
- // Knowledge articles should NOT have command parameters extracted
322
- expect(result.entries[0].parameters).toBeUndefined();
323
- });
324
- test("knowledge article with frontmatter params should still have parameters", async () => {
325
- const stashDir = tmpStash();
326
- writeFile(path.join(stashDir, "knowledge", "config-ref.md"), [
327
- "---",
328
- 'description: "Configuration reference"',
329
- "params:",
330
- " api_key: Your API key",
331
- "---",
332
- "# Configuration",
333
- "",
334
- "Set {{api_key}} in your config file.",
335
- ].join("\n"));
336
- const result = await generateMetadataFlat(stashDir, [path.join(stashDir, "knowledge", "config-ref.md")]);
337
- expect(result.entries.length).toBe(1);
338
- expect(result.entries[0].type).toBe("knowledge");
339
- // Frontmatter params: should still be extracted for all types
340
- expect(result.entries[0].parameters).toBeDefined();
341
- expect(result.entries[0].parameters?.length).toBe(1);
342
- expect(result.entries[0].parameters?.[0].name).toBe("api_key");
343
- // But {{api_key}} from the body should NOT be extracted (not a command)
344
- });
345
- });
346
- // ── Test 11: Positional regex should not match $10 as $1 ─────────────────
347
- describe("positional parameter boundary matching", () => {
348
- test("$10 should not produce a $1 parameter", () => {
349
- const params = extractCommandParameters("Process $10 items");
350
- expect(params).toBeUndefined();
351
- });
352
- test("$1 followed by non-digit is still matched", () => {
353
- const params = extractCommandParameters("Deploy $1 to $2");
354
- expect(params).toBeDefined();
355
- expect(params?.length).toBe(2);
356
- expect(params?.[0].name).toBe("$1");
357
- expect(params?.[1].name).toBe("$2");
358
- });
359
- test("$1 at end of string is matched", () => {
360
- const params = extractCommandParameters("Deploy $1");
361
- expect(params).toBeDefined();
362
- expect(params?.length).toBe(1);
363
- expect(params?.[0].name).toBe("$1");
364
- });
365
- });