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,83 +0,0 @@
1
- /**
2
- * Subprocess test: real SIGINT delivery cleans up registered fns before
3
- * exit (#267).
4
- *
5
- * The real handler calls `process.exit(130)` — fatal inside the test
6
- * runner. So we drive it from a child Bun process and assert via side
7
- * effects (touchstones written to a tmpdir) that every cleanup fn ran.
8
- */
9
- import { afterAll, describe, expect, test } from "bun:test";
10
- import { spawnSync } from "node:child_process";
11
- import fs from "node:fs";
12
- import path from "node:path";
13
- import { benchMkdtemp } from "./tmp";
14
- const tempDirs = [];
15
- function makeTempDir(prefix = "akm-bench-cleanup-sigint-") {
16
- const dir = benchMkdtemp(prefix);
17
- tempDirs.push(dir);
18
- return dir;
19
- }
20
- afterAll(() => {
21
- for (const dir of tempDirs) {
22
- fs.rmSync(dir, { recursive: true, force: true });
23
- }
24
- });
25
- const repoRoot = path.resolve(import.meta.dir, "..", "..");
26
- describe("SIGINT delivery → registered cleanups run (#267)", () => {
27
- test("subprocess: SIGINT runs every registered cleanup fn before exit", () => {
28
- const sigDir = makeTempDir();
29
- const a = path.join(sigDir, "a.touchstone");
30
- const b = path.join(sigDir, "b.touchstone");
31
- const c = path.join(sigDir, "c.touchstone");
32
- // Inline driver script. It registers three cleanup fns that each touch
33
- // a unique file, then `process.kill(pid, 'SIGINT')` and waits long
34
- // enough for the handler to fire `process.exit(130)`.
35
- const driverScript = `
36
- import { registerCleanup } from ${JSON.stringify(path.join(repoRoot, "tests", "bench", "cleanup.ts"))};
37
- import fs from "node:fs";
38
-
39
- registerCleanup(() => fs.writeFileSync(${JSON.stringify(a)}, "a"));
40
- registerCleanup(() => fs.writeFileSync(${JSON.stringify(b)}, "b"));
41
- registerCleanup(async () => {
42
- await new Promise((r) => setTimeout(r, 5));
43
- fs.writeFileSync(${JSON.stringify(c)}, "c");
44
- });
45
-
46
- process.kill(process.pid, "SIGINT");
47
- // Stay alive until the signal handler fires + exits.
48
- await new Promise((r) => setTimeout(r, 2000));
49
- `;
50
- const scriptPath = path.join(sigDir, "driver.mjs");
51
- fs.writeFileSync(scriptPath, driverScript);
52
- const result = spawnSync("bun", ["run", scriptPath], {
53
- encoding: "utf8",
54
- timeout: 10_000,
55
- });
56
- // Exit code 130 = signalled exit (POSIX convention 128 + SIGINT(2)).
57
- expect(result.status).toBe(130);
58
- // All three cleanup fns ran before exit.
59
- expect(fs.existsSync(a)).toBe(true);
60
- expect(fs.existsSync(b)).toBe(true);
61
- expect(fs.existsSync(c)).toBe(true);
62
- });
63
- test("subprocess: SIGTERM also triggers cleanup", () => {
64
- const sigDir = makeTempDir();
65
- const a = path.join(sigDir, "term.touchstone");
66
- const driverScript = `
67
- import { registerCleanup } from ${JSON.stringify(path.join(repoRoot, "tests", "bench", "cleanup.ts"))};
68
- import fs from "node:fs";
69
-
70
- registerCleanup(() => fs.writeFileSync(${JSON.stringify(a)}, "term"));
71
- process.kill(process.pid, "SIGTERM");
72
- await new Promise((r) => setTimeout(r, 2000));
73
- `;
74
- const scriptPath = path.join(sigDir, "driver-term.mjs");
75
- fs.writeFileSync(scriptPath, driverScript);
76
- const result = spawnSync("bun", ["run", scriptPath], {
77
- encoding: "utf8",
78
- timeout: 10_000,
79
- });
80
- expect(result.status).toBe(130);
81
- expect(fs.existsSync(a)).toBe(true);
82
- });
83
- });
@@ -1,234 +0,0 @@
1
- /**
2
- * Shared cleanup registry for the bench harness (#267).
3
- *
4
- * The bench creates many tmp directories — per (task, arm, seed) workspace,
5
- * per-task fixture stash, per-fixture evolveStash + preStash. Each of these
6
- * is wrapped in a try/finally so happy-path runs leave nothing behind. But
7
- * an external SIGINT/SIGTERM (operator hits Ctrl-C, CI cancels the job)
8
- * bypasses `finally` blocks entirely on Bun, leaving orphan tmp dirs under
9
- * the bench tmp root (#276 redirected this from the OS temp dir to
10
- * `${AKM_CACHE_DIR}/bench/`) that nothing reaps.
11
- *
12
- * `registerCleanup(fn)` captures the cleanup intent on a process-wide
13
- * registry and returns a deregister function. The first `registerCleanup`
14
- * call also installs ONE pair of SIGINT/SIGTERM handlers — subsequent calls
15
- * never re-install. On signal we walk every registered fn (swallowing
16
- * errors), remove our own listeners (so a second Ctrl-C force-exits), and
17
- * `process.exit(130)`.
18
- *
19
- * The handler is idempotent: re-entrant signals while cleanup is in flight
20
- * are dropped. Per-tmp `try/finally` callers should:
21
- * 1. Register the cleanup at the top of `try`.
22
- * 2. Deregister it in `finally` *before* running cleanup themselves so the
23
- * handler doesn't double-fire.
24
- *
25
- * Garbage-collection of orphan dirs (#276): the FIRST `registerCleanup` call
26
- * also sweeps `${AKM_CACHE_DIR}/bench/*` entries older than 6h. This catches
27
- * orphans from prior crashed runs that bypassed `finally`. Subsequent calls
28
- * never re-sweep — the GC is install-once.
29
- */
30
- import * as fs from "node:fs";
31
- import * as path from "node:path";
32
- import { warn } from "../../src/core/warn";
33
- import { benchTmpRoot } from "./tmp";
34
- /**
35
- * Register a process-group kill for a spawned opencode PID.
36
- *
37
- * On SIGINT/SIGTERM the bench driver must kill the entire opencode process
38
- * group (not just the node wrapper) so .opencode children don't become orphans
39
- * that keep pipes open and block subsequent runs.
40
- *
41
- * Call this immediately after spawning opencode. Returns a deregister thunk
42
- * that should be called once the process has exited (in the run's finally
43
- * block).
44
- *
45
- * The SIGKILL is sent to the process group (`-pid`) if available, falling back
46
- * to the individual PID for environments where group-kill is unavailable.
47
- */
48
- export function registerProcessGroupCleanup(pid) {
49
- const fn = () => {
50
- try {
51
- process.kill(-pid, "SIGKILL");
52
- }
53
- catch {
54
- // Process group may not exist (process already exited or pid unavailable).
55
- try {
56
- process.kill(pid, "SIGKILL");
57
- }
58
- catch {
59
- /* already gone */
60
- }
61
- }
62
- };
63
- return registerCleanup(fn);
64
- }
65
- const registry = {
66
- fns: new Set(),
67
- installed: false,
68
- running: false,
69
- };
70
- /**
71
- * Register a cleanup function. Returns a deregister thunk that removes the
72
- * function from the registry. Calling deregister after the function has
73
- * already run is a no-op.
74
- */
75
- export function registerCleanup(fn) {
76
- registry.fns.add(fn);
77
- installSignalHandlers();
78
- return () => {
79
- registry.fns.delete(fn);
80
- };
81
- }
82
- /** GC threshold for orphan bench tmp dirs: 6 hours in milliseconds. */
83
- const BENCH_TMP_GC_MAX_AGE_MS = 6 * 60 * 60 * 1000;
84
- /**
85
- * Sweep `${AKM_CACHE_DIR}/bench/*` entries whose mtime is older than 6h.
86
- * Best-effort: any individual rmSync failure is swallowed (warned in
87
- * verbose mode) so a permission-bound entry does not kill the install.
88
- *
89
- * Idempotent because it only runs from the first-installer path in
90
- * `installSignalHandlers` — gated by `registry.installed`.
91
- */
92
- function gcOrphanBenchTmp() {
93
- let root;
94
- try {
95
- root = benchTmpRoot();
96
- }
97
- catch {
98
- // If the cache dir cannot be resolved (e.g. HOME unset in a sandboxed
99
- // CI shell), skip GC silently — the bench will fail later on its own.
100
- return;
101
- }
102
- let entries;
103
- try {
104
- entries = fs.readdirSync(root);
105
- }
106
- catch {
107
- // Root does not yet exist or is unreadable — nothing to reap.
108
- return;
109
- }
110
- const cutoff = Date.now() - BENCH_TMP_GC_MAX_AGE_MS;
111
- for (const name of entries) {
112
- const full = path.join(root, name);
113
- let stat;
114
- try {
115
- stat = fs.lstatSync(full);
116
- }
117
- catch {
118
- continue;
119
- }
120
- if (stat.mtimeMs > cutoff)
121
- continue;
122
- try {
123
- fs.rmSync(full, { recursive: true, force: true });
124
- }
125
- catch (err) {
126
- warn(`bench tmp GC: could not remove ${full}: ${err.message}`);
127
- }
128
- }
129
- }
130
- function installSignalHandlers() {
131
- if (registry.installed)
132
- return;
133
- registry.installed = true;
134
- // First-installer GC sweep: reap orphan bench tmp dirs older than 6h.
135
- // Subsequent registerCleanup() calls never re-trigger this — the
136
- // `registry.installed` guard above ensures install-once semantics.
137
- gcOrphanBenchTmp();
138
- const handler = () => {
139
- // Re-entrant signals are dropped — a second Ctrl-C will hit our
140
- // already-removed listeners and the runtime's default handler will
141
- // force-exit. That is the documented escape hatch.
142
- if (registry.running)
143
- return;
144
- registry.running = true;
145
- // Snapshot then drop registrations. We invoke synchronously where
146
- // possible; async fns get fired-and-forget but we still await them so
147
- // the exit doesn't beat the rmdir on slow filesystems.
148
- const fns = [...registry.fns];
149
- registry.fns.clear();
150
- void runAllAndExit(fns);
151
- };
152
- registry.handlerSigint = handler;
153
- registry.handlerSigterm = handler;
154
- process.on("SIGINT", handler);
155
- process.on("SIGTERM", handler);
156
- }
157
- async function runAllAndExit(fns) {
158
- // BUG-H5: wrap the body in try/finally so a synchronous throw outside the
159
- // per-fn try/catch (e.g. an exception thrown by `process.off` on a
160
- // pathological listener list) does not leave `registry.running = true`.
161
- // Without this guard, a subsequent registerCleanup() call would re-install
162
- // listeners but the new handler would short-circuit on the stale flag and
163
- // skip cleanup on the next signal.
164
- try {
165
- for (const fn of fns) {
166
- try {
167
- await fn();
168
- }
169
- catch {
170
- // Best-effort: cleanup must never throw out of the signal path.
171
- }
172
- }
173
- // Remove our listeners so a second Ctrl-C force-exits via the default.
174
- if (registry.handlerSigint)
175
- process.off("SIGINT", registry.handlerSigint);
176
- if (registry.handlerSigterm)
177
- process.off("SIGTERM", registry.handlerSigterm);
178
- registry.installed = false;
179
- registry.handlerSigint = undefined;
180
- registry.handlerSigterm = undefined;
181
- }
182
- finally {
183
- registry.running = false;
184
- // 128 + SIGINT(2) — POSIX convention for signal-induced exits.
185
- process.exit(130);
186
- }
187
- }
188
- // ── Test-only seam ──────────────────────────────────────────────────────────
189
- /**
190
- * Test-only: drive the cleanup path as if a signal arrived, *without*
191
- * calling `process.exit`. Returns a promise that resolves once every
192
- * registered fn has settled. Used by the unit test to assert ordering
193
- * without killing the test process.
194
- *
195
- * Resets the registry to an uninstalled state on completion so subsequent
196
- * tests can re-install handlers cleanly.
197
- */
198
- export async function _drainForTest() {
199
- const fns = [...registry.fns];
200
- registry.fns.clear();
201
- registry.running = true;
202
- for (const fn of fns) {
203
- try {
204
- await fn();
205
- }
206
- catch {
207
- /* swallow */
208
- }
209
- }
210
- if (registry.handlerSigint)
211
- process.off("SIGINT", registry.handlerSigint);
212
- if (registry.handlerSigterm)
213
- process.off("SIGTERM", registry.handlerSigterm);
214
- registry.installed = false;
215
- registry.running = false;
216
- registry.handlerSigint = undefined;
217
- registry.handlerSigterm = undefined;
218
- }
219
- /** Test-only: reset the registry without firing cleanups (for unit setup). */
220
- export function _resetForTest() {
221
- registry.fns.clear();
222
- if (registry.handlerSigint)
223
- process.off("SIGINT", registry.handlerSigint);
224
- if (registry.handlerSigterm)
225
- process.off("SIGTERM", registry.handlerSigterm);
226
- registry.installed = false;
227
- registry.running = false;
228
- registry.handlerSigint = undefined;
229
- registry.handlerSigterm = undefined;
230
- }
231
- /** Test-only: peek at the current registration count. */
232
- export function _registeredCountForTest() {
233
- return registry.fns.size;
234
- }
@@ -1,166 +0,0 @@
1
- /**
2
- * Unit tests for the bench cleanup registry (#267).
3
- *
4
- * The shared registry installs ONE pair of SIGINT/SIGTERM handlers on first
5
- * registration and runs every registered fn when a signal fires. We use the
6
- * `_drainForTest` test seam so the assertions don't have to actually kill
7
- * the test process — but we also exercise the real handler installation
8
- * path to make sure it's idempotent.
9
- */
10
- import { afterEach, beforeEach, describe, expect, test } from "bun:test";
11
- import fs from "node:fs";
12
- import { _drainForTest, _registeredCountForTest, _resetForTest, registerCleanup } from "./cleanup";
13
- import { benchMkdtemp, benchTmpRoot } from "./tmp";
14
- beforeEach(() => {
15
- _resetForTest();
16
- });
17
- afterEach(() => {
18
- _resetForTest();
19
- });
20
- describe("registerCleanup (#267)", () => {
21
- test("registers a cleanup fn and increments the count", () => {
22
- expect(_registeredCountForTest()).toBe(0);
23
- registerCleanup(() => { });
24
- expect(_registeredCountForTest()).toBe(1);
25
- });
26
- test("returns a deregister thunk that drops the registration", () => {
27
- const deregister = registerCleanup(() => { });
28
- expect(_registeredCountForTest()).toBe(1);
29
- deregister();
30
- expect(_registeredCountForTest()).toBe(0);
31
- });
32
- test("drainForTest runs every registered cleanup once", async () => {
33
- const calls = [];
34
- registerCleanup(() => {
35
- calls.push("a");
36
- });
37
- registerCleanup(() => {
38
- calls.push("b");
39
- });
40
- registerCleanup(() => {
41
- calls.push("c");
42
- });
43
- await _drainForTest();
44
- expect(calls.sort()).toEqual(["a", "b", "c"]);
45
- // Registry is empty after drain.
46
- expect(_registeredCountForTest()).toBe(0);
47
- });
48
- test("drainForTest swallows errors so one bad fn doesn't block the rest", async () => {
49
- const calls = [];
50
- registerCleanup(() => {
51
- calls.push("first");
52
- });
53
- registerCleanup(() => {
54
- throw new Error("boom");
55
- });
56
- registerCleanup(() => {
57
- calls.push("third");
58
- });
59
- await _drainForTest();
60
- expect(calls.sort()).toEqual(["first", "third"]);
61
- });
62
- test("drainForTest awaits async cleanup fns", async () => {
63
- const calls = [];
64
- registerCleanup(async () => {
65
- await new Promise((resolve) => setTimeout(resolve, 10));
66
- calls.push("async-done");
67
- });
68
- await _drainForTest();
69
- expect(calls).toEqual(["async-done"]);
70
- });
71
- test("idempotent installer: repeated registers do not multiply listeners", () => {
72
- const initialSigint = process.listenerCount("SIGINT");
73
- const initialSigterm = process.listenerCount("SIGTERM");
74
- registerCleanup(() => { });
75
- registerCleanup(() => { });
76
- registerCleanup(() => { });
77
- // Exactly one listener each, no matter how many cleanup fns we add.
78
- expect(process.listenerCount("SIGINT")).toBe(initialSigint + 1);
79
- expect(process.listenerCount("SIGTERM")).toBe(initialSigterm + 1);
80
- });
81
- test("deregister-all leaves the listeners installed (registry is sticky)", () => {
82
- // The contract is: once installed, the handlers stay installed for the
83
- // process lifetime. Subsequent register calls reuse them.
84
- registerCleanup(() => { })();
85
- expect(_registeredCountForTest()).toBe(0);
86
- // Re-register: this MUST NOT add a second pair of listeners.
87
- const initialSigint = process.listenerCount("SIGINT");
88
- registerCleanup(() => { });
89
- expect(process.listenerCount("SIGINT")).toBe(initialSigint);
90
- });
91
- test("deregistered fns do not run on drain", async () => {
92
- const calls = [];
93
- const dereg = registerCleanup(() => {
94
- calls.push("kept");
95
- });
96
- const _wasted = registerCleanup(() => {
97
- calls.push("dropped");
98
- });
99
- _wasted();
100
- void dereg; // keep referenced
101
- await _drainForTest();
102
- expect(calls).toEqual(["kept"]);
103
- });
104
- test("simulated SIGINT path: handler runs cleanup before exit (via drain seam)", async () => {
105
- // We can't actually `process.exit(130)` inside a unit test, so we use the
106
- // drain seam (which mirrors the runAllAndExit path minus the exit call).
107
- // This verifies the OBSERVABLE behaviour the brief asked for: signal →
108
- // every registered cleanup fn ran.
109
- const ran = [];
110
- registerCleanup(() => {
111
- ran.push("rmsync(workspace)");
112
- });
113
- registerCleanup(() => {
114
- ran.push("rmsync(stash)");
115
- });
116
- await _drainForTest();
117
- expect(ran.length).toBe(2);
118
- expect(ran).toContain("rmsync(workspace)");
119
- expect(ran).toContain("rmsync(stash)");
120
- });
121
- test("first registerCleanup sweeps bench tmp entries older than 6h (#276)", () => {
122
- // Ensure root exists before populating.
123
- benchTmpRoot();
124
- // Stale entry: mtime backdated 7h.
125
- const stale = benchMkdtemp("akm-bench-gc-stale-");
126
- const sevenHoursAgo = (Date.now() - 7 * 60 * 60 * 1000) / 1000;
127
- fs.utimesSync(stale, sevenHoursAgo, sevenHoursAgo);
128
- // Fresh entry: untouched mtime (now).
129
- const fresh = benchMkdtemp("akm-bench-gc-fresh-");
130
- expect(fs.existsSync(stale)).toBe(true);
131
- expect(fs.existsSync(fresh)).toBe(true);
132
- // Trigger first-installer GC.
133
- registerCleanup(() => { });
134
- expect(fs.existsSync(stale)).toBe(false);
135
- expect(fs.existsSync(fresh)).toBe(true);
136
- // Cleanup the fresh entry ourselves.
137
- fs.rmSync(fresh, { recursive: true, force: true });
138
- });
139
- test("GC is idempotent — second registerCleanup does not re-sweep", () => {
140
- // Install once with a sentinel registered.
141
- registerCleanup(() => { });
142
- // Now create a stale entry AFTER install. Second registerCleanup must
143
- // NOT sweep it because the GC only runs on first install.
144
- const stale = benchMkdtemp("akm-bench-gc-postinstall-");
145
- const sevenHoursAgo = (Date.now() - 7 * 60 * 60 * 1000) / 1000;
146
- fs.utimesSync(stale, sevenHoursAgo, sevenHoursAgo);
147
- registerCleanup(() => { });
148
- expect(fs.existsSync(stale)).toBe(true);
149
- fs.rmSync(stale, { recursive: true, force: true });
150
- });
151
- test("re-entrant signals during running cleanup are dropped", async () => {
152
- // Drive the registered handler twice in sequence. The second drain
153
- // should observe the cleared registry (running flag flipped) and run
154
- // nothing extra. This protects against a Ctrl-C double-press
155
- // interrupting cleanup mid-run.
156
- const calls = [];
157
- registerCleanup(() => {
158
- calls.push("first");
159
- });
160
- await _drainForTest();
161
- expect(calls).toEqual(["first"]);
162
- // No further fns registered → second drain is a no-op.
163
- await _drainForTest();
164
- expect(calls).toEqual(["first"]);
165
- });
166
- });