@vibe-agent-toolkit/utils 0.2.0-rc.1 → 0.2.0-rc.11

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 (270) hide show
  1. package/README.md +69 -23
  2. package/dist/asset-reference.js +3 -11
  3. package/dist/asset-reference.js.map +1 -1
  4. package/dist/compare-code-units.d.ts +28 -0
  5. package/dist/compare-code-units.d.ts.map +1 -0
  6. package/dist/compare-code-units.js +33 -0
  7. package/dist/compare-code-units.js.map +1 -0
  8. package/dist/crawl-timing.d.ts +657 -0
  9. package/dist/crawl-timing.d.ts.map +1 -0
  10. package/dist/crawl-timing.js +765 -0
  11. package/dist/crawl-timing.js.map +1 -0
  12. package/dist/crawl.d.ts +2 -4
  13. package/dist/crawl.d.ts.map +1 -1
  14. package/dist/crawl.js +2 -4
  15. package/dist/crawl.js.map +1 -1
  16. package/dist/dirent-kind.d.ts +83 -0
  17. package/dist/dirent-kind.d.ts.map +1 -0
  18. package/dist/dirent-kind.js +129 -0
  19. package/dist/dirent-kind.js.map +1 -0
  20. package/dist/entrypoint.d.ts +54 -0
  21. package/dist/entrypoint.d.ts.map +1 -0
  22. package/dist/entrypoint.js +71 -0
  23. package/dist/entrypoint.js.map +1 -0
  24. package/dist/env-flag.d.ts +57 -0
  25. package/dist/env-flag.d.ts.map +1 -0
  26. package/dist/env-flag.js +68 -0
  27. package/dist/env-flag.js.map +1 -0
  28. package/dist/errors/errno.d.ts +51 -0
  29. package/dist/errors/errno.d.ts.map +1 -0
  30. package/dist/errors/errno.js +112 -0
  31. package/dist/errors/errno.js.map +1 -0
  32. package/dist/errors/vat-error.d.ts +60 -0
  33. package/dist/errors/vat-error.d.ts.map +1 -0
  34. package/dist/errors/vat-error.js +86 -0
  35. package/dist/errors/vat-error.js.map +1 -0
  36. package/dist/file-crawler.d.ts +60 -0
  37. package/dist/file-crawler.d.ts.map +1 -1
  38. package/dist/file-crawler.js +133 -29
  39. package/dist/file-crawler.js.map +1 -1
  40. package/dist/fs/file-hash.d.ts.map +1 -1
  41. package/dist/fs/file-hash.js +0 -1
  42. package/dist/fs/file-hash.js.map +1 -1
  43. package/dist/fs-utils.d.ts +422 -191
  44. package/dist/fs-utils.d.ts.map +1 -1
  45. package/dist/fs-utils.js +534 -204
  46. package/dist/fs-utils.js.map +1 -1
  47. package/dist/fs.d.ts +6 -2
  48. package/dist/fs.d.ts.map +1 -1
  49. package/dist/fs.js +10 -7
  50. package/dist/fs.js.map +1 -1
  51. package/dist/git-run.d.ts +167 -0
  52. package/dist/git-run.d.ts.map +1 -0
  53. package/dist/git-run.js +154 -0
  54. package/dist/git-run.js.map +1 -0
  55. package/dist/git-snapshot.d.ts +199 -0
  56. package/dist/git-snapshot.d.ts.map +1 -0
  57. package/dist/git-snapshot.js +250 -0
  58. package/dist/git-snapshot.js.map +1 -0
  59. package/dist/git-tracker.d.ts +166 -3
  60. package/dist/git-tracker.d.ts.map +1 -1
  61. package/dist/git-tracker.js +250 -22
  62. package/dist/git-tracker.js.map +1 -1
  63. package/dist/git-utils.d.ts +84 -1
  64. package/dist/git-utils.d.ts.map +1 -1
  65. package/dist/git-utils.js +208 -82
  66. package/dist/git-utils.js.map +1 -1
  67. package/dist/git.d.ts +13 -4
  68. package/dist/git.d.ts.map +1 -1
  69. package/dist/git.js +12 -4
  70. package/dist/git.js.map +1 -1
  71. package/dist/gitignore-checker.d.ts.map +1 -1
  72. package/dist/gitignore-checker.js +15 -6
  73. package/dist/gitignore-checker.js.map +1 -1
  74. package/dist/glob/glob-pattern.js +5 -5
  75. package/dist/glob/glob-pattern.js.map +1 -1
  76. package/dist/index.d.ts +32 -20
  77. package/dist/index.d.ts.map +1 -1
  78. package/dist/index.js +95 -50
  79. package/dist/index.js.map +1 -1
  80. package/dist/listing-refusal.d.ts +112 -0
  81. package/dist/listing-refusal.d.ts.map +1 -0
  82. package/dist/listing-refusal.js +105 -0
  83. package/dist/listing-refusal.js.map +1 -0
  84. package/dist/path-containment.d.ts +79 -0
  85. package/dist/path-containment.d.ts.map +1 -0
  86. package/dist/path-containment.js +128 -0
  87. package/dist/path-containment.js.map +1 -0
  88. package/dist/path-core.d.ts +113 -18
  89. package/dist/path-core.d.ts.map +1 -1
  90. package/dist/path-core.js +139 -23
  91. package/dist/path-core.js.map +1 -1
  92. package/dist/path-utils.d.ts.map +1 -1
  93. package/dist/path-utils.js +42 -47
  94. package/dist/path-utils.js.map +1 -1
  95. package/dist/path.d.ts +1 -1
  96. package/dist/path.d.ts.map +1 -1
  97. package/dist/path.js +1 -1
  98. package/dist/path.js.map +1 -1
  99. package/dist/process.d.ts +7 -0
  100. package/dist/process.d.ts.map +1 -1
  101. package/dist/process.js +7 -0
  102. package/dist/process.js.map +1 -1
  103. package/dist/project-utils.d.ts.map +1 -1
  104. package/dist/project-utils.js +15 -9
  105. package/dist/project-utils.js.map +1 -1
  106. package/dist/project.d.ts +7 -5
  107. package/dist/project.d.ts.map +1 -1
  108. package/dist/project.js +7 -5
  109. package/dist/project.js.map +1 -1
  110. package/dist/safe-exec.d.ts +40 -11
  111. package/dist/safe-exec.d.ts.map +1 -1
  112. package/dist/safe-exec.js +111 -25
  113. package/dist/safe-exec.js.map +1 -1
  114. package/dist/skill-targets.d.ts +2 -2
  115. package/dist/skill-targets.js +2 -2
  116. package/dist/skill-test/auth-resolver.d.ts +10 -2
  117. package/dist/skill-test/auth-resolver.d.ts.map +1 -1
  118. package/dist/skill-test/auth-resolver.js +26 -11
  119. package/dist/skill-test/auth-resolver.js.map +1 -1
  120. package/dist/skill-test/index.d.ts +1 -1
  121. package/dist/skill-test/index.d.ts.map +1 -1
  122. package/dist/skill-test/index.js +1 -1
  123. package/dist/skill-test/index.js.map +1 -1
  124. package/dist/skill-test/spawn-claude.d.ts +30 -1
  125. package/dist/skill-test/spawn-claude.d.ts.map +1 -1
  126. package/dist/skill-test/spawn-claude.js +71 -5
  127. package/dist/skill-test/spawn-claude.js.map +1 -1
  128. package/dist/skill-test/transcript.d.ts +41 -2
  129. package/dist/skill-test/transcript.d.ts.map +1 -1
  130. package/dist/skill-test/transcript.js +55 -10
  131. package/dist/skill-test/transcript.js.map +1 -1
  132. package/dist/spawn-hardened.js +0 -1
  133. package/dist/spawn-hardened.js.map +1 -1
  134. package/dist/stdio-blocking.js +13 -14
  135. package/dist/stdio-blocking.js.map +1 -1
  136. package/dist/test-helpers.d.ts +135 -100
  137. package/dist/test-helpers.d.ts.map +1 -1
  138. package/dist/test-helpers.js +224 -146
  139. package/dist/test-helpers.js.map +1 -1
  140. package/dist/testing/executables.d.ts +32 -0
  141. package/dist/testing/executables.d.ts.map +1 -0
  142. package/dist/testing/executables.js +70 -0
  143. package/dist/testing/executables.js.map +1 -0
  144. package/dist/testing/hostile-tree.d.ts +79 -0
  145. package/dist/testing/hostile-tree.d.ts.map +1 -0
  146. package/dist/testing/hostile-tree.js +164 -0
  147. package/dist/testing/hostile-tree.js.map +1 -0
  148. package/dist/testing/platform-gates.d.ts +31 -0
  149. package/dist/testing/platform-gates.d.ts.map +1 -0
  150. package/dist/testing/platform-gates.js +31 -0
  151. package/dist/testing/platform-gates.js.map +1 -0
  152. package/dist/testing/temp-dir.d.ts +255 -0
  153. package/dist/testing/temp-dir.d.ts.map +1 -0
  154. package/dist/testing/temp-dir.js +363 -0
  155. package/dist/testing/temp-dir.js.map +1 -0
  156. package/dist/testing.d.ts +94 -3
  157. package/dist/testing.d.ts.map +1 -1
  158. package/dist/testing.js +119 -3
  159. package/dist/testing.js.map +1 -1
  160. package/dist/text-content.d.ts +226 -0
  161. package/dist/text-content.d.ts.map +1 -0
  162. package/dist/text-content.js +418 -0
  163. package/dist/text-content.js.map +1 -0
  164. package/dist/text-file.d.ts +84 -0
  165. package/dist/text-file.d.ts.map +1 -0
  166. package/dist/text-file.js +118 -0
  167. package/dist/text-file.js.map +1 -0
  168. package/dist/text.d.ts +16 -0
  169. package/dist/text.d.ts.map +1 -0
  170. package/dist/text.js +16 -0
  171. package/dist/text.js.map +1 -0
  172. package/dist/timing-dump.d.ts +136 -0
  173. package/dist/timing-dump.d.ts.map +1 -0
  174. package/dist/timing-dump.js +229 -0
  175. package/dist/timing-dump.js.map +1 -0
  176. package/dist/windows-shell.d.ts +0 -1
  177. package/dist/windows-shell.d.ts.map +1 -1
  178. package/dist/windows-shell.js +0 -1
  179. package/dist/windows-shell.js.map +1 -1
  180. package/dist/zod-introspection.js +2 -2
  181. package/dist/zod-introspection.js.map +1 -1
  182. package/eslint/README.md +223 -49
  183. package/eslint/index.cjs +111 -97
  184. package/eslint/index.d.cts +16 -6
  185. package/eslint/rules/callee-name.cjs +23 -0
  186. package/eslint/rules/commands-import-boundary.cjs +148 -0
  187. package/eslint/rules/dead-import.cjs +1 -1
  188. package/eslint/rules/dirent-type-needs-symlink-check.cjs +282 -0
  189. package/eslint/rules/eslint-rule-factory.cjs +5 -2
  190. package/eslint/rules/exempt-path-matcher.cjs +2 -1
  191. package/eslint/rules/explicit-zod-strictness.cjs +120 -0
  192. package/eslint/rules/no-bare-dynamic-import-path.cjs +5 -1
  193. package/eslint/rules/no-bare-executable-spawn.cjs +70 -0
  194. package/eslint/rules/no-bare-symlink-in-tests.cjs +213 -0
  195. package/eslint/rules/no-blind-catch.cjs +171 -0
  196. package/eslint/rules/no-child-process-execSync.cjs +8 -0
  197. package/eslint/rules/no-decaying-referent.cjs +217 -0
  198. package/eslint/rules/no-dotdot-containment.cjs +160 -0
  199. package/eslint/rules/no-file-url-string-concat.cjs +4 -1
  200. package/eslint/rules/no-fragile-entrypoint-guard.cjs +228 -0
  201. package/eslint/rules/no-fs-mkdirSync.cjs +8 -0
  202. package/eslint/rules/no-fs-promises-cp.cjs +7 -0
  203. package/eslint/rules/no-fs-realpathSync.cjs +8 -0
  204. package/eslint/rules/no-hardcoded-path-split.cjs +29 -18
  205. package/eslint/rules/no-io-in-unit-tier.cjs +129 -0
  206. package/eslint/rules/no-literal-process-exit.cjs +230 -0
  207. package/eslint/rules/no-manual-path-normalize.cjs +136 -107
  208. package/eslint/rules/no-os-tmpdir.cjs +8 -0
  209. package/eslint/rules/no-path-operations-in-comparisons.cjs +7 -2
  210. package/eslint/rules/no-path-sep-in-strings.cjs +7 -2
  211. package/eslint/rules/no-path-startswith.cjs +7 -2
  212. package/eslint/rules/no-process-exit-in-phase.cjs +127 -0
  213. package/eslint/rules/no-raw-node-path.cjs +563 -0
  214. package/eslint/rules/no-raw-text-decode.cjs +305 -0
  215. package/eslint/rules/no-registry-count-pin.cjs +255 -0
  216. package/eslint/rules/no-self-package-import.cjs +168 -0
  217. package/eslint/rules/no-test-scoped-functions.cjs +10 -3
  218. package/eslint/rules/no-unix-shell-commands.cjs +6 -2
  219. package/eslint/rules/no-unsafe-root-join.cjs +23 -2
  220. package/eslint/rules/no-url-pathname-for-fs.cjs +5 -1
  221. package/eslint/rules/no-version-literal.cjs +151 -0
  222. package/eslint/rules/prefer-startswith-over-regex.cjs +9 -0
  223. package/eslint/rules/require-justified-skip.cjs +8 -2
  224. package/eslint/rules/safe-import.cjs +1 -1
  225. package/package.json +16 -16
  226. package/dist/link-auth/build-headers.d.ts +0 -34
  227. package/dist/link-auth/build-headers.d.ts.map +0 -1
  228. package/dist/link-auth/build-headers.js +0 -58
  229. package/dist/link-auth/build-headers.js.map +0 -1
  230. package/dist/link-auth/expand-macro.d.ts +0 -38
  231. package/dist/link-auth/expand-macro.d.ts.map +0 -1
  232. package/dist/link-auth/expand-macro.js +0 -133
  233. package/dist/link-auth/expand-macro.js.map +0 -1
  234. package/dist/link-auth/macros.yaml +0 -50
  235. package/dist/link-auth/resolve-token.d.ts +0 -83
  236. package/dist/link-auth/resolve-token.d.ts.map +0 -1
  237. package/dist/link-auth/resolve-token.js +0 -101
  238. package/dist/link-auth/resolve-token.js.map +0 -1
  239. package/dist/link-auth/resolve.d.ts +0 -102
  240. package/dist/link-auth/resolve.d.ts.map +0 -1
  241. package/dist/link-auth/resolve.js +0 -66
  242. package/dist/link-auth/resolve.js.map +0 -1
  243. package/dist/link-auth/rewrite.d.ts +0 -52
  244. package/dist/link-auth/rewrite.d.ts.map +0 -1
  245. package/dist/link-auth/rewrite.js +0 -102
  246. package/dist/link-auth/rewrite.js.map +0 -1
  247. package/dist/link-auth/select-provider.d.ts +0 -30
  248. package/dist/link-auth/select-provider.d.ts.map +0 -1
  249. package/dist/link-auth/select-provider.js +0 -55
  250. package/dist/link-auth/select-provider.js.map +0 -1
  251. package/dist/link-auth/template.d.ts +0 -40
  252. package/dist/link-auth/template.d.ts.map +0 -1
  253. package/dist/link-auth/template.js +0 -89
  254. package/dist/link-auth/template.js.map +0 -1
  255. package/dist/link-auth/transforms.d.ts +0 -46
  256. package/dist/link-auth/transforms.d.ts.map +0 -1
  257. package/dist/link-auth/transforms.js +0 -52
  258. package/dist/link-auth/transforms.js.map +0 -1
  259. package/dist/template-entry.d.ts +0 -10
  260. package/dist/template-entry.d.ts.map +0 -1
  261. package/dist/template-entry.js +0 -10
  262. package/dist/template-entry.js.map +0 -1
  263. package/dist/template.d.ts +0 -7
  264. package/dist/template.d.ts.map +0 -1
  265. package/dist/template.js +0 -18
  266. package/dist/template.js.map +0 -1
  267. package/eslint/rules/no-path-join.cjs +0 -14
  268. package/eslint/rules/no-path-relative.cjs +0 -14
  269. package/eslint/rules/no-path-resolve.cjs +0 -14
  270. package/eslint/rules/path-function-rule-factory.cjs +0 -496
@@ -5,7 +5,10 @@
5
5
  *
6
6
  * The stream-json schema is informally documented and may shift between
7
7
  * Claude Code releases. This parser is intentionally tolerant of unknown
8
- * event kinds — anything we don't recognise is appended to `raw`.
8
+ * event kinds — an event whose `type` we do not recognise is simply not
9
+ * consumed. Tolerance stops at unknown SHAPES: a line that is not JSON at all is
10
+ * counted into {@link ParsedTranscript.malformedLineCount}, because that is data
11
+ * loss, not forward compatibility.
9
12
  *
10
13
  * Shape notes (empirically verified):
11
14
  * - `tool_use` content blocks carry `input.command` for Bash calls.
@@ -23,11 +26,40 @@ export interface ParsedTranscript {
23
26
  inputSummary: string;
24
27
  }>;
25
28
  errors: string[];
26
- raw: string[];
29
+ /**
30
+ * How many non-empty lines failed `JSON.parse` and were therefore NOT consumed.
31
+ *
32
+ * This is the only thing that distinguishes "parsed fine, nothing found" from
33
+ * "we lost the evidence". A corrupted line drops its whole event — the tool call
34
+ * in it, and any contamination it proves — while every other line, including the
35
+ * terminal `result`, still parses. Every downstream "did this transcript decode?"
36
+ * test is an any-of over the populated fields, so the surviving `result` line on
37
+ * its own certifies the transcript as decoded and the loss goes unremarked.
38
+ *
39
+ * Non-zero means the scan that consumed this transcript ran on less than the
40
+ * whole transcript and its verdict must be reported as DEGRADED, never clean.
41
+ * Zero is a positive statement, not an absence: every line was accounted for.
42
+ */
43
+ malformedLineCount: number;
27
44
  toolUses: Array<{
28
45
  id: string | null;
29
46
  name: string;
30
47
  command?: string;
48
+ /**
49
+ * The FULL, unsummarized tool input.
50
+ *
51
+ * `inputSummary` beside it is truncated to 200 characters by
52
+ * {@link summarizeValue}, which is fine for an observation record a human
53
+ * skims and useless for anything that has to REASON about what the call did
54
+ * — a path 200 characters into a `Read` input simply is not there. The
55
+ * baseline-integrity detector walks tool inputs to decide whether the
56
+ * skill-absent arm reached the skill, so it needs the whole value; the
57
+ * summary stays for the consumers that only ever display it.
58
+ *
59
+ * `unknown` because the shape is per-tool and informally documented — every
60
+ * consumer narrows it itself rather than trusting a type we invented.
61
+ */
62
+ input: unknown;
31
63
  inputSummary: string;
32
64
  parentToolUseId: string | null;
33
65
  }>;
@@ -35,6 +67,13 @@ export interface ParsedTranscript {
35
67
  toolUseId: string | null;
36
68
  isError: boolean;
37
69
  contentSummary: string;
70
+ /**
71
+ * The FULL tool result, flattened to text. Same reason as `input` above:
72
+ * `contentSummary` is truncated to 200 characters, so scanning a tool RESULT
73
+ * for evidence (a file's contents echoed back, a grep hit line) cannot be
74
+ * done through it. Never truncated — the caller bounds what it keeps.
75
+ */
76
+ content: string;
38
77
  }>;
39
78
  result?: {
40
79
  subtype?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"transcript.d.ts","sourceRoot":"","sources":["../../src/skill-test/transcript.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7D,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,QAAQ,EAAE,KAAK,CAAC;QACd,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;QAClB,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;QACrB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;KAChC,CAAC,CAAC;IACH,WAAW,EAAE,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC3F,MAAM,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1F,WAAW,EAAE,OAAO,CAAC;CACtB;AA4HD,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB,CA4B9E;AAED,wBAAgB,8BAA8B,CAC5C,MAAM,EAAE,gBAAgB,EACxB,iBAAiB,EAAE,SAAS,MAAM,EAAE,GACnC,OAAO,CAOT"}
1
+ {"version":3,"file":"transcript.d.ts","sourceRoot":"","sources":["../../src/skill-test/transcript.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7D,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB;;;;;;;;;;;;;OAaG;IACH,kBAAkB,EAAE,MAAM,CAAC;IAC3B,QAAQ,EAAE,KAAK,CAAC;QACd,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;QAClB,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB;;;;;;;;;;;;;WAaG;QACH,KAAK,EAAE,OAAO,CAAC;QACf,YAAY,EAAE,MAAM,CAAC;QACrB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;KAChC,CAAC,CAAC;IACH,WAAW,EAAE,KAAK,CAAC;QACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;QACzB,OAAO,EAAE,OAAO,CAAC;QACjB,cAAc,EAAE,MAAM,CAAC;QACvB;;;;;WAKG;QACH,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC,CAAC;IACH,MAAM,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1F,WAAW,EAAE,OAAO,CAAC;CACtB;AA+JD,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB,CAiC9E;AAED,wBAAgB,8BAA8B,CAC5C,MAAM,EAAE,gBAAgB,EACxB,iBAAiB,EAAE,SAAS,MAAM,EAAE,GACnC,OAAO,CAOT"}
@@ -5,7 +5,10 @@
5
5
  *
6
6
  * The stream-json schema is informally documented and may shift between
7
7
  * Claude Code releases. This parser is intentionally tolerant of unknown
8
- * event kinds — anything we don't recognise is appended to `raw`.
8
+ * event kinds — an event whose `type` we do not recognise is simply not
9
+ * consumed. Tolerance stops at unknown SHAPES: a line that is not JSON at all is
10
+ * counted into {@link ParsedTranscript.malformedLineCount}, because that is data
11
+ * loss, not forward compatibility.
9
12
  *
10
13
  * Shape notes (empirically verified):
11
14
  * - `tool_use` content blocks carry `input.command` for Bash calls.
@@ -16,14 +19,48 @@
16
19
  * - The terminal `result` event carries `subtype`, `is_error`, `num_turns`,
17
20
  * and `total_cost_usd`.
18
21
  */
22
+ /**
23
+ * Every value these two see is a fragment of a `JSON.parse` result — plain
24
+ * data, no cycles, no BigInt, no `toJSON` — so `JSON.stringify` cannot fail on
25
+ * it, and neither wraps it in a catch. The one thing it CAN answer with is
26
+ * `undefined`, for an absent field (a `tool_use` with no `input`); that used
27
+ * to trip a `.length` inside a blind catch and surface as `<unserializable>`,
28
+ * a parser accident reported as a property of the event.
29
+ */
19
30
  function summarizeValue(value) {
20
- try {
21
- const json = JSON.stringify(value);
22
- return json.length > 200 ? `${json.slice(0, 197)}...` : json;
23
- }
24
- catch {
25
- return '<unserializable>';
31
+ const json = stringifyValue(value);
32
+ return json.length > 200 ? `${json.slice(0, 197)}...` : json;
33
+ }
34
+ function stringifyValue(value) {
35
+ return JSON.stringify(value) ?? '';
36
+ }
37
+ /**
38
+ * Flatten a `tool_result` `content` field to plain text, WITHOUT truncating.
39
+ *
40
+ * Three shapes occur in practice: a bare string (most Bash results), an array of
41
+ * content blocks (`{type:'text',text}` and friends), and — for a tool that
42
+ * returns structured data — an arbitrary object. The array case is the one that
43
+ * matters: `JSON.stringify` of it would bury the text behind escaped quotes, and
44
+ * a consumer scanning for a verbatim sentence would never find it.
45
+ */
46
+ function contentToText(value) {
47
+ if (typeof value === 'string')
48
+ return value;
49
+ if (Array.isArray(value))
50
+ return value.map((block) => contentBlockToText(block)).join('\n');
51
+ if (value === undefined || value === null)
52
+ return '';
53
+ return stringifyValue(value);
54
+ }
55
+ function contentBlockToText(block) {
56
+ if (typeof block === 'string')
57
+ return block;
58
+ if (block !== null && typeof block === 'object') {
59
+ const { text } = block;
60
+ if (typeof text === 'string')
61
+ return text;
26
62
  }
63
+ return stringifyValue(block);
27
64
  }
28
65
  function extractCommand(input) {
29
66
  if (input !== null && typeof input === 'object' && typeof input.command === 'string') {
@@ -44,6 +81,7 @@ function consumeAssistantBlocks(blocks, parentToolUseId, out) {
44
81
  id: typeof block.id === 'string' ? block.id : null,
45
82
  name: block.name,
46
83
  ...(command === undefined ? {} : { command }),
84
+ input: block.input,
47
85
  inputSummary,
48
86
  parentToolUseId,
49
87
  });
@@ -57,6 +95,7 @@ function consumeUserBlocks(blocks, out) {
57
95
  toolUseId: typeof block.tool_use_id === 'string' ? block.tool_use_id : null,
58
96
  isError: block.is_error === true,
59
97
  contentSummary: summarizeValue(block.content),
98
+ content: contentToText(block.content),
60
99
  });
61
100
  }
62
101
  }
@@ -114,22 +153,28 @@ export function parseStreamJsonTranscript(streamText) {
114
153
  text: '',
115
154
  toolUseEvents: [],
116
155
  errors: [],
117
- raw: [],
118
156
  toolUses: [],
119
157
  toolResults: [],
120
158
  rateLimited: false,
159
+ malformedLineCount: 0,
121
160
  };
122
161
  const lines = streamText.split('\n');
123
162
  for (const line of lines) {
124
163
  const trimmed = line.trim();
125
164
  if (trimmed.length === 0)
126
165
  continue;
127
- out.raw.push(trimmed);
128
166
  let parsed;
129
167
  try {
130
168
  parsed = JSON.parse(trimmed);
131
169
  }
132
- catch {
170
+ catch (error) {
171
+ // A line that is not JSON is counted, not thrown on, and never pushed
172
+ // into `errors`: `errors` reports what the AGENT hit, this reports what
173
+ // the PARSER lost. Conflating them would make a harness defect look like
174
+ // a run failure and vice versa. Only a parse failure is a lost line.
175
+ if (!(error instanceof SyntaxError))
176
+ throw error;
177
+ out.malformedLineCount += 1;
133
178
  continue;
134
179
  }
135
180
  applyEvent(parsed, out);
@@ -1 +1 @@
1
- {"version":3,"file":"transcript.js","sourceRoot":"","sources":["../../src/skill-test/transcript.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"transcript.js","sourceRoot":"","sources":["../../src/skill-test/transcript.ts"],"names":[],"mappings":"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"}
@@ -28,7 +28,6 @@ export function spawnHardened(command, args, options = {}) {
28
28
  // Windows shell path (.cmd/.bat/.ps1). Token selection is shared with the sync
29
29
  // `safeExecSync` path so the two cannot drift — see `resolveShellCommandToken`.
30
30
  const shellLine = buildWindowsShellLine(resolveShellCommandToken(command, resolved), args);
31
- // eslint-disable-next-line sonarjs/os-command -- Windows DEP0190 workaround: command is a PATH-resolved bare name or an explicit path; args are per-arg shell-quoted via windowsShellQuote()
32
31
  return spawn(shellLine, { ...options, shell: true });
33
32
  }
34
33
  //# sourceMappingURL=spawn-hardened.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"spawn-hardened.js","sourceRoot":"","sources":["../src/spawn-hardened.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAwC,MAAM,oBAAoB,CAAC;AAEjF,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EACL,qBAAqB,EACrB,UAAU,EACV,wBAAwB,EACxB,cAAc,GACf,MAAM,oBAAoB,CAAC;AAE5B;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,aAAa,CAC3B,OAAe,EACf,IAAc,EACd,UAAwB,EAAE;IAE1B,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAErE,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IAC7D,CAAC;IAED,+EAA+E;IAC/E,gFAAgF;IAChF,MAAM,SAAS,GAAG,qBAAqB,CAAC,wBAAwB,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC;IAC3F,6LAA6L;IAC7L,OAAO,KAAK,CAAC,SAAS,EAAE,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AACvD,CAAC"}
1
+ {"version":3,"file":"spawn-hardened.js","sourceRoot":"","sources":["../src/spawn-hardened.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAwC,MAAM,oBAAoB,CAAC;AAEjF,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EACL,qBAAqB,EACrB,UAAU,EACV,wBAAwB,EACxB,cAAc,GACf,MAAM,oBAAoB,CAAC;AAE5B;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,aAAa,CAC3B,OAAe,EACf,IAAc,EACd,UAAwB,EAAE;IAE1B,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAErE,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IAC7D,CAAC;IAED,+EAA+E;IAC/E,gFAAgF;IAChF,MAAM,SAAS,GAAG,qBAAqB,CAAC,wBAAwB,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC;IAE3F,OAAO,KAAK,CAAC,SAAS,EAAE,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AACvD,CAAC"}
@@ -10,23 +10,22 @@
10
10
  /**
11
11
  * Switch one stream's underlying handle into blocking mode.
12
12
  *
13
- * @returns `true` only if `setBlocking` was present AND completed without
14
- * throwing. Both failure modes are real: a handle can be absent (stdio replaced,
15
- * or a shape libuv does not wrap), and `setBlocking` can throw on a handle type
16
- * that does not support it.
13
+ * @returns `true` only if `setBlocking` was present AND reported success. Both
14
+ * failure modes are real: a handle can be absent (stdio replaced, or a shape
15
+ * libuv does not wrap), and libuv can refuse the switch for a handle type that
16
+ * does not support it. It reports that refusal as a negative errno RETURN VALUE
17
+ * — `LibuvStreamWrap::SetBlocking` hands back `uv_stream_set_blocking`'s int,
18
+ * and a Windows TTY answers `UV_EINVAL` — never as a throw. This used to sit in
19
+ * a `try` that read a throw as the failure and the return value as nothing, so
20
+ * a refused switch reported `true` and the debug line said truncation could
21
+ * not happen on a stream it could happen on. A throw from in here would be a
22
+ * bug in Node's internals, and stays loud.
17
23
  */
18
24
  function setStreamBlocking(stream) {
19
- try {
20
- const handle = stream._handle;
21
- if (typeof handle?.setBlocking !== 'function')
22
- return false;
23
- handle.setBlocking(true);
24
- return true;
25
- }
26
- catch {
27
- // Best-effort: never let a startup nicety take down the process.
25
+ const handle = stream._handle;
26
+ if (typeof handle?.setBlocking !== 'function')
28
27
  return false;
29
- }
28
+ return handle.setBlocking(true) === 0;
30
29
  }
31
30
  /**
32
31
  * Make stdout/stderr BLOCKING, so that `process.exit()` can never discard output.
@@ -1 +1 @@
1
- {"version":3,"file":"stdio-blocking.js","sourceRoot":"","sources":["../src/stdio-blocking.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAQH;;;;;;;GAOG;AACH,SAAS,iBAAiB,CAAC,MAA0B;IACpD,IAAI,CAAC;QACJ,MAAM,MAAM,GACX,MACA,CAAC,OAAO,CAAC;QACV,IAAI,OAAO,MAAM,EAAE,WAAW,KAAK,UAAU;YAAE,OAAO,KAAK,CAAC;QAC5D,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACzB,OAAO,IAAI,CAAC;IACb,CAAC;IAAC,MAAM,CAAC;QACR,iEAAiE;QACjE,OAAO,KAAK,CAAC;IACd,CAAC;AACF,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,UAAU,iBAAiB;IAChC,OAAO;QACN,MAAM,EAAE,iBAAiB,CAAC,OAAO,CAAC,MAAM,CAAC;QACzC,MAAM,EAAE,iBAAiB,CAAC,OAAO,CAAC,MAAM,CAAC;KACzC,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAA2B;IAChE,MAAM,MAAM,GAAI,CAAC,QAAQ,EAAE,QAAQ,CAAW,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IAC/E,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,4EAA4E,CAAC;IACrF,CAAC;IACD,OAAO,yBAAyB,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,yBAAyB,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,eAAe,yDAAyD,CAAC;AAC7L,CAAC"}
1
+ {"version":3,"file":"stdio-blocking.js","sourceRoot":"","sources":["../src/stdio-blocking.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAQH;;;;;;;;;;;;;GAaG;AACH,SAAS,iBAAiB,CAAC,MAA0B;IACpD,MAAM,MAAM,GACX,MACA,CAAC,OAAO,CAAC;IACV,IAAI,OAAO,MAAM,EAAE,WAAW,KAAK,UAAU;QAAE,OAAO,KAAK,CAAC;IAC5D,OAAO,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACvC,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,UAAU,iBAAiB;IAChC,OAAO;QACN,MAAM,EAAE,iBAAiB,CAAC,OAAO,CAAC,MAAM,CAAC;QACzC,MAAM,EAAE,iBAAiB,CAAC,OAAO,CAAC,MAAM,CAAC;KACzC,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAA2B;IAChE,MAAM,MAAM,GAAI,CAAC,QAAQ,EAAE,QAAQ,CAAW,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IAC/E,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,4EAA4E,CAAC;IACrF,CAAC;IACD,OAAO,yBAAyB,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,yBAAyB,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,eAAe,yDAAyD,CAAC;AAC7L,CAAC"}
@@ -1,118 +1,153 @@
1
+ declare const symlinkCapabilityBrand: unique symbol;
1
2
  /**
2
- * Get isolated test output directory for current test run
3
- *
4
- * Creates a unique directory under `packages/{packageName}/.test-output/{testType}/{runId}`
5
- * where runId is `{timestamp}-{randomId}` to ensure isolation across parallel test runs.
6
- *
7
- * @param packageName - Name of package (e.g., 'rag-lancedb')
8
- * @param testType - Type of test ('unit', 'integration', 'system')
9
- * @param subdirs - Optional subdirectories to create within the test output directory
10
- * @returns Absolute path to the created directory
11
- *
12
- * @example
13
- * ```typescript
14
- * // Create isolated database directory for system tests
15
- * const dbPath = getTestOutputDir('rag-lancedb', 'system', 'databases', 'test-db');
16
- * // Result: packages/rag-lancedb/.test-output/system/20260105-143022-abc123/databases/test-db
17
- *
18
- * // Create temporary file directory for integration tests
19
- * const tempDir = getTestOutputDir('agent-skills', 'integration', 'temp-files');
20
- * // Result: packages/agent-skills/.test-output/integration/20260105-143022-def456/temp-files
21
- * ```
3
+ * Proof that this process can create filesystem symlinks.
4
+ *
5
+ * The only way to obtain one is {@link symlinkCapability}, and it exists at
6
+ * all only when a real probe already succeeded — so a function that requires
7
+ * this as a parameter cannot be reached by code that skipped the check. That
8
+ * is the point of branding it rather than passing a `boolean`: forgetting the
9
+ * check becomes a type error instead of a runtime `EPERM` on a machine you
10
+ * don't control.
22
11
  */
23
- export declare function getTestOutputDir(packageName: string, testType: 'unit' | 'integration' | 'system', ...subdirs: string[]): string;
12
+ export type SymlinkCapability = {
13
+ readonly [symlinkCapabilityBrand]: true;
14
+ };
24
15
  /**
25
- * Get the base test output directory for a package
26
- * Useful for cleanup operations that need to remove all test output
16
+ * Whether this PROCESS can create symlinks — probed once and memoized.
27
17
  *
28
- * @param packageName - Name of package (e.g., 'rag-lancedb')
29
- * @returns Absolute path to packages/{packageName}/.test-output
30
- *
31
- * @example
32
- * ```typescript
33
- * const baseDir = getTestOutputBase('rag-lancedb');
34
- * // Result: packages/rag-lancedb/.test-output
35
- * ```
18
+ * On Windows, `symlink()` needs either Developer Mode or
19
+ * `SeCreateSymbolicLinkPrivilege`. That privilege lives on the process's
20
+ * security token, not on any one directory: it cannot change between calls
21
+ * within a single run, so probing it once and reusing the result is a
22
+ * memoization, not a shortcut that risks a stale answer. (A filesystem that
23
+ * itself has no symlinks — some network shares, some FAT variants — answers
24
+ * `ENOTSUP` and is read as the same "no"; every fixture in this repo creates
25
+ * its roots under {@link normalizedTmpdir}, so it never arises here.)
26
+ *
27
+ * Because the answer is memoized for the whole process, what reads as "no"
28
+ * matters more than usual: a `null` here silently `skip()`s every symlink test
29
+ * for the rest of the run. So ONLY {@link SYMLINK_UNSUPPORTED_ERRNOS} is a
30
+ * no. A tmpdir that is unwritable or missing, or a bug, is not an answer about
31
+ * symlinks at all and stays loud rather than becoming a process-wide skip for
32
+ * a reason nothing reported.
33
+ *
34
+ * Fixtures that depend on symlinks must ask rather than assume — and, having
35
+ * asked, must SAY they skipped. A symlink case that silently no-ops reads as
36
+ * a passing test for a property nobody exercised.
37
+ *
38
+ * @returns A {@link SymlinkCapability} token when this process can create
39
+ * symlinks, else `null`. Route the `null` case through vitest's `skip()`
40
+ * rather than a plain `return`, so the skip is visible in the report.
36
41
  */
37
- export declare function getTestOutputBase(packageName: string): string;
42
+ export declare function symlinkCapability(): SymlinkCapability | null;
38
43
  /**
39
- * Per-suite temp directory pattern (async version)
40
- * Creates a single temp directory for the entire test suite,
41
- * with subdirectories for each test. This is 3-5x faster on Windows
42
- * than creating a new mkdtemp for each test.
43
- *
44
- * @param prefix - Prefix for the suite temp directory name
45
- * @returns Suite helper with beforeAll, afterAll, beforeEach, afterEach, and getTempDir
46
- *
47
- * @example
48
- * ```typescript
49
- * const suite = setupAsyncTempDirSuite('my-test');
50
- *
51
- * describe('my tests', () => {
52
- * beforeAll(suite.beforeAll);
53
- * afterAll(suite.afterAll);
54
- * beforeEach(suite.beforeEach);
44
+ * Create a symlink — the one sanctioned call site for `fs.symlinkSync` in
45
+ * test code. Requires a {@link SymlinkCapability}, which only
46
+ * {@link symlinkCapability} can mint, so a test cannot reach the real
47
+ * syscall without first proving (or explicitly bypassing via `skip()`) that
48
+ * this host supports it.
49
+ *
50
+ * @param _cap - Proof from {@link symlinkCapability} that this host can create symlinks
51
+ * @param target - The existing path the new link should point at
52
+ * @param path - Where to create the link
53
+ * @param type - Windows-only link-type hint (`'file'` \| `'dir'` \| `'junction'`); ignored on POSIX
54
+ */
55
+ export declare function createSymlink(_cap: SymlinkCapability, target: string, path: string, type?: 'dir' | 'file' | 'junction'): void;
56
+ /**
57
+ * The async counterpart of {@link createSymlink}, for fixtures already using
58
+ * `node:fs/promises`. Same capability requirement, same reasoning.
55
59
  *
56
- * it('test 1', async () => {
57
- * const tempDir = suite.getTempDir();
58
- * // Use tempDir...
59
- * });
60
- * });
61
- * ```
60
+ * @param _cap - Proof from {@link symlinkCapability} that this host can create symlinks
61
+ * @param target - The existing path the new link should point at
62
+ * @param path - Where to create the link
63
+ * @param type - Windows-only link-type hint (`'file'` \| `'dir'` \| `'junction'`); ignored on POSIX
62
64
  */
63
- export declare function setupAsyncTempDirSuite(prefix: string): {
64
- beforeAll: () => Promise<void>;
65
- afterAll: () => Promise<void>;
66
- beforeEach: () => Promise<void>;
67
- afterEach: () => Promise<void>;
68
- getTempDir: () => string;
69
- };
65
+ export declare function createSymlinkAsync(_cap: SymlinkCapability, target: string, path: string, type?: 'dir' | 'file' | 'junction'): Promise<void>;
70
66
  /**
71
- * Per-suite temp directory pattern (sync version)
72
- * Creates a single temp directory for the entire test suite,
73
- * with subdirectories for each test. This is 3-5x faster on Windows
74
- * than creating a new mkdtemp for each test.
67
+ * The variables git exports into a hook, which a fixture must clear before it
68
+ * can fabricate its own.
75
69
  *
76
- * @param prefix - Prefix for the suite temp directory name
77
- * @returns Suite helper with beforeAll, afterAll, beforeEach, afterEach, and getTempDir
70
+ * These are the ones git sets *for* you. Deliberately **not** the operator's own
71
+ * `GIT_CONFIG_COUNT`/`KEY_n`/`VALUE_n`/`GLOBAL`/`SYSTEM` channel — a test may be
72
+ * using that on purpose to point a clone at a local path, and clearing it sends
73
+ * the clone to the network instead.
74
+ */
75
+ export declare const INHERITED_GIT_ENV: readonly ["GIT_ALTERNATE_OBJECT_DIRECTORIES", "GIT_CEILING_DIRECTORIES", "GIT_COMMON_DIR", "GIT_CONFIG_PARAMETERS", "GIT_DIR", "GIT_DISCOVERY_ACROSS_FILESYSTEM", "GIT_GRAFT_FILE", "GIT_INDEX_FILE", "GIT_INDEX_VERSION", "GIT_NAMESPACE", "GIT_NOTES_REF", "GIT_OBJECT_DIRECTORY", "GIT_PREFIX", "GIT_SHALLOW_FILE", "GIT_WORK_TREE"];
76
+ /**
77
+ * Remove every inherited git redirection from `process.env`, and hand back the
78
+ * undo.
79
+ *
80
+ * A test that fabricates a hook environment has to start from a known-clean one,
81
+ * or it inherits whatever the *outer* runner exported and can no longer tell its
82
+ * own fixture apart from the ambient state — it then passes or fails for reasons
83
+ * it never set up. Restoring afterwards matters just as much: these are
84
+ * process-global, so a test that leaks `GIT_DIR` silently redirects every later
85
+ * test sharing the worker.
86
+ *
87
+ * ⚠️ **The key list is restated here on purpose, not by oversight.** Deriving it
88
+ * from `@vibe-validate/git`'s `stripGitEnv()` would be tidier, and it is exactly
89
+ * what this function did for one revision — but this module is the `./testing`
90
+ * subpath, which `subpath-purity.test.ts` pins as reaching **no third-party
91
+ * package at all** so it stays importable with zero dependencies installed. One
92
+ * import cost that property. The drift risk the derivation was avoiding is
93
+ * handled instead by {@link "../test/test-helpers-git-env.test".default}, which
94
+ * asserts this list equals what the shipped scrub removes.
95
+ *
96
+ * @returns A function restoring every variable to its prior value, putting back
97
+ * "was not set" as unset rather than as an empty string
78
98
  *
79
99
  * @example
80
100
  * ```typescript
81
- * const suite = setupSyncTempDirSuite('my-test');
82
- *
83
- * describe('my tests', () => {
84
- * beforeAll(suite.beforeAll);
85
- * afterAll(suite.afterAll);
86
- * beforeEach(suite.beforeEach);
87
- *
88
- * it('test 1', () => {
89
- * const tempDir = suite.getTempDir();
90
- * // Use tempDir...
91
- * });
92
- * });
101
+ * let restoreGitEnv: () => void;
102
+ * beforeEach(() => { restoreGitEnv = detachGitEnv(); });
103
+ * afterEach(() => { restoreGitEnv(); });
93
104
  * ```
94
105
  */
95
- export declare function setupSyncTempDirSuite(prefix: string): {
96
- beforeAll: () => void;
97
- afterAll: () => void;
98
- beforeEach: () => void;
99
- afterEach: () => void;
100
- getTempDir: () => string;
101
- };
106
+ export declare function detachGitEnv(): () => void;
102
107
  /**
103
- * Probe whether this host can create symbolic links inside `dir`.
104
- *
105
- * On Windows, `symlink()` needs either Developer Mode or
106
- * `SeCreateSymbolicLinkPrivilege`; CI agents frequently have neither. Fixtures
107
- * that depend on symlinks must therefore ask rather than assume — and, having
108
- * asked, must SAY they skipped. A symlink case that silently no-ops reads as a
109
- * passing test for a property nobody exercised.
110
- *
111
- * The probe creates and removes one link, because the privilege cannot be
112
- * inferred from `process.platform` alone.
113
- *
114
- * @param dir - An existing directory to probe in (the probe cleans up after itself)
115
- * @returns True when a symlink was created successfully
108
+ * The errno-shaped error a refused `fs` call throws: a message, the `code`,
109
+ * and the `syscall`, exactly as Node shapes one.
110
+ */
111
+ export declare function errnoError(code: string, syscall: string, target: string): NodeJS.ErrnoException;
112
+ /** The sync `node:fs` calls a refusal can be injected into. */
113
+ export type RefusableSyncFsMethod = 'readdirSync' | 'readFileSync' | 'statSync' | 'lstatSync' | 'realpathSync' | 'renameSync' | 'rmSync' | 'unlinkSync';
114
+ /** The `node:fs/promises` calls a refusal can be injected into. */
115
+ export type RefusableAsyncFsMethod = 'readdir' | 'readFile' | 'stat' | 'lstat' | 'access';
116
+ /**
117
+ * Make `fs[method]` throw `code` for exactly `targetPath` until the returned
118
+ * restore is called; every other path, and every other method, stays real.
119
+ *
120
+ * A patch rather than a `chmod`: `chmod` reaches one errno (`EACCES`), only
121
+ * where POSIX modes bind, and not as root — and the property under test is
122
+ * "any refusal that is not an absence", so `EACCES`, `ELOOP`, `EMFILE` must all
123
+ * be reachable. What a walk under test meets is ONE refused call inside an
124
+ * otherwise ordinary tree; a walk that gave up entirely would pass a test where
125
+ * everything was refused.
126
+ *
127
+ * Lives in the shipped helpers because consumers in five packages each need to
128
+ * refuse a call, and the duplication gate refuses five copies.
129
+ */
130
+ export declare function refuseSyncFs(method: RefusableSyncFsMethod, targetPath: string, code: string): () => void;
131
+ /**
132
+ * `fs/promises[method]` rejects with `code` for exactly `targetPath` until the
133
+ * returned restore is called; every other path, and every other method, is real.
134
+ */
135
+ export declare function refuseAsyncFs(method: RefusableAsyncFsMethod, targetPath: string, code: string): () => void;
136
+ /**
137
+ * Run `body` while `fs[method]` throws `code` for exactly `targetPath`; the
138
+ * patch is lifted however `body` exits. See {@link refuseSyncFs}.
139
+ */
140
+ export declare function withSyncFsRefused<T>(method: RefusableSyncFsMethod, targetPath: string, code: string, body: () => T | Promise<T>): Promise<T>;
141
+ /**
142
+ * Run `body` with `fs.readdirSync` of exactly `directory` throwing an error
143
+ * carrying errno `code`. The listing case of {@link withSyncFsRefused}, named
144
+ * because refusing a LISTING is the question the crawler's consumers ask.
145
+ *
146
+ * @param directory - Absolute path of the one directory to refuse
147
+ * @param code - The errno to reject with
148
+ * @param body - Runs while the refusal is in force; may be async
149
+ * @returns Whatever `body` returned
116
150
  */
117
- export declare function canCreateSymlinks(dir: string): boolean;
151
+ export declare function withReaddirSyncRefused<T>(directory: string, code: string, body: () => T | Promise<T>): Promise<T>;
152
+ export {};
118
153
  //# sourceMappingURL=test-helpers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"test-helpers.d.ts","sourceRoot":"","sources":["../src/test-helpers.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GAAG,aAAa,GAAG,QAAQ,EAC3C,GAAG,OAAO,EAAE,MAAM,EAAE,GACnB,MAAM,CAuBR;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAG7D;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG;IACtD,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAChC,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,UAAU,EAAE,MAAM,MAAM,CAAC;CAC1B,CAyBA;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG;IACrD,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,UAAU,EAAE,MAAM,MAAM,CAAC;CAC1B,CAwBA;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAUtD"}
1
+ {"version":3,"file":"test-helpers.d.ts","sourceRoot":"","sources":["../src/test-helpers.ts"],"names":[],"mappings":"AAQA,OAAO,CAAC,MAAM,sBAAsB,EAAE,OAAO,MAAM,CAAC;AAEpD;;;;;;;;;GASG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAAE,QAAQ,CAAC,CAAC,sBAAsB,CAAC,EAAE,IAAI,CAAA;CAAE,CAAC;AAe5E;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,iBAAiB,IAAI,iBAAiB,GAAG,IAAI,CAwB5D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,iBAAiB,EACvB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,UAAU,GACjC,IAAI,CAEN;AAED;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,iBAAiB,EACvB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,UAAU,GACjC,OAAO,CAAC,IAAI,CAAC,CAEf;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB,yUAgBpB,CAAC;AAEX;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,YAAY,IAAI,MAAM,IAAI,CAoBzC;AAGD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,cAAc,CAE/F;AAED,+DAA+D;AAC/D,MAAM,MAAM,qBAAqB,GAC7B,aAAa,GACb,cAAc,GACd,UAAU,GACV,WAAW,GACX,cAAc,GACd,YAAY,GACZ,QAAQ,GACR,YAAY,CAAC;AAEjB,mEAAmE;AACnE,MAAM,MAAM,sBAAsB,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAiB1F;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,qBAAqB,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,IAAI,CAQxG;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,IAAI,CAQ1G;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,CAAC,EACvC,MAAM,EAAE,qBAAqB,EAC7B,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GACzB,OAAO,CAAC,CAAC,CAAC,CAOZ;AAED;;;;;;;;;GASG;AACH,wBAAsB,sBAAsB,CAAC,CAAC,EAC5C,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GACzB,OAAO,CAAC,CAAC,CAAC,CAEZ"}