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,395 +0,0 @@
1
- import { afterEach, describe, expect, test } from "bun:test";
2
- import { spawnSync } from "node:child_process";
3
- import fs from "node:fs";
4
- import os from "node:os";
5
- import path from "node:path";
6
- const CLI = path.join(__dirname, "..", "src", "cli.ts");
7
- const tempDirs = [];
8
- function makeTempDir(prefix) {
9
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
10
- tempDirs.push(dir);
11
- return dir;
12
- }
13
- function createWorkflowEnv() {
14
- const stashDir = makeTempDir("akm-wfqa-stash-");
15
- const xdgCache = makeTempDir("akm-wfqa-cache-");
16
- const xdgConfig = makeTempDir("akm-wfqa-config-");
17
- return {
18
- ...process.env,
19
- AKM_STASH_DIR: stashDir,
20
- XDG_CACHE_HOME: xdgCache,
21
- XDG_CONFIG_HOME: xdgConfig,
22
- };
23
- }
24
- function runCli(args, env) {
25
- return spawnSync("bun", [CLI, ...args], {
26
- encoding: "utf8",
27
- timeout: 30_000,
28
- env,
29
- });
30
- }
31
- afterEach(() => {
32
- for (const dir of tempDirs.splice(0)) {
33
- fs.rmSync(dir, { recursive: true, force: true });
34
- }
35
- });
36
- const TWO_STEP_WORKFLOW = `---
37
- description: Test workflow
38
- ---
39
-
40
- # Workflow: Test Flow
41
-
42
- ## Step: First Step
43
- Step ID: first
44
-
45
- ### Instructions
46
- Do the first thing.
47
-
48
- ### Completion Criteria
49
- - First thing done
50
-
51
- ## Step: Second Step
52
- Step ID: second
53
-
54
- ### Instructions
55
- Do the second thing.
56
- `;
57
- function setupWorkflow(env, name = "test-flow") {
58
- const sourceDir = makeTempDir("akm-wfqa-src-");
59
- const sourcePath = path.join(sourceDir, "wf.md");
60
- fs.writeFileSync(sourcePath, TWO_STEP_WORKFLOW, "utf8");
61
- const result = runCli(["workflow", "create", name, "--from", sourcePath], env);
62
- if (result.status !== 0) {
63
- throw new Error(`Failed to create workflow: ${result.stderr}`);
64
- }
65
- }
66
- // ---------------------------------------------------------------------------
67
- // 1. resumeWorkflowRun — blocked → active; fails on completed
68
- // ---------------------------------------------------------------------------
69
- describe("workflow resume command", () => {
70
- test("resume flips a blocked run back to active", () => {
71
- const env = createWorkflowEnv();
72
- setupWorkflow(env);
73
- const started = runCli(["workflow", "start", "workflow:test-flow"], env);
74
- expect(started.status).toBe(0);
75
- const { run: startRun } = JSON.parse(started.stdout);
76
- // Complete first step as blocked
77
- expect(runCli(["workflow", "complete", startRun.id, "--step", "first", "--state", "blocked"], env).status).toBe(0);
78
- // Verify it's blocked
79
- const statusBlocked = runCli(["workflow", "status", startRun.id], env);
80
- expect(statusBlocked.status).toBe(0);
81
- const { run: blockedRun } = JSON.parse(statusBlocked.stdout);
82
- expect(blockedRun.status).toBe("blocked");
83
- // Resume it
84
- const resumed = runCli(["workflow", "resume", startRun.id], env);
85
- expect(resumed.status).toBe(0);
86
- const { run: resumedRun } = JSON.parse(resumed.stdout);
87
- expect(resumedRun.status).toBe("active");
88
- });
89
- test("resume on a completed run returns an error", () => {
90
- const env = createWorkflowEnv();
91
- setupWorkflow(env);
92
- const started = runCli(["workflow", "start", "workflow:test-flow"], env);
93
- expect(started.status).toBe(0);
94
- const { run: startRun } = JSON.parse(started.stdout);
95
- expect(runCli(["workflow", "complete", startRun.id, "--step", "first"], env).status).toBe(0);
96
- expect(runCli(["workflow", "complete", startRun.id, "--step", "second"], env).status).toBe(0);
97
- const resumed = runCli(["workflow", "resume", startRun.id], env);
98
- expect(resumed.status).toBe(2);
99
- const err = JSON.parse(resumed.stderr);
100
- expect(err.error).toContain("already completed");
101
- });
102
- test("resume on a failed run flips it to active", () => {
103
- const env = createWorkflowEnv();
104
- setupWorkflow(env);
105
- const started = runCli(["workflow", "start", "workflow:test-flow"], env);
106
- expect(started.status).toBe(0);
107
- const { run: startRun } = JSON.parse(started.stdout);
108
- expect(runCli(["workflow", "complete", startRun.id, "--step", "first", "--state", "failed"], env).status).toBe(0);
109
- const resumed = runCli(["workflow", "resume", startRun.id], env);
110
- expect(resumed.status).toBe(0);
111
- const { run: resumedRun } = JSON.parse(resumed.stdout);
112
- expect(resumedRun.status).toBe("active");
113
- });
114
- // Issue #156: after resuming a blocked run, the previously-blocked step must be
115
- // re-actionable so it can be reclassified to completed/failed/skipped.
116
- for (const newState of ["completed", "failed", "skipped"]) {
117
- test(`resume re-opens a blocked step so it can be reclassified to ${newState}`, () => {
118
- const env = createWorkflowEnv();
119
- setupWorkflow(env);
120
- const started = runCli(["workflow", "start", "workflow:test-flow"], env);
121
- expect(started.status).toBe(0);
122
- const { run: startRun } = JSON.parse(started.stdout);
123
- expect(runCli(["workflow", "complete", startRun.id, "--step", "first", "--state", "blocked"], env).status).toBe(0);
124
- expect(runCli(["workflow", "resume", startRun.id], env).status).toBe(0);
125
- const reclassified = runCli(["workflow", "complete", startRun.id, "--step", "first", "--state", newState, "--notes", "resolved"], env);
126
- expect(reclassified.status).toBe(0);
127
- const parsed = JSON.parse(reclassified.stdout);
128
- const firstStep = parsed.workflow.steps.find((s) => s.id === "first");
129
- expect(firstStep?.status).toBe(newState);
130
- });
131
- }
132
- test("resume does not disturb already-completed earlier steps", () => {
133
- const env = createWorkflowEnv();
134
- setupWorkflow(env);
135
- const started = runCli(["workflow", "start", "workflow:test-flow"], env);
136
- expect(started.status).toBe(0);
137
- const { run: startRun } = JSON.parse(started.stdout);
138
- expect(runCli(["workflow", "complete", startRun.id, "--step", "first"], env).status).toBe(0);
139
- expect(runCli(["workflow", "complete", startRun.id, "--step", "second", "--state", "blocked"], env).status).toBe(0);
140
- expect(runCli(["workflow", "resume", startRun.id], env).status).toBe(0);
141
- const status = runCli(["workflow", "status", startRun.id], env);
142
- expect(status.status).toBe(0);
143
- const detail = JSON.parse(status.stdout);
144
- expect(detail.workflow.steps.find((s) => s.id === "first")?.status).toBe("completed");
145
- expect(detail.workflow.steps.find((s) => s.id === "second")?.status).toBe("pending");
146
- });
147
- });
148
- // ---------------------------------------------------------------------------
149
- // 2. listWorkflowRuns --active includes blocked runs
150
- // ---------------------------------------------------------------------------
151
- describe("workflow list --active includes blocked", () => {
152
- test("blocked run appears in --active list", () => {
153
- const env = createWorkflowEnv();
154
- setupWorkflow(env);
155
- const started = runCli(["workflow", "start", "workflow:test-flow"], env);
156
- expect(started.status).toBe(0);
157
- const { run: startRun } = JSON.parse(started.stdout);
158
- expect(runCli(["workflow", "complete", startRun.id, "--step", "first", "--state", "blocked"], env).status).toBe(0);
159
- const listed = runCli(["workflow", "list", "--ref", "workflow:test-flow", "--active"], env);
160
- expect(listed.status).toBe(0);
161
- const { runs } = JSON.parse(listed.stdout);
162
- expect(runs.some((r) => r.id === startRun.id && r.status === "blocked")).toBe(true);
163
- });
164
- test("completed run does NOT appear in --active list", () => {
165
- const env = createWorkflowEnv();
166
- setupWorkflow(env);
167
- const started = runCli(["workflow", "start", "workflow:test-flow"], env);
168
- expect(started.status).toBe(0);
169
- const { run: startRun } = JSON.parse(started.stdout);
170
- expect(runCli(["workflow", "complete", startRun.id, "--step", "first"], env).status).toBe(0);
171
- expect(runCli(["workflow", "complete", startRun.id, "--step", "second"], env).status).toBe(0);
172
- const listed = runCli(["workflow", "list", "--active"], env);
173
- expect(listed.status).toBe(0);
174
- const { runs } = JSON.parse(listed.stdout);
175
- expect(runs.some((r) => r.id === startRun.id)).toBe(false);
176
- });
177
- });
178
- // ---------------------------------------------------------------------------
179
- // 3. workflow next on a completed run: done:true, step:null
180
- // ---------------------------------------------------------------------------
181
- describe("workflow next — completed run signals done", () => {
182
- test("next on a completed run-id returns done:true and step:null", () => {
183
- const env = createWorkflowEnv();
184
- setupWorkflow(env);
185
- const started = runCli(["workflow", "start", "workflow:test-flow"], env);
186
- expect(started.status).toBe(0);
187
- const { run: startRun } = JSON.parse(started.stdout);
188
- expect(runCli(["workflow", "complete", startRun.id, "--step", "first"], env).status).toBe(0);
189
- expect(runCli(["workflow", "complete", startRun.id, "--step", "second"], env).status).toBe(0);
190
- const next = runCli(["workflow", "next", startRun.id], env);
191
- expect(next.status).toBe(0);
192
- const nextJson = JSON.parse(next.stdout);
193
- expect(nextJson.done).toBe(true);
194
- expect(nextJson.step).toBeNull();
195
- });
196
- });
197
- // ---------------------------------------------------------------------------
198
- // 4. workflow next <ref> auto-start: autoStarted:true
199
- // ---------------------------------------------------------------------------
200
- describe("workflow next — auto-start flags autoStarted", () => {
201
- test("next on a ref with no existing run returns autoStarted:true", () => {
202
- const env = createWorkflowEnv();
203
- setupWorkflow(env);
204
- const next = runCli(["workflow", "next", "workflow:test-flow"], env);
205
- expect(next.status).toBe(0);
206
- const nextJson = JSON.parse(next.stdout);
207
- expect(nextJson.autoStarted).toBe(true);
208
- expect(nextJson.run.status).toBe("active");
209
- expect(nextJson.step.id).toBe("first");
210
- });
211
- test("next on a ref with existing active run does NOT set autoStarted", () => {
212
- const env = createWorkflowEnv();
213
- setupWorkflow(env);
214
- // First call auto-starts
215
- const first = runCli(["workflow", "next", "workflow:test-flow"], env);
216
- expect(first.status).toBe(0);
217
- // Second call resumes existing — no autoStarted
218
- const second = runCli(["workflow", "next", "workflow:test-flow"], env);
219
- expect(second.status).toBe(0);
220
- const secondJson = JSON.parse(second.stdout);
221
- expect(secondJson.autoStarted).toBeUndefined();
222
- });
223
- });
224
- // ---------------------------------------------------------------------------
225
- // 5. workflow next --params: sets params on auto-start; fails on existing run
226
- // ---------------------------------------------------------------------------
227
- describe("workflow next --params", () => {
228
- test("--params is accepted when auto-starting a new run", () => {
229
- const env = createWorkflowEnv();
230
- setupWorkflow(env);
231
- const next = runCli(["workflow", "next", "workflow:test-flow", "--params", '{"x":1}'], env);
232
- expect(next.status).toBe(0);
233
- const nextJson = JSON.parse(next.stdout);
234
- expect(nextJson.autoStarted).toBe(true);
235
- expect(nextJson.run.params?.x).toBe(1);
236
- });
237
- test("--params fails when an active run already exists (ref specifier)", () => {
238
- const env = createWorkflowEnv();
239
- setupWorkflow(env);
240
- // Start a run first
241
- expect(runCli(["workflow", "start", "workflow:test-flow"], env).status).toBe(0);
242
- const next = runCli(["workflow", "next", "workflow:test-flow", "--params", '{"x":1}'], env);
243
- expect(next.status).toBe(2);
244
- const err = JSON.parse(next.stderr);
245
- expect(err.error).toContain("--params can only be set on a new run");
246
- });
247
- test("--params fails when given a direct run-id (existing run)", () => {
248
- const env = createWorkflowEnv();
249
- setupWorkflow(env);
250
- const started = runCli(["workflow", "start", "workflow:test-flow"], env);
251
- expect(started.status).toBe(0);
252
- const { run: startRun } = JSON.parse(started.stdout);
253
- const next = runCli(["workflow", "next", startRun.id, "--params", '{"x":1}'], env);
254
- expect(next.status).toBe(2);
255
- const err = JSON.parse(next.stderr);
256
- expect(err.error).toContain("--params can only be used when starting a new run from a workflow ref");
257
- expect(err.error).toContain("existing run id");
258
- });
259
- });
260
- // ---------------------------------------------------------------------------
261
- // 6. workflow status workflow:<name> resolves to most-recent run
262
- // ---------------------------------------------------------------------------
263
- describe("workflow status with workflow ref", () => {
264
- test("status workflow:<name> resolves to the most-recently-updated run", () => {
265
- const env = createWorkflowEnv();
266
- setupWorkflow(env);
267
- const started = runCli(["workflow", "start", "workflow:test-flow"], env);
268
- expect(started.status).toBe(0);
269
- const { run: startRun } = JSON.parse(started.stdout);
270
- const status = runCli(["workflow", "status", "workflow:test-flow"], env);
271
- expect(status.status).toBe(0);
272
- const statusJson = JSON.parse(status.stdout);
273
- expect(statusJson.run.id).toBe(startRun.id);
274
- expect(statusJson.run.status).toBe("active");
275
- });
276
- test("status workflow:<name> returns NotFoundError when no runs exist", () => {
277
- const env = createWorkflowEnv();
278
- setupWorkflow(env);
279
- const status = runCli(["workflow", "status", "workflow:test-flow"], env);
280
- // No runs created yet — should fail with not-found (exit 1)
281
- expect(status.status).toBe(1);
282
- const err = JSON.parse(status.stderr);
283
- expect(err.error).toContain("No workflow runs found");
284
- });
285
- test("next with an unknown run id returns WORKFLOW_NOT_FOUND", () => {
286
- const env = createWorkflowEnv();
287
- setupWorkflow(env);
288
- const next = runCli(["workflow", "next", "bogus-run-id"], env);
289
- expect(next.status).toBe(1);
290
- const err = JSON.parse(next.stderr);
291
- expect(err.code).toBe("WORKFLOW_NOT_FOUND");
292
- expect(err.hint).toContain("akm workflow list --active");
293
- });
294
- test("status with an unknown run id returns WORKFLOW_NOT_FOUND", () => {
295
- const env = createWorkflowEnv();
296
- setupWorkflow(env);
297
- const status = runCli(["workflow", "status", "bogus-run-id"], env);
298
- expect(status.status).toBe(1);
299
- const err = JSON.parse(status.stderr);
300
- expect(err.code).toBe("WORKFLOW_NOT_FOUND");
301
- expect(err.hint).toContain("akm workflow list --active");
302
- });
303
- });
304
- // ---------------------------------------------------------------------------
305
- // 7. workflow create name validation
306
- // ---------------------------------------------------------------------------
307
- describe("workflow create — name validation", () => {
308
- test("name with spaces is rejected", () => {
309
- const env = createWorkflowEnv();
310
- const result = runCli(["workflow", "create", "name with spaces"], env);
311
- expect(result.status).toBe(2);
312
- const err = JSON.parse(result.stderr);
313
- expect(err.error).toContain("Workflow name must start with a lowercase letter");
314
- });
315
- test("name with uppercase is rejected", () => {
316
- const env = createWorkflowEnv();
317
- const result = runCli(["workflow", "create", "MyWorkflow"], env);
318
- expect(result.status).toBe(2);
319
- const err = JSON.parse(result.stderr);
320
- expect(err.error).toContain("Workflow name must start with a lowercase letter");
321
- });
322
- test("valid lowercase name is accepted", () => {
323
- const env = createWorkflowEnv();
324
- const result = runCli(["workflow", "create", "my-workflow"], env);
325
- expect(result.status).toBe(0);
326
- const json = JSON.parse(result.stdout);
327
- expect(json.ref).toBe("workflow:my-workflow");
328
- });
329
- test("hierarchical name with forward slash is accepted", () => {
330
- // Slashes are allowed for hierarchical naming (e.g. release/ship)
331
- const env = createWorkflowEnv();
332
- const result = runCli(["workflow", "create", "release/ship"], env);
333
- expect(result.status).toBe(0);
334
- const json = JSON.parse(result.stdout);
335
- expect(json.ref).toBe("workflow:release/ship");
336
- });
337
- test("name validation error message mentions slashes", () => {
338
- const env = createWorkflowEnv();
339
- const result = runCli(["workflow", "create", "BAD NAME"], env);
340
- expect(result.status).toBe(2);
341
- const err = JSON.parse(result.stderr);
342
- expect(err.error).toContain("slashes");
343
- });
344
- });
345
- // ---------------------------------------------------------------------------
346
- // 8. workflow create --force without --from/--reset is rejected
347
- // ---------------------------------------------------------------------------
348
- describe("workflow create --force guard", () => {
349
- test("--force without --from or --reset is rejected", () => {
350
- const env = createWorkflowEnv();
351
- setupWorkflow(env);
352
- const result = runCli(["workflow", "create", "test-flow", "--force"], env);
353
- expect(result.status).toBe(2);
354
- const err = JSON.parse(result.stderr);
355
- expect(err.error).toContain("Refusing to overwrite with template");
356
- });
357
- test("--force --reset succeeds and overwrites with template", () => {
358
- const env = createWorkflowEnv();
359
- setupWorkflow(env);
360
- const result = runCli(["workflow", "create", "test-flow", "--force", "--reset"], env);
361
- expect(result.status).toBe(0);
362
- const json = JSON.parse(result.stdout);
363
- expect(json.ok).toBe(true);
364
- expect(json.ref).toBe("workflow:test-flow");
365
- });
366
- test("--force --from <file> succeeds and overwrites with file content", () => {
367
- const env = createWorkflowEnv();
368
- setupWorkflow(env);
369
- const sourceDir = makeTempDir("akm-wfqa-src2-");
370
- const sourcePath = path.join(sourceDir, "new.md");
371
- fs.writeFileSync(sourcePath, TWO_STEP_WORKFLOW, "utf8");
372
- const result = runCli(["workflow", "create", "test-flow", "--force", "--from", sourcePath], env);
373
- expect(result.status).toBe(0);
374
- const json = JSON.parse(result.stdout);
375
- expect(json.ok).toBe(true);
376
- });
377
- });
378
- // ---------------------------------------------------------------------------
379
- // 9. complete --state help text documents default (just ensure it runs)
380
- // ---------------------------------------------------------------------------
381
- describe("workflow complete --state default", () => {
382
- test("complete without --state defaults to completed", () => {
383
- const env = createWorkflowEnv();
384
- setupWorkflow(env);
385
- const started = runCli(["workflow", "start", "workflow:test-flow"], env);
386
- expect(started.status).toBe(0);
387
- const { run: startRun } = JSON.parse(started.stdout);
388
- // No --state flag → should default to 'completed'
389
- const completed = runCli(["workflow", "complete", startRun.id, "--step", "first"], env);
390
- expect(completed.status).toBe(0);
391
- const json = JSON.parse(completed.stdout);
392
- const step = json.workflow.steps.find((s) => s.id === "first");
393
- expect(step?.status).toBe("completed");
394
- });
395
- });
@@ -1,213 +0,0 @@
1
- import { afterEach, beforeEach, expect, test } from "bun:test";
2
- import fs from "node:fs";
3
- import os from "node:os";
4
- import path from "node:path";
5
- import { getDbPath } from "../../src/core/paths";
6
- import { resetQuiet, resetVerbose, setVerbose } from "../../src/core/warn";
7
- import { closeDatabase, openDatabase } from "../../src/indexer/db";
8
- import { akmIndex } from "../../src/indexer/indexer";
9
- let testConfigDir = "";
10
- let testCacheDir = "";
11
- const originalXdgConfigHome = process.env.XDG_CONFIG_HOME;
12
- const originalXdgCacheHome = process.env.XDG_CACHE_HOME;
13
- beforeEach(() => {
14
- testConfigDir = fs.mkdtempSync(path.join(os.tmpdir(), "akm-wf-idx-config-"));
15
- testCacheDir = fs.mkdtempSync(path.join(os.tmpdir(), "akm-wf-idx-cache-"));
16
- process.env.XDG_CONFIG_HOME = testConfigDir;
17
- process.env.XDG_CACHE_HOME = testCacheDir;
18
- const dbPath = getDbPath();
19
- for (const f of [dbPath, `${dbPath}-wal`, `${dbPath}-shm`]) {
20
- try {
21
- fs.unlinkSync(f);
22
- }
23
- catch {
24
- /* ignore */
25
- }
26
- }
27
- // Defensive: other test files may have left the warn module's quiet/verbose
28
- // latches on. Reset both before each test so the noise-gate assertions read
29
- // a clean state.
30
- resetQuiet();
31
- resetVerbose();
32
- delete process.env.AKM_VERBOSE;
33
- });
34
- afterEach(() => {
35
- if (originalXdgConfigHome === undefined)
36
- delete process.env.XDG_CONFIG_HOME;
37
- else
38
- process.env.XDG_CONFIG_HOME = originalXdgConfigHome;
39
- if (originalXdgCacheHome === undefined)
40
- delete process.env.XDG_CACHE_HOME;
41
- else
42
- process.env.XDG_CACHE_HOME = originalXdgCacheHome;
43
- if (testConfigDir) {
44
- fs.rmSync(testConfigDir, { recursive: true, force: true });
45
- testConfigDir = "";
46
- }
47
- if (testCacheDir) {
48
- fs.rmSync(testCacheDir, { recursive: true, force: true });
49
- testCacheDir = "";
50
- }
51
- resetVerbose();
52
- delete process.env.AKM_VERBOSE;
53
- });
54
- function tmpStash() {
55
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), "akm-wf-idx-"));
56
- fs.mkdirSync(path.join(dir, "workflows"), { recursive: true });
57
- return dir;
58
- }
59
- function writeWorkflow(stashDir, name, content) {
60
- const file = path.join(stashDir, "workflows", `${name}.md`);
61
- fs.writeFileSync(file, content);
62
- return file;
63
- }
64
- const VALID_WORKFLOW = `# Workflow: Ship Release
65
-
66
- ## Step: Validate
67
- Step ID: validate
68
-
69
- ### Instructions
70
- Confirm release notes are present.
71
- `;
72
- const BROKEN_WORKFLOW = `# Workflow: Bad
73
-
74
- ## Step: First
75
- Step ID: first
76
- ### Instructions
77
- do A
78
-
79
- ## Step: Second
80
- Step ID: first
81
- ### Instructions
82
- do B
83
- `;
84
- test("indexer admits valid workflows and writes their JSON to workflow_documents", async () => {
85
- const stashDir = tmpStash();
86
- writeWorkflow(stashDir, "good", VALID_WORKFLOW);
87
- const result = await akmIndex({ stashDir, full: true });
88
- expect(result.totalEntries).toBe(1);
89
- const db = openDatabase();
90
- try {
91
- const row = db
92
- .prepare(`SELECT wd.document_json, wd.schema_version, wd.source_path
93
- FROM workflow_documents wd
94
- JOIN entries e ON e.id = wd.entry_id
95
- WHERE e.entry_type = 'workflow' AND e.entry_key LIKE ?`)
96
- .get(`${stashDir}:workflow:%`);
97
- expect(row).toBeDefined();
98
- if (!row)
99
- return;
100
- expect(row.schema_version).toBe(1);
101
- expect(row.source_path).toContain("good.md");
102
- const doc = JSON.parse(row.document_json);
103
- expect(doc.title).toBe("Ship Release");
104
- expect(doc.steps).toHaveLength(1);
105
- expect(doc.steps[0].instructions.text).toContain("Confirm release notes");
106
- expect(doc.steps[0].source.start).toBeGreaterThan(0);
107
- }
108
- finally {
109
- closeDatabase(db);
110
- }
111
- });
112
- test("indexer rejects broken workflows and surfaces every error in IndexResponse.warnings", async () => {
113
- const stashDir = tmpStash();
114
- writeWorkflow(stashDir, "good", VALID_WORKFLOW);
115
- const brokenPath = writeWorkflow(stashDir, "bad", BROKEN_WORKFLOW);
116
- const result = await akmIndex({ stashDir, full: true });
117
- expect(result.totalEntries).toBe(1); // only the good one
118
- expect(result.warnings ?? []).toBeDefined();
119
- const warnings = result.warnings ?? [];
120
- // The broken workflow has a duplicate step ID; the warning string must
121
- // mention the file and at least one of its errors.
122
- const brokenWarning = warnings.find((w) => w.includes(brokenPath));
123
- expect(brokenWarning).toBeDefined();
124
- expect(brokenWarning).toMatch(/already used|Step ID/);
125
- const db = openDatabase();
126
- try {
127
- const goodRow = db
128
- .prepare(`SELECT 1 FROM workflow_documents wd
129
- JOIN entries e ON e.id = wd.entry_id
130
- WHERE e.entry_key = ?`)
131
- .get(`${stashDir}:workflow:good`);
132
- expect(goodRow).toBeDefined();
133
- const badRow = db
134
- .prepare(`SELECT 1 FROM workflow_documents wd
135
- JOIN entries e ON e.id = wd.entry_id
136
- WHERE e.entry_key = ?`)
137
- .get(`${stashDir}:workflow:bad`);
138
- expect(badRow).toBeFalsy();
139
- }
140
- finally {
141
- closeDatabase(db);
142
- }
143
- });
144
- // ── Workflow validation noise gate (issue #273) ─────────────────────────────
145
- async function captureStderr(fn) {
146
- const lines = [];
147
- const originalWarn = console.warn.bind(console);
148
- console.warn = (...args) => {
149
- lines.push(args.map((a) => (typeof a === "string" ? a : JSON.stringify(a))).join(" "));
150
- };
151
- try {
152
- const result = await fn();
153
- return { result, lines };
154
- }
155
- finally {
156
- console.warn = originalWarn;
157
- }
158
- }
159
- test("default verbosity emits one summary line, not per-spec workflow warnings", async () => {
160
- const stashDir = tmpStash();
161
- // Two broken workflows so we can prove the summary line is emitted instead
162
- // of two separate per-spec warnings on stderr.
163
- writeWorkflow(stashDir, "bad1", BROKEN_WORKFLOW);
164
- writeWorkflow(stashDir, "bad2", BROKEN_WORKFLOW);
165
- const { lines } = await captureStderr(() => akmIndex({ stashDir, full: true }));
166
- const perSpec = lines.filter((l) => l.startsWith("Skipped workflow "));
167
- expect(perSpec).toHaveLength(0);
168
- const summary = lines.filter((l) => l.includes("workflow specs skipped due to validation errors"));
169
- expect(summary).toHaveLength(1);
170
- expect(summary[0]).toMatch(/^2 workflow specs skipped/);
171
- expect(summary[0]).toContain("--verbose");
172
- expect(summary[0]).toContain("AKM_VERBOSE");
173
- });
174
- test("default verbosity uses singular 'workflow spec' when only one was skipped", async () => {
175
- const stashDir = tmpStash();
176
- writeWorkflow(stashDir, "bad", BROKEN_WORKFLOW);
177
- const { lines } = await captureStderr(() => akmIndex({ stashDir, full: true }));
178
- const summary = lines.filter((l) => l.includes("workflow spec skipped"));
179
- expect(summary).toHaveLength(1);
180
- expect(summary[0]).toMatch(/^1 workflow spec skipped/);
181
- });
182
- test("--verbose flag restores per-spec workflow warnings and suppresses the summary", async () => {
183
- const stashDir = tmpStash();
184
- writeWorkflow(stashDir, "bad1", BROKEN_WORKFLOW);
185
- writeWorkflow(stashDir, "bad2", BROKEN_WORKFLOW);
186
- setVerbose(true);
187
- const { lines } = await captureStderr(() => akmIndex({ stashDir, full: true }));
188
- const perSpec = lines.filter((l) => l.startsWith("Skipped workflow "));
189
- expect(perSpec).toHaveLength(2);
190
- const summary = lines.filter((l) => l.includes("workflow specs skipped due to validation errors"));
191
- expect(summary).toHaveLength(0);
192
- });
193
- test("AKM_VERBOSE=1 restores per-spec output even with the verbose flag unset", async () => {
194
- const stashDir = tmpStash();
195
- writeWorkflow(stashDir, "bad", BROKEN_WORKFLOW);
196
- process.env.AKM_VERBOSE = "1";
197
- const { lines } = await captureStderr(() => akmIndex({ stashDir, full: true }));
198
- const perSpec = lines.filter((l) => l.startsWith("Skipped workflow "));
199
- expect(perSpec).toHaveLength(1);
200
- const summary = lines.filter((l) => l.includes("workflow spec skipped"));
201
- expect(summary).toHaveLength(0);
202
- });
203
- test("AKM_VERBOSE=0 hard-disables verbose output even when --verbose flag was set", async () => {
204
- const stashDir = tmpStash();
205
- writeWorkflow(stashDir, "bad", BROKEN_WORKFLOW);
206
- setVerbose(true);
207
- process.env.AKM_VERBOSE = "0";
208
- const { lines } = await captureStderr(() => akmIndex({ stashDir, full: true }));
209
- const perSpec = lines.filter((l) => l.startsWith("Skipped workflow "));
210
- expect(perSpec).toHaveLength(0);
211
- const summary = lines.filter((l) => l.includes("workflow spec skipped"));
212
- expect(summary).toHaveLength(1);
213
- });
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes