akm-cli 0.7.0 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (332) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/{src/cli.js → cli.js} +22 -8
  3. package/dist/{src/commands → commands}/installed-stashes.js +1 -1
  4. package/dist/{src/commands → commands}/source-add.js +1 -1
  5. package/dist/{src/core → core}/common.js +16 -1
  6. package/dist/{src/core → core}/config.js +5 -2
  7. package/dist/{src/indexer → indexer}/db-search.js +16 -1
  8. package/dist/{src/indexer → indexer}/graph-extraction.js +5 -3
  9. package/dist/{src/indexer → indexer}/indexer.js +27 -11
  10. package/dist/{src/indexer → indexer}/memory-inference.js +47 -58
  11. package/dist/{src/indexer → indexer}/search-source.js +1 -1
  12. package/dist/{src/llm → llm}/client.js +61 -1
  13. package/dist/{src/llm → llm}/embedder.js +8 -5
  14. package/dist/{src/llm → llm}/embedders/local.js +8 -2
  15. package/dist/{src/llm → llm}/embedders/remote.js +4 -2
  16. package/dist/{src/llm → llm}/graph-extract.js +4 -4
  17. package/dist/llm/memory-infer.js +114 -0
  18. package/dist/{src/llm → llm}/metadata-enhance.js +2 -2
  19. package/dist/{src/output → output}/cli-hints.js +2 -0
  20. package/dist/{src/setup → setup}/setup.js +30 -20
  21. package/dist/sources/providers/website.js +27 -0
  22. package/dist/{src/sources/providers/website.js → sources/website-ingest.js} +38 -51
  23. package/docs/README.md +7 -0
  24. package/docs/migration/release-notes/0.7.0.md +14 -0
  25. package/package.json +11 -8
  26. package/dist/src/llm/memory-infer.js +0 -86
  27. package/dist/tests/add-website-source.test.js +0 -119
  28. package/dist/tests/agent/agent-config-loader.test.js +0 -70
  29. package/dist/tests/agent/agent-config.test.js +0 -221
  30. package/dist/tests/agent/agent-detect.test.js +0 -100
  31. package/dist/tests/agent/agent-spawn.test.js +0 -234
  32. package/dist/tests/agent-output.test.js +0 -186
  33. package/dist/tests/architecture/agent-no-llm-sdk-guard.test.js +0 -103
  34. package/dist/tests/architecture/agent-spawn-seam.test.js +0 -193
  35. package/dist/tests/architecture/llm-stateless-seam.test.js +0 -112
  36. package/dist/tests/asset-ref.test.js +0 -192
  37. package/dist/tests/asset-registry.test.js +0 -103
  38. package/dist/tests/asset-spec.test.js +0 -241
  39. package/dist/tests/bench/attribution.test.js +0 -996
  40. package/dist/tests/bench/cleanup-sigint.test.js +0 -83
  41. package/dist/tests/bench/cleanup.js +0 -234
  42. package/dist/tests/bench/cleanup.test.js +0 -166
  43. package/dist/tests/bench/cli.js +0 -1018
  44. package/dist/tests/bench/cli.test.js +0 -445
  45. package/dist/tests/bench/compare.test.js +0 -556
  46. package/dist/tests/bench/corpus.js +0 -317
  47. package/dist/tests/bench/corpus.test.js +0 -258
  48. package/dist/tests/bench/doctor.js +0 -525
  49. package/dist/tests/bench/driver.js +0 -401
  50. package/dist/tests/bench/driver.test.js +0 -584
  51. package/dist/tests/bench/environment.js +0 -233
  52. package/dist/tests/bench/environment.test.js +0 -199
  53. package/dist/tests/bench/evolve-metrics.js +0 -179
  54. package/dist/tests/bench/evolve-metrics.test.js +0 -187
  55. package/dist/tests/bench/evolve.js +0 -647
  56. package/dist/tests/bench/evolve.test.js +0 -624
  57. package/dist/tests/bench/failure-modes.test.js +0 -349
  58. package/dist/tests/bench/feedback-integrity.test.js +0 -457
  59. package/dist/tests/bench/leakage.test.js +0 -228
  60. package/dist/tests/bench/learning-curve.test.js +0 -134
  61. package/dist/tests/bench/metrics.js +0 -2395
  62. package/dist/tests/bench/metrics.test.js +0 -1150
  63. package/dist/tests/bench/no-os-tmpdir-invariant.test.js +0 -43
  64. package/dist/tests/bench/opencode-config.js +0 -194
  65. package/dist/tests/bench/opencode-config.test.js +0 -370
  66. package/dist/tests/bench/report.js +0 -1885
  67. package/dist/tests/bench/report.test.js +0 -1038
  68. package/dist/tests/bench/run-config.js +0 -355
  69. package/dist/tests/bench/run-config.test.js +0 -298
  70. package/dist/tests/bench/run-curate-test.js +0 -32
  71. package/dist/tests/bench/run-failing-tasks.js +0 -56
  72. package/dist/tests/bench/run-full-bench.js +0 -51
  73. package/dist/tests/bench/run-items36-targeted.js +0 -69
  74. package/dist/tests/bench/run-nano-quick.js +0 -42
  75. package/dist/tests/bench/run-waveg-targeted.js +0 -62
  76. package/dist/tests/bench/runner.js +0 -699
  77. package/dist/tests/bench/runner.test.js +0 -958
  78. package/dist/tests/bench/search-bridge.test.js +0 -331
  79. package/dist/tests/bench/tmp.js +0 -131
  80. package/dist/tests/bench/trajectory.js +0 -116
  81. package/dist/tests/bench/trajectory.test.js +0 -127
  82. package/dist/tests/bench/verifier.js +0 -114
  83. package/dist/tests/bench/verifier.test.js +0 -118
  84. package/dist/tests/bench/workflow-evaluator.js +0 -557
  85. package/dist/tests/bench/workflow-evaluator.test.js +0 -421
  86. package/dist/tests/bench/workflow-spec.js +0 -345
  87. package/dist/tests/bench/workflow-spec.test.js +0 -363
  88. package/dist/tests/bench/workflow-trace.js +0 -472
  89. package/dist/tests/bench/workflow-trace.test.js +0 -254
  90. package/dist/tests/benchmark-search-quality.js +0 -536
  91. package/dist/tests/benchmark-suite.js +0 -1441
  92. package/dist/tests/capture-cli.test.js +0 -112
  93. package/dist/tests/cli-errors.test.js +0 -204
  94. package/dist/tests/commands/events.test.js +0 -370
  95. package/dist/tests/commands/history.test.js +0 -418
  96. package/dist/tests/commands/import.test.js +0 -103
  97. package/dist/tests/commands/proposal-cli.test.js +0 -209
  98. package/dist/tests/commands/reflect-propose-cli.test.js +0 -333
  99. package/dist/tests/commands/remember.test.js +0 -97
  100. package/dist/tests/commands/scope-flags.test.js +0 -300
  101. package/dist/tests/commands/search.test.js +0 -537
  102. package/dist/tests/commands/show-indexer-parity.test.js +0 -117
  103. package/dist/tests/commands/show.test.js +0 -294
  104. package/dist/tests/common.test.js +0 -266
  105. package/dist/tests/completions.test.js +0 -142
  106. package/dist/tests/config-cli.test.js +0 -193
  107. package/dist/tests/config-llm-features.test.js +0 -139
  108. package/dist/tests/config.test.js +0 -569
  109. package/dist/tests/contracts/migration-baseline.test.js +0 -43
  110. package/dist/tests/contracts/reflect-propose-envelope.test.js +0 -139
  111. package/dist/tests/contracts/spec-helpers.js +0 -46
  112. package/dist/tests/contracts/v1-spec-section-11-proposal-queue.test.js +0 -228
  113. package/dist/tests/contracts/v1-spec-section-12-agent-config.test.js +0 -56
  114. package/dist/tests/contracts/v1-spec-section-13-lesson-type.test.js +0 -34
  115. package/dist/tests/contracts/v1-spec-section-14-llm-features.test.js +0 -94
  116. package/dist/tests/contracts/v1-spec-section-4-1-asset-types.test.js +0 -39
  117. package/dist/tests/contracts/v1-spec-section-4-2-quality-rules.test.js +0 -44
  118. package/dist/tests/contracts/v1-spec-section-5-configuration.test.js +0 -47
  119. package/dist/tests/contracts/v1-spec-section-6-orchestration.test.js +0 -40
  120. package/dist/tests/contracts/v1-spec-section-7-module-layout.test.js +0 -58
  121. package/dist/tests/contracts/v1-spec-section-8-extension-points.test.js +0 -34
  122. package/dist/tests/contracts/v1-spec-section-9-4-cli-surface.test.js +0 -75
  123. package/dist/tests/contracts/v1-spec-section-9-7-llm-agent-boundary.test.js +0 -36
  124. package/dist/tests/core/write-source.test.js +0 -366
  125. package/dist/tests/curate-command.test.js +0 -87
  126. package/dist/tests/db-scoring.test.js +0 -201
  127. package/dist/tests/db.test.js +0 -654
  128. package/dist/tests/distill-cli-flag.test.js +0 -208
  129. package/dist/tests/distill.test.js +0 -515
  130. package/dist/tests/docker-install.test.js +0 -120
  131. package/dist/tests/e2e.test.js +0 -1419
  132. package/dist/tests/embedder.test.js +0 -340
  133. package/dist/tests/embedding-model-config.test.js +0 -379
  134. package/dist/tests/feedback-command.test.js +0 -172
  135. package/dist/tests/file-context.test.js +0 -552
  136. package/dist/tests/fixtures/scripts/git/summarize-diff.js +0 -9
  137. package/dist/tests/fixtures/scripts/lint/eslint-check.js +0 -7
  138. package/dist/tests/fixtures/stashes/load.js +0 -166
  139. package/dist/tests/fixtures/stashes/load.test.js +0 -97
  140. package/dist/tests/fixtures/stashes/ranking-baseline/scripts/mem0-search.js +0 -12
  141. package/dist/tests/frontmatter.test.js +0 -190
  142. package/dist/tests/fts-field-weighting.test.js +0 -254
  143. package/dist/tests/fuzzy-search.test.js +0 -230
  144. package/dist/tests/git-provider-clone.test.js +0 -45
  145. package/dist/tests/github.test.js +0 -161
  146. package/dist/tests/graph-boost-ranking.test.js +0 -305
  147. package/dist/tests/graph-extraction.test.js +0 -282
  148. package/dist/tests/helpers/usage-events.js +0 -8
  149. package/dist/tests/index-pass-llm.test.js +0 -161
  150. package/dist/tests/indexer.test.js +0 -570
  151. package/dist/tests/info-command.test.js +0 -166
  152. package/dist/tests/init.test.js +0 -69
  153. package/dist/tests/install-script.test.js +0 -246
  154. package/dist/tests/integration/agent-real-profile.test.js +0 -94
  155. package/dist/tests/issue-36-repro.test.js +0 -304
  156. package/dist/tests/issues-191-194.test.js +0 -160
  157. package/dist/tests/lesson-lint.test.js +0 -111
  158. package/dist/tests/llm-client.test.js +0 -115
  159. package/dist/tests/llm-feature-gate.test.js +0 -151
  160. package/dist/tests/llm.test.js +0 -139
  161. package/dist/tests/lockfile.test.js +0 -216
  162. package/dist/tests/manifest.test.js +0 -205
  163. package/dist/tests/markdown.test.js +0 -126
  164. package/dist/tests/matchers-unit.test.js +0 -189
  165. package/dist/tests/memory-inference.test.js +0 -299
  166. package/dist/tests/merge-scoring.test.js +0 -136
  167. package/dist/tests/metadata.test.js +0 -313
  168. package/dist/tests/migration-help.test.js +0 -89
  169. package/dist/tests/origin-resolve.test.js +0 -124
  170. package/dist/tests/output-baseline.test.js +0 -218
  171. package/dist/tests/output-shapes-unit.test.js +0 -478
  172. package/dist/tests/parallel-search.test.js +0 -272
  173. package/dist/tests/parameter-metadata.test.js +0 -365
  174. package/dist/tests/paths.test.js +0 -177
  175. package/dist/tests/progressive-disclosure.test.js +0 -280
  176. package/dist/tests/proposals.test.js +0 -279
  177. package/dist/tests/proposed-quality.test.js +0 -271
  178. package/dist/tests/provider-registry.test.js +0 -32
  179. package/dist/tests/ranking-regression.test.js +0 -548
  180. package/dist/tests/reflect-propose.test.js +0 -455
  181. package/dist/tests/registry-build-index.test.js +0 -394
  182. package/dist/tests/registry-cli.test.js +0 -290
  183. package/dist/tests/registry-index-v2.test.js +0 -430
  184. package/dist/tests/registry-install.test.js +0 -728
  185. package/dist/tests/registry-providers/parity.test.js +0 -189
  186. package/dist/tests/registry-providers/skills-sh.test.js +0 -309
  187. package/dist/tests/registry-providers/static-index.test.js +0 -238
  188. package/dist/tests/registry-resolve.test.js +0 -126
  189. package/dist/tests/registry-search.test.js +0 -923
  190. package/dist/tests/remember-frontmatter.test.js +0 -378
  191. package/dist/tests/remember-unit.test.js +0 -123
  192. package/dist/tests/ripgrep-install.test.js +0 -251
  193. package/dist/tests/ripgrep-resolve.test.js +0 -108
  194. package/dist/tests/ripgrep.test.js +0 -163
  195. package/dist/tests/save-command.test.js +0 -94
  196. package/dist/tests/save-trust-qa-fixes.test.js +0 -270
  197. package/dist/tests/scoring-pipeline.test.js +0 -648
  198. package/dist/tests/search-include-proposed-cli.test.js +0 -118
  199. package/dist/tests/self-update.test.js +0 -442
  200. package/dist/tests/semantic-search-e2e.test.js +0 -512
  201. package/dist/tests/semantic-status.test.js +0 -471
  202. package/dist/tests/setup-run.integration.js +0 -877
  203. package/dist/tests/setup-wizard.test.js +0 -198
  204. package/dist/tests/setup.test.js +0 -131
  205. package/dist/tests/source-add.test.js +0 -11
  206. package/dist/tests/source-clone.test.js +0 -254
  207. package/dist/tests/source-manage.test.js +0 -366
  208. package/dist/tests/source-providers/filesystem.test.js +0 -82
  209. package/dist/tests/source-providers/git.test.js +0 -252
  210. package/dist/tests/source-providers/website.test.js +0 -128
  211. package/dist/tests/source-qa-fixes.test.js +0 -286
  212. package/dist/tests/source-registry.test.js +0 -350
  213. package/dist/tests/source-resolve.test.js +0 -100
  214. package/dist/tests/source-source.test.js +0 -281
  215. package/dist/tests/source.test.js +0 -533
  216. package/dist/tests/tar-utils-scan.test.js +0 -73
  217. package/dist/tests/toggle-components.test.js +0 -73
  218. package/dist/tests/usage-telemetry.test.js +0 -265
  219. package/dist/tests/utility-scoring.test.js +0 -558
  220. package/dist/tests/vault-load-error.test.js +0 -78
  221. package/dist/tests/vault-qa-fixes.test.js +0 -194
  222. package/dist/tests/vault.test.js +0 -429
  223. package/dist/tests/vector-search.test.js +0 -608
  224. package/dist/tests/walker.test.js +0 -252
  225. package/dist/tests/wave2-cluster-bc.test.js +0 -228
  226. package/dist/tests/wave2-cluster-d.test.js +0 -180
  227. package/dist/tests/wave2-cluster-e.test.js +0 -179
  228. package/dist/tests/wiki-qa-fixes.test.js +0 -270
  229. package/dist/tests/wiki.test.js +0 -529
  230. package/dist/tests/workflow-cli.test.js +0 -271
  231. package/dist/tests/workflow-markdown.test.js +0 -171
  232. package/dist/tests/workflow-path-escape.test.js +0 -132
  233. package/dist/tests/workflow-qa-fixes.test.js +0 -395
  234. package/dist/tests/workflows/indexer-rejection.test.js +0 -213
  235. /package/dist/{src/commands → commands}/completions.js +0 -0
  236. /package/dist/{src/commands → commands}/config-cli.js +0 -0
  237. /package/dist/{src/commands → commands}/curate.js +0 -0
  238. /package/dist/{src/commands → commands}/distill.js +0 -0
  239. /package/dist/{src/commands → commands}/events.js +0 -0
  240. /package/dist/{src/commands → commands}/history.js +0 -0
  241. /package/dist/{src/commands → commands}/info.js +0 -0
  242. /package/dist/{src/commands → commands}/init.js +0 -0
  243. /package/dist/{src/commands → commands}/install-audit.js +0 -0
  244. /package/dist/{src/commands → commands}/migration-help.js +0 -0
  245. /package/dist/{src/commands → commands}/proposal.js +0 -0
  246. /package/dist/{src/commands → commands}/propose.js +0 -0
  247. /package/dist/{src/commands → commands}/reflect.js +0 -0
  248. /package/dist/{src/commands → commands}/registry-search.js +0 -0
  249. /package/dist/{src/commands → commands}/remember.js +0 -0
  250. /package/dist/{src/commands → commands}/search.js +0 -0
  251. /package/dist/{src/commands → commands}/self-update.js +0 -0
  252. /package/dist/{src/commands → commands}/show.js +0 -0
  253. /package/dist/{src/commands → commands}/source-clone.js +0 -0
  254. /package/dist/{src/commands → commands}/source-manage.js +0 -0
  255. /package/dist/{src/commands → commands}/vault.js +0 -0
  256. /package/dist/{src/core → core}/asset-ref.js +0 -0
  257. /package/dist/{src/core → core}/asset-registry.js +0 -0
  258. /package/dist/{src/core → core}/asset-spec.js +0 -0
  259. /package/dist/{src/core → core}/errors.js +0 -0
  260. /package/dist/{src/core → core}/events.js +0 -0
  261. /package/dist/{src/core → core}/frontmatter.js +0 -0
  262. /package/dist/{src/core → core}/lesson-lint.js +0 -0
  263. /package/dist/{src/core → core}/markdown.js +0 -0
  264. /package/dist/{src/core → core}/paths.js +0 -0
  265. /package/dist/{src/core → core}/proposals.js +0 -0
  266. /package/dist/{src/core → core}/warn.js +0 -0
  267. /package/dist/{src/core → core}/write-source.js +0 -0
  268. /package/dist/{src/indexer → indexer}/db.js +0 -0
  269. /package/dist/{src/indexer → indexer}/file-context.js +0 -0
  270. /package/dist/{src/indexer → indexer}/graph-boost.js +0 -0
  271. /package/dist/{src/indexer → indexer}/manifest.js +0 -0
  272. /package/dist/{src/indexer → indexer}/matchers.js +0 -0
  273. /package/dist/{src/indexer → indexer}/metadata.js +0 -0
  274. /package/dist/{src/indexer → indexer}/search-fields.js +0 -0
  275. /package/dist/{src/indexer → indexer}/semantic-status.js +0 -0
  276. /package/dist/{src/indexer → indexer}/usage-events.js +0 -0
  277. /package/dist/{src/indexer → indexer}/walker.js +0 -0
  278. /package/dist/{src/integrations → integrations}/agent/config.js +0 -0
  279. /package/dist/{src/integrations → integrations}/agent/detect.js +0 -0
  280. /package/dist/{src/integrations → integrations}/agent/index.js +0 -0
  281. /package/dist/{src/integrations → integrations}/agent/profiles.js +0 -0
  282. /package/dist/{src/integrations → integrations}/agent/prompts.js +0 -0
  283. /package/dist/{src/integrations → integrations}/agent/spawn.js +0 -0
  284. /package/dist/{src/integrations → integrations}/github.js +0 -0
  285. /package/dist/{src/integrations → integrations}/lockfile.js +0 -0
  286. /package/dist/{src/llm → llm}/embedders/cache.js +0 -0
  287. /package/dist/{src/llm → llm}/embedders/types.js +0 -0
  288. /package/dist/{src/llm → llm}/feature-gate.js +0 -0
  289. /package/dist/{src/llm → llm}/index-passes.js +0 -0
  290. /package/dist/{src/output → output}/context.js +0 -0
  291. /package/dist/{src/output → output}/renderers.js +0 -0
  292. /package/dist/{src/output → output}/shapes.js +0 -0
  293. /package/dist/{src/output → output}/text.js +0 -0
  294. /package/dist/{src/registry → registry}/build-index.js +0 -0
  295. /package/dist/{src/registry → registry}/create-provider-registry.js +0 -0
  296. /package/dist/{src/registry → registry}/factory.js +0 -0
  297. /package/dist/{src/registry → registry}/origin-resolve.js +0 -0
  298. /package/dist/{src/registry → registry}/providers/index.js +0 -0
  299. /package/dist/{src/registry → registry}/providers/skills-sh.js +0 -0
  300. /package/dist/{src/registry → registry}/providers/static-index.js +0 -0
  301. /package/dist/{src/registry → registry}/providers/types.js +0 -0
  302. /package/dist/{src/registry → registry}/resolve.js +0 -0
  303. /package/dist/{src/registry → registry}/types.js +0 -0
  304. /package/dist/{src/setup → setup}/detect.js +0 -0
  305. /package/dist/{src/setup → setup}/ripgrep-install.js +0 -0
  306. /package/dist/{src/setup → setup}/ripgrep-resolve.js +0 -0
  307. /package/dist/{src/setup → setup}/steps.js +0 -0
  308. /package/dist/{src/sources → sources}/include.js +0 -0
  309. /package/dist/{src/sources → sources}/provider-factory.js +0 -0
  310. /package/dist/{src/sources → sources}/provider.js +0 -0
  311. /package/dist/{src/sources → sources}/providers/filesystem.js +0 -0
  312. /package/dist/{src/sources → sources}/providers/git.js +0 -0
  313. /package/dist/{src/sources → sources}/providers/index.js +0 -0
  314. /package/dist/{src/sources → sources}/providers/install-types.js +0 -0
  315. /package/dist/{src/sources → sources}/providers/npm.js +0 -0
  316. /package/dist/{src/sources → sources}/providers/provider-utils.js +0 -0
  317. /package/dist/{src/sources → sources}/providers/sync-from-ref.js +0 -0
  318. /package/dist/{src/sources → sources}/providers/tar-utils.js +0 -0
  319. /package/dist/{src/sources → sources}/resolve.js +0 -0
  320. /package/dist/{src/sources → sources}/types.js +0 -0
  321. /package/dist/{src/templates → templates}/wiki-templates.js +0 -0
  322. /package/dist/{src/version.js → version.js} +0 -0
  323. /package/dist/{src/wiki → wiki}/wiki.js +0 -0
  324. /package/dist/{src/workflows → workflows}/authoring.js +0 -0
  325. /package/dist/{src/workflows → workflows}/cli.js +0 -0
  326. /package/dist/{src/workflows → workflows}/db.js +0 -0
  327. /package/dist/{src/workflows → workflows}/document-cache.js +0 -0
  328. /package/dist/{src/workflows → workflows}/parser.js +0 -0
  329. /package/dist/{src/workflows → workflows}/renderer.js +0 -0
  330. /package/dist/{src/workflows → workflows}/runs.js +0 -0
  331. /package/dist/{src/workflows → workflows}/schema.js +0 -0
  332. /package/dist/{src/workflows → workflows}/validator.js +0 -0
@@ -1,161 +0,0 @@
1
- import { afterEach, describe, expect, mock, spyOn, test } from "bun:test";
2
- import * as childProcess from "node:child_process";
3
- import { asRecord, asString, GITHUB_API_BASE, githubHeaders } from "../src/integrations/github";
4
- // ── Environment helpers ─────────────────────────────────────────────────────
5
- const originalGithubToken = process.env.GITHUB_TOKEN;
6
- const originalGhToken = process.env.GH_TOKEN;
7
- afterEach(() => {
8
- mock.restore();
9
- if (originalGithubToken === undefined) {
10
- delete process.env.GITHUB_TOKEN;
11
- }
12
- else {
13
- process.env.GITHUB_TOKEN = originalGithubToken;
14
- }
15
- if (originalGhToken === undefined) {
16
- delete process.env.GH_TOKEN;
17
- }
18
- else {
19
- process.env.GH_TOKEN = originalGhToken;
20
- }
21
- });
22
- // ── GITHUB_API_BASE ─────────────────────────────────────────────────────────
23
- describe("GITHUB_API_BASE", () => {
24
- test("is the GitHub API URL", () => {
25
- expect(GITHUB_API_BASE).toBe("https://api.github.com");
26
- });
27
- });
28
- // ── githubHeaders ───────────────────────────────────────────────────────────
29
- describe("githubHeaders", () => {
30
- test("includes Accept and User-Agent headers", () => {
31
- delete process.env.GITHUB_TOKEN;
32
- const headers = githubHeaders();
33
- expect(headers.Accept).toBe("application/vnd.github+json");
34
- expect(headers["User-Agent"]).toBe("akm-registry");
35
- });
36
- test("does not include Authorization when GITHUB_TOKEN is unset", () => {
37
- delete process.env.GITHUB_TOKEN;
38
- delete process.env.GH_TOKEN;
39
- spyOn(childProcess, "spawnSync").mockReturnValue({ status: 1, stdout: "" });
40
- const headers = githubHeaders();
41
- expect(headers.Authorization).toBeUndefined();
42
- });
43
- test("includes Authorization when GITHUB_TOKEN is set", () => {
44
- process.env.GITHUB_TOKEN = "ghp_test_token_123";
45
- const headers = githubHeaders();
46
- expect(headers.Authorization).toBe("Bearer ghp_test_token_123");
47
- });
48
- test("trims whitespace from GITHUB_TOKEN", () => {
49
- process.env.GITHUB_TOKEN = " ghp_trimmed ";
50
- delete process.env.GH_TOKEN;
51
- const headers = githubHeaders();
52
- expect(headers.Authorization).toBe("Bearer ghp_trimmed");
53
- });
54
- test("does not include Authorization when GITHUB_TOKEN is empty", () => {
55
- process.env.GITHUB_TOKEN = "";
56
- delete process.env.GH_TOKEN;
57
- const headers = githubHeaders();
58
- expect(headers.Authorization).toBeUndefined();
59
- });
60
- test("does not include Authorization when GITHUB_TOKEN is whitespace-only", () => {
61
- process.env.GITHUB_TOKEN = " ";
62
- delete process.env.GH_TOKEN;
63
- const headers = githubHeaders();
64
- expect(headers.Authorization).toBeUndefined();
65
- });
66
- test("uses GH_TOKEN when GITHUB_TOKEN is unset", () => {
67
- delete process.env.GITHUB_TOKEN;
68
- process.env.GH_TOKEN = "ghs_from_gh_token";
69
- const headers = githubHeaders();
70
- expect(headers.Authorization).toBe("Bearer ghs_from_gh_token");
71
- });
72
- test("prefers GITHUB_TOKEN over GH_TOKEN", () => {
73
- process.env.GITHUB_TOKEN = "ghp_preferred";
74
- process.env.GH_TOKEN = "ghs_fallback";
75
- const headers = githubHeaders();
76
- expect(headers.Authorization).toBe("Bearer ghp_preferred");
77
- });
78
- test("falls back to gh auth token when env vars are unset", () => {
79
- delete process.env.GITHUB_TOKEN;
80
- delete process.env.GH_TOKEN;
81
- const spawnSyncSpy = spyOn(childProcess, "spawnSync").mockReturnValue({
82
- status: 0,
83
- stdout: "gho_cli_token\n",
84
- });
85
- const headers = githubHeaders();
86
- expect(headers.Authorization).toBe("Bearer gho_cli_token");
87
- expect(spawnSyncSpy).toHaveBeenCalledWith("gh", ["auth", "token"], {
88
- encoding: "utf8",
89
- timeout: 5000,
90
- stdio: ["ignore", "pipe", "ignore"],
91
- });
92
- });
93
- test("does not include gh auth token for non-GitHub URLs", () => {
94
- delete process.env.GITHUB_TOKEN;
95
- delete process.env.GH_TOKEN;
96
- spyOn(childProcess, "spawnSync").mockReturnValue({ status: 0, stdout: "gho_cli_token\n" });
97
- const headers = githubHeaders("https://example.com/file.tgz");
98
- expect(headers.Authorization).toBeUndefined();
99
- });
100
- });
101
- // ── asRecord ────────────────────────────────────────────────────────────────
102
- describe("asRecord", () => {
103
- test("returns object as-is for a plain object", () => {
104
- const obj = { key: "value", num: 42 };
105
- expect(asRecord(obj)).toBe(obj);
106
- });
107
- test("returns empty object for null", () => {
108
- expect(asRecord(null)).toEqual({});
109
- });
110
- test("returns empty object for undefined", () => {
111
- expect(asRecord(undefined)).toEqual({});
112
- });
113
- test("returns empty object for a string", () => {
114
- expect(asRecord("hello")).toEqual({});
115
- });
116
- test("returns empty object for a number", () => {
117
- expect(asRecord(42)).toEqual({});
118
- });
119
- test("returns empty object for a boolean", () => {
120
- expect(asRecord(true)).toEqual({});
121
- });
122
- test("returns empty object for an array", () => {
123
- expect(asRecord([1, 2, 3])).toEqual({});
124
- });
125
- test("returns the object for nested objects", () => {
126
- const nested = { a: { b: "c" } };
127
- const result = asRecord(nested);
128
- expect(result).toBe(nested);
129
- expect(result.a).toEqual({ b: "c" });
130
- });
131
- });
132
- // ── asString ────────────────────────────────────────────────────────────────
133
- describe("asString", () => {
134
- test("returns string for a non-empty string", () => {
135
- expect(asString("hello")).toBe("hello");
136
- });
137
- test("returns undefined for an empty string", () => {
138
- expect(asString("")).toBeUndefined();
139
- });
140
- test("returns undefined for null", () => {
141
- expect(asString(null)).toBeUndefined();
142
- });
143
- test("returns undefined for undefined", () => {
144
- expect(asString(undefined)).toBeUndefined();
145
- });
146
- test("returns undefined for a number", () => {
147
- expect(asString(42)).toBeUndefined();
148
- });
149
- test("returns undefined for a boolean", () => {
150
- expect(asString(true)).toBeUndefined();
151
- });
152
- test("returns undefined for an object", () => {
153
- expect(asString({ toString: () => "obj" })).toBeUndefined();
154
- });
155
- test("returns undefined for an array", () => {
156
- expect(asString(["hello"])).toBeUndefined();
157
- });
158
- test("returns string with whitespace preserved", () => {
159
- expect(asString(" spaced ")).toBe(" spaced ");
160
- });
161
- });
@@ -1,305 +0,0 @@
1
- /**
2
- * Integration test for the search-time graph boost (#207).
3
- *
4
- * Asserts deterministically that for a corpus with a known graph-eligible
5
- * query, the rank of the expected target improves when a `graph.json`
6
- * artifact is present versus absent. No LLM calls are made — the graph
7
- * file is written directly to the fixture stash, simulating what the
8
- * extraction pass would produce.
9
- *
10
- * The test uses TWO independent runs against the SAME database state:
11
- * 1. Baseline — `graph.json` deleted before search.
12
- * 2. Boosted — `graph.json` present before search.
13
- *
14
- * Acceptance: the target's rank in the boosted run must be ≤ its rank in
15
- * the baseline run, and at least one of (rank improves) OR (score
16
- * strictly increases) must hold. This is a deterministic comparison, not
17
- * a percentage threshold.
18
- *
19
- * It also verifies that the graph signal feeds the SAME `score` field on
20
- * `SourceSearchHit` — i.e. there is no second SearchHit scorer; the
21
- * graph-aware run produces a (weakly) higher score on the same hit.
22
- */
23
- import { afterAll, beforeAll, describe, expect, test } from "bun:test";
24
- import fs from "node:fs";
25
- import os from "node:os";
26
- import path from "node:path";
27
- import { akmSearch } from "../src/commands/search";
28
- import { resetConfigCache, saveConfig } from "../src/core/config";
29
- import { getDbPath } from "../src/core/paths";
30
- import { closeDatabase, openDatabase, rebuildFts, setMeta, upsertEntry } from "../src/indexer/db";
31
- import { GRAPH_FILE_SCHEMA_VERSION, getGraphFilePath } from "../src/indexer/graph-extraction";
32
- import { buildSearchText } from "../src/indexer/search-fields";
33
- // ── Environment isolation ───────────────────────────────────────────────────
34
- let stashDir = "";
35
- let originalXdgCacheHome;
36
- let originalXdgConfigHome;
37
- let originalAkmStashDir;
38
- let testCacheDir = "";
39
- let testConfigDir = "";
40
- beforeAll(() => {
41
- originalXdgCacheHome = process.env.XDG_CACHE_HOME;
42
- originalXdgConfigHome = process.env.XDG_CONFIG_HOME;
43
- originalAkmStashDir = process.env.AKM_STASH_DIR;
44
- testCacheDir = fs.mkdtempSync(path.join(os.tmpdir(), "akm-graph-rank-cache-"));
45
- testConfigDir = fs.mkdtempSync(path.join(os.tmpdir(), "akm-graph-rank-config-"));
46
- stashDir = fs.mkdtempSync(path.join(os.tmpdir(), "akm-graph-rank-stash-"));
47
- process.env.XDG_CACHE_HOME = testCacheDir;
48
- process.env.XDG_CONFIG_HOME = testConfigDir;
49
- process.env.AKM_STASH_DIR = stashDir;
50
- resetConfigCache();
51
- saveConfig({
52
- semanticSearchMode: "off",
53
- sources: [{ type: "filesystem", path: stashDir }],
54
- registries: [],
55
- });
56
- buildFixture();
57
- });
58
- afterAll(() => {
59
- if (originalXdgCacheHome === undefined)
60
- delete process.env.XDG_CACHE_HOME;
61
- else
62
- process.env.XDG_CACHE_HOME = originalXdgCacheHome;
63
- if (originalXdgConfigHome === undefined)
64
- delete process.env.XDG_CONFIG_HOME;
65
- else
66
- process.env.XDG_CONFIG_HOME = originalXdgConfigHome;
67
- if (originalAkmStashDir === undefined)
68
- delete process.env.AKM_STASH_DIR;
69
- else
70
- process.env.AKM_STASH_DIR = originalAkmStashDir;
71
- resetConfigCache();
72
- for (const dir of [testCacheDir, testConfigDir, stashDir]) {
73
- if (dir)
74
- fs.rmSync(dir, { recursive: true, force: true });
75
- }
76
- });
77
- // ── Fixture builder ─────────────────────────────────────────────────────────
78
- //
79
- // The corpus is small but deliberately constructed so the lexical signal
80
- // is roughly even between two candidates — the graph boost is what
81
- // separates them deterministically:
82
- //
83
- // - knowledge:database-runbook — recovery procedure for a database
84
- // outage. The TARGET of the test query. Doesn't have any postgres-
85
- // specific name match, so a postgres-aware query splits between this
86
- // and the FAQ on lexical signals alone.
87
- // - knowledge:database-faq — Q&A document. Lexical match on the same
88
- // query terms but unrelated to outage recovery operationally. The
89
- // COMPETITOR.
90
- // - memory:incident-2024-shard — operational note. Anchors the graph
91
- // edge connecting "outage recovery" to the runbook file.
92
- //
93
- // With graph.json present, the runbook's entities directly match the
94
- // query tokens and pick up the graph boost; the FAQ has no graph node
95
- // and gets nothing. The deterministic acceptance is "rank improves OR
96
- // score strictly increases" — both work even if the baseline already
97
- // happens to put the runbook on top.
98
- function buildFixture() {
99
- // Asset files on disk — the search-time graph boost matches by absolute
100
- // file path, so paths must be consistent between fixture build and
101
- // graph.json contents.
102
- const knowledgeDir = path.join(stashDir, "knowledge");
103
- const memoryDir = path.join(stashDir, "memories");
104
- fs.mkdirSync(knowledgeDir, { recursive: true });
105
- fs.mkdirSync(memoryDir, { recursive: true });
106
- const runbookPath = path.join(knowledgeDir, "database-runbook.md");
107
- fs.writeFileSync(runbookPath, "---\ntype: knowledge\n---\n\nThe runbook for database outage recovery after a hardware fault.\n");
108
- const faqPath = path.join(knowledgeDir, "database-faq.md");
109
- fs.writeFileSync(faqPath, "---\ntype: knowledge\n---\n\nA database FAQ covering connection limits, recovery tunables, and outage post-mortems.\n");
110
- const memoryPath = path.join(memoryDir, "incident-2024-shard.md");
111
- fs.writeFileSync(memoryPath, "---\ntype: memory\n---\n\nDuring the 2024 database outage we recovered shard-3 by following the runbook.\n");
112
- // Index the corpus directly into the SQLite DB.
113
- const dbPath = getDbPath();
114
- // Make sure the cache dir exists (akm-graph-rank-cache-* is fresh).
115
- fs.mkdirSync(path.dirname(dbPath), { recursive: true });
116
- const db = openDatabase(dbPath);
117
- try {
118
- const entries = [
119
- {
120
- entry: {
121
- name: "database-runbook",
122
- type: "knowledge",
123
- filename: "database-runbook.md",
124
- description: "Runbook for database outage recovery after a hardware fault.",
125
- },
126
- filePath: runbookPath,
127
- dirPath: knowledgeDir,
128
- },
129
- {
130
- entry: {
131
- name: "database-faq",
132
- type: "knowledge",
133
- filename: "database-faq.md",
134
- description: "Database FAQ covering connection limits, recovery tunables, and outage post-mortems.",
135
- },
136
- filePath: faqPath,
137
- dirPath: knowledgeDir,
138
- },
139
- {
140
- entry: {
141
- name: "incident-2024-shard",
142
- type: "memory",
143
- filename: "incident-2024-shard.md",
144
- description: "We recovered shard-3 during the 2024 database outage by following the runbook.",
145
- },
146
- filePath: memoryPath,
147
- dirPath: memoryDir,
148
- },
149
- ];
150
- for (const e of entries) {
151
- const entryKey = `${stashDir}:${e.entry.type}:${e.entry.name}`;
152
- const searchText = buildSearchText(e.entry);
153
- upsertEntry(db, entryKey, e.dirPath, e.filePath, stashDir, e.entry, searchText);
154
- }
155
- rebuildFts(db);
156
- setMeta(db, "stashDir", stashDir);
157
- setMeta(db, "builtAt", new Date().toISOString());
158
- setMeta(db, "stashDirs", JSON.stringify([stashDir]));
159
- setMeta(db, "hasEmbeddings", "0");
160
- }
161
- finally {
162
- closeDatabase(db);
163
- }
164
- // Pre-build the graph file once, but DON'T install it yet — each test
165
- // installs/removes it as needed. The entity set is exactly what the
166
- // graph-extraction LLM helper would have produced for these bodies.
167
- const graphFile = {
168
- schemaVersion: GRAPH_FILE_SCHEMA_VERSION,
169
- generatedAt: new Date().toISOString(),
170
- stashRoot: stashDir,
171
- files: [
172
- {
173
- path: runbookPath,
174
- type: "knowledge",
175
- entities: ["database", "outage", "recovery", "runbook"],
176
- relations: [
177
- { from: "outage", to: "recovery" },
178
- { from: "recovery", to: "database" },
179
- ],
180
- },
181
- {
182
- path: memoryPath,
183
- type: "memory",
184
- entities: ["database", "outage", "recovery", "shard-3"],
185
- relations: [{ from: "outage", to: "recovery" }],
186
- },
187
- // database-faq has NO graph entries — the FAQ doesn't operationally
188
- // relate to outage recovery; it just shares vocabulary. This is the
189
- // asymmetry that lets the graph signal differentiate the runbook
190
- // from a vocabulary-matching FAQ.
191
- ],
192
- };
193
- // Stash the prepared graph payload as a JSON file alongside the stash so
194
- // tests can install/uninstall by file rename.
195
- fs.mkdirSync(path.join(stashDir, ".akm"), { recursive: true });
196
- fs.writeFileSync(path.join(stashDir, ".akm", "graph.prepared.json"), `${JSON.stringify(graphFile, null, 2)}\n`, "utf8");
197
- }
198
- function installGraph() {
199
- const prepared = path.join(stashDir, ".akm", "graph.prepared.json");
200
- fs.copyFileSync(prepared, getGraphFilePath(stashDir));
201
- }
202
- function uninstallGraph() {
203
- const target = getGraphFilePath(stashDir);
204
- if (fs.existsSync(target))
205
- fs.unlinkSync(target);
206
- }
207
- async function searchHits(query) {
208
- const result = await akmSearch({ query, source: "stash", limit: 20 });
209
- return result.hits;
210
- }
211
- function rankOf(hits, name) {
212
- const idx = hits.findIndex((h) => h.name === name);
213
- return idx === -1 ? Infinity : idx + 1;
214
- }
215
- function scoreOf(hits, name) {
216
- const hit = hits.find((h) => h.name === name);
217
- return hit?.score ?? 0;
218
- }
219
- // ── Tests ───────────────────────────────────────────────────────────────────
220
- describe("graph boost — search-time integration (#207)", () => {
221
- test("graph signal lifts runbook above FAQ for outage-recovery query", async () => {
222
- const query = "database outage recovery";
223
- // Baseline: no graph file.
224
- uninstallGraph();
225
- const baselineHits = await searchHits(query);
226
- const baselineRank = rankOf(baselineHits, "database-runbook");
227
- const baselineScore = scoreOf(baselineHits, "database-runbook");
228
- expect(baselineRank).not.toBe(Infinity);
229
- // Boosted: graph file present.
230
- installGraph();
231
- const boostedHits = await searchHits(query);
232
- const boostedRank = rankOf(boostedHits, "database-runbook");
233
- const boostedScore = scoreOf(boostedHits, "database-runbook");
234
- expect(boostedRank).not.toBe(Infinity);
235
- // Acceptance criterion (deterministic, not a percentage threshold):
236
- // - rank must not regress, AND
237
- // - score must not regress.
238
- // Per CLAUDE.md / spec §9, displayed scores are clamped to [0,1]; on
239
- // the strong-match runbook fixture both runs may clamp to the ceiling,
240
- // so the observable contract collapses from "score strictly increases"
241
- // to "score does not regress" while rank ordering separately confirms
242
- // the graph signal lifted the runbook over the FAQ.
243
- expect(boostedRank).toBeLessThanOrEqual(baselineRank);
244
- expect(boostedScore).toBeGreaterThanOrEqual(baselineScore);
245
- });
246
- test("absent graph file has no effect on score (no parallel scoring track)", async () => {
247
- // With the graph file uninstalled, a non-graph-eligible query should
248
- // produce the same hit ordering as it did before #207 landed. This
249
- // confirms the graph integration is purely additive — there's no
250
- // hidden second scorer running unconditionally.
251
- uninstallGraph();
252
- const without = await searchHits("database");
253
- expect(without.length).toBeGreaterThan(0);
254
- // Re-run the same query while the file is uninstalled — must be
255
- // byte-identical (same hits, same scores) within the deterministic
256
- // tiebreaker.
257
- const without2 = await searchHits("database");
258
- expect(without2.map((h) => h.name)).toEqual(without.map((h) => h.name));
259
- expect(without2.map((h) => h.score)).toEqual(without.map((h) => h.score));
260
- });
261
- test("score is clamped to [0,1] even when boosts would push above 1.0", async () => {
262
- // Fixes a pre-existing breach of the CLAUDE.md / spec §9 contract that
263
- // locks `SearchHit.score` to [0,1]: the boost loop in db-search.ts can
264
- // accumulate FTS-base + multiple additive boosts whose product exceeds
265
- // 1.0, and the addition of #207's graph boost (up to ~1.05 additive)
266
- // makes the breach detectable in practice. The runbook fixture above
267
- // matches an exact-name query (boost +2.0) AND has a full graph hit,
268
- // which combined push the raw computed score above 1.0. The clamp
269
- // (`Math.min(1, Math.max(0, score))` near `Math.round(score * 10000)`
270
- // in `searchDatabase`) guarantees the final SearchHit.score is exactly
271
- // 1 in that case rather than overflowing.
272
- installGraph();
273
- const hits = await searchHits("database-runbook");
274
- const target = hits.find((h) => h.name === "database-runbook");
275
- expect(target).toBeDefined();
276
- expect(typeof target?.score).toBe("number");
277
- // Every emitted score must satisfy the locked contract.
278
- for (const h of hits) {
279
- expect(h.score ?? 0).toBeLessThanOrEqual(1);
280
- expect(h.score ?? 0).toBeGreaterThanOrEqual(0);
281
- }
282
- // The exact-name + graph-boosted case clamps to the ceiling.
283
- expect(target?.score).toBe(1);
284
- });
285
- test("graph signal feeds the same SearchHit.score field — no second scorer", async () => {
286
- // Verify the boost lands on the same `score` property of the same hit
287
- // object. This is the contract: the graph signal is one boost
288
- // component inside the FTS5+boosts loop, not a parallel ranking.
289
- const query = "database outage recovery";
290
- uninstallGraph();
291
- const baseline = await searchHits(query);
292
- const baselineHit = baseline.find((h) => h.name === "database-runbook");
293
- expect(baselineHit).toBeDefined();
294
- expect(typeof baselineHit?.score).toBe("number");
295
- installGraph();
296
- const boosted = await searchHits(query);
297
- const boostedHit = boosted.find((h) => h.name === "database-runbook");
298
- expect(boostedHit).toBeDefined();
299
- expect(typeof boostedHit?.score).toBe("number");
300
- // Same hit shape, same score field — just (weakly) higher.
301
- expect(boostedHit?.path).toBe(baselineHit?.path);
302
- expect(boostedHit?.ref).toBe(baselineHit?.ref);
303
- expect(boostedHit?.score ?? 0).toBeGreaterThanOrEqual(baselineHit?.score ?? 0);
304
- });
305
- });