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,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
- });