@vibe-agent-toolkit/utils 0.1.42 → 0.2.0-rc.10

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 (279) hide show
  1. package/README.md +81 -19
  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 +51 -0
  25. package/dist/env-flag.d.ts.map +1 -0
  26. package/dist/env-flag.js +62 -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 +214 -24
  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 +628 -38
  44. package/dist/fs-utils.d.ts.map +1 -1
  45. package/dist/fs-utils.js +770 -71
  46. package/dist/fs-utils.js.map +1 -1
  47. package/dist/fs.d.ts +5 -1
  48. package/dist/fs.d.ts.map +1 -1
  49. package/dist/fs.js +9 -1
  50. package/dist/fs.js.map +1 -1
  51. package/dist/git-root-cache.d.ts +44 -0
  52. package/dist/git-root-cache.d.ts.map +1 -0
  53. package/dist/git-root-cache.js +68 -0
  54. package/dist/git-root-cache.js.map +1 -0
  55. package/dist/git-run.d.ts +167 -0
  56. package/dist/git-run.d.ts.map +1 -0
  57. package/dist/git-run.js +154 -0
  58. package/dist/git-run.js.map +1 -0
  59. package/dist/git-snapshot.d.ts +199 -0
  60. package/dist/git-snapshot.d.ts.map +1 -0
  61. package/dist/git-snapshot.js +250 -0
  62. package/dist/git-snapshot.js.map +1 -0
  63. package/dist/git-tracker.d.ts +166 -3
  64. package/dist/git-tracker.d.ts.map +1 -1
  65. package/dist/git-tracker.js +250 -22
  66. package/dist/git-tracker.js.map +1 -1
  67. package/dist/git-utils.d.ts +95 -1
  68. package/dist/git-utils.d.ts.map +1 -1
  69. package/dist/git-utils.js +227 -81
  70. package/dist/git-utils.js.map +1 -1
  71. package/dist/git.d.ts +13 -4
  72. package/dist/git.d.ts.map +1 -1
  73. package/dist/git.js +12 -4
  74. package/dist/git.js.map +1 -1
  75. package/dist/gitignore-checker.d.ts.map +1 -1
  76. package/dist/gitignore-checker.js +15 -6
  77. package/dist/gitignore-checker.js.map +1 -1
  78. package/dist/glob/glob-pattern.js +5 -5
  79. package/dist/glob/glob-pattern.js.map +1 -1
  80. package/dist/index.d.ts +33 -19
  81. package/dist/index.d.ts.map +1 -1
  82. package/dist/index.js +113 -33
  83. package/dist/index.js.map +1 -1
  84. package/dist/listing-refusal.d.ts +112 -0
  85. package/dist/listing-refusal.d.ts.map +1 -0
  86. package/dist/listing-refusal.js +105 -0
  87. package/dist/listing-refusal.js.map +1 -0
  88. package/dist/numeric-args.d.ts +24 -0
  89. package/dist/numeric-args.d.ts.map +1 -0
  90. package/dist/numeric-args.js +37 -0
  91. package/dist/numeric-args.js.map +1 -0
  92. package/dist/path-containment.d.ts +64 -0
  93. package/dist/path-containment.d.ts.map +1 -0
  94. package/dist/path-containment.js +111 -0
  95. package/dist/path-containment.js.map +1 -0
  96. package/dist/path-core.d.ts +143 -18
  97. package/dist/path-core.d.ts.map +1 -1
  98. package/dist/path-core.js +171 -23
  99. package/dist/path-core.js.map +1 -1
  100. package/dist/path-utils.d.ts.map +1 -1
  101. package/dist/path-utils.js +42 -47
  102. package/dist/path-utils.js.map +1 -1
  103. package/dist/path.d.ts +1 -1
  104. package/dist/path.d.ts.map +1 -1
  105. package/dist/path.js +1 -1
  106. package/dist/path.js.map +1 -1
  107. package/dist/process.d.ts +7 -0
  108. package/dist/process.d.ts.map +1 -1
  109. package/dist/process.js +7 -0
  110. package/dist/process.js.map +1 -1
  111. package/dist/project-utils.d.ts +7 -1
  112. package/dist/project-utils.d.ts.map +1 -1
  113. package/dist/project-utils.js +24 -10
  114. package/dist/project-utils.js.map +1 -1
  115. package/dist/project.d.ts +7 -5
  116. package/dist/project.d.ts.map +1 -1
  117. package/dist/project.js +7 -5
  118. package/dist/project.js.map +1 -1
  119. package/dist/safe-exec.d.ts +40 -11
  120. package/dist/safe-exec.d.ts.map +1 -1
  121. package/dist/safe-exec.js +111 -25
  122. package/dist/safe-exec.js.map +1 -1
  123. package/dist/skill-targets.d.ts +2 -2
  124. package/dist/skill-targets.js +2 -2
  125. package/dist/skill-test/auth-resolver.d.ts +10 -2
  126. package/dist/skill-test/auth-resolver.d.ts.map +1 -1
  127. package/dist/skill-test/auth-resolver.js +26 -11
  128. package/dist/skill-test/auth-resolver.js.map +1 -1
  129. package/dist/skill-test/index.d.ts +1 -1
  130. package/dist/skill-test/index.d.ts.map +1 -1
  131. package/dist/skill-test/index.js +1 -1
  132. package/dist/skill-test/index.js.map +1 -1
  133. package/dist/skill-test/spawn-claude.d.ts +30 -1
  134. package/dist/skill-test/spawn-claude.d.ts.map +1 -1
  135. package/dist/skill-test/spawn-claude.js +71 -5
  136. package/dist/skill-test/spawn-claude.js.map +1 -1
  137. package/dist/skill-test/transcript.d.ts +41 -2
  138. package/dist/skill-test/transcript.d.ts.map +1 -1
  139. package/dist/skill-test/transcript.js +55 -10
  140. package/dist/skill-test/transcript.js.map +1 -1
  141. package/dist/spawn-hardened.js +0 -1
  142. package/dist/spawn-hardened.js.map +1 -1
  143. package/dist/stdio-blocking.js +13 -14
  144. package/dist/stdio-blocking.js.map +1 -1
  145. package/dist/test-helpers.d.ts +130 -79
  146. package/dist/test-helpers.d.ts.map +1 -1
  147. package/dist/test-helpers.js +229 -124
  148. package/dist/test-helpers.js.map +1 -1
  149. package/dist/testing/executables.d.ts +32 -0
  150. package/dist/testing/executables.d.ts.map +1 -0
  151. package/dist/testing/executables.js +70 -0
  152. package/dist/testing/executables.js.map +1 -0
  153. package/dist/testing/hostile-tree.d.ts +79 -0
  154. package/dist/testing/hostile-tree.d.ts.map +1 -0
  155. package/dist/testing/hostile-tree.js +164 -0
  156. package/dist/testing/hostile-tree.js.map +1 -0
  157. package/dist/testing/platform-gates.d.ts +31 -0
  158. package/dist/testing/platform-gates.d.ts.map +1 -0
  159. package/dist/testing/platform-gates.js +31 -0
  160. package/dist/testing/platform-gates.js.map +1 -0
  161. package/dist/testing/temp-dir.d.ts +255 -0
  162. package/dist/testing/temp-dir.d.ts.map +1 -0
  163. package/dist/testing/temp-dir.js +363 -0
  164. package/dist/testing/temp-dir.js.map +1 -0
  165. package/dist/testing.d.ts +94 -3
  166. package/dist/testing.d.ts.map +1 -1
  167. package/dist/testing.js +119 -3
  168. package/dist/testing.js.map +1 -1
  169. package/dist/text-content.d.ts +197 -0
  170. package/dist/text-content.d.ts.map +1 -0
  171. package/dist/text-content.js +354 -0
  172. package/dist/text-content.js.map +1 -0
  173. package/dist/text-file.d.ts +45 -0
  174. package/dist/text-file.d.ts.map +1 -0
  175. package/dist/text-file.js +51 -0
  176. package/dist/text-file.js.map +1 -0
  177. package/dist/text.d.ts +16 -0
  178. package/dist/text.d.ts.map +1 -0
  179. package/dist/text.js +16 -0
  180. package/dist/text.js.map +1 -0
  181. package/dist/timing-dump.d.ts +136 -0
  182. package/dist/timing-dump.d.ts.map +1 -0
  183. package/dist/timing-dump.js +229 -0
  184. package/dist/timing-dump.js.map +1 -0
  185. package/dist/windows-shell.d.ts +0 -1
  186. package/dist/windows-shell.d.ts.map +1 -1
  187. package/dist/windows-shell.js +0 -1
  188. package/dist/windows-shell.js.map +1 -1
  189. package/dist/zod-introspection.js +2 -2
  190. package/dist/zod-introspection.js.map +1 -1
  191. package/eslint/README.md +224 -50
  192. package/eslint/index.cjs +111 -97
  193. package/eslint/index.d.cts +16 -6
  194. package/eslint/rules/callee-name.cjs +23 -0
  195. package/eslint/rules/commands-import-boundary.cjs +148 -0
  196. package/eslint/rules/dead-import.cjs +62 -12
  197. package/eslint/rules/dirent-type-needs-symlink-check.cjs +282 -0
  198. package/eslint/rules/eslint-rule-factory.cjs +21 -3
  199. package/eslint/rules/exempt-path-matcher.cjs +2 -1
  200. package/eslint/rules/explicit-zod-strictness.cjs +120 -0
  201. package/eslint/rules/no-bare-dynamic-import-path.cjs +5 -1
  202. package/eslint/rules/no-bare-executable-spawn.cjs +70 -0
  203. package/eslint/rules/no-bare-symlink-in-tests.cjs +213 -0
  204. package/eslint/rules/no-blind-catch.cjs +171 -0
  205. package/eslint/rules/no-child-process-execSync.cjs +8 -0
  206. package/eslint/rules/no-decaying-referent.cjs +217 -0
  207. package/eslint/rules/no-dotdot-containment.cjs +160 -0
  208. package/eslint/rules/no-file-url-string-concat.cjs +4 -1
  209. package/eslint/rules/no-fragile-entrypoint-guard.cjs +228 -0
  210. package/eslint/rules/no-fs-mkdirSync.cjs +8 -0
  211. package/eslint/rules/no-fs-promises-cp.cjs +7 -0
  212. package/eslint/rules/no-fs-realpathSync.cjs +8 -0
  213. package/eslint/rules/no-hardcoded-path-split.cjs +29 -18
  214. package/eslint/rules/no-io-in-unit-tier.cjs +129 -0
  215. package/eslint/rules/no-literal-process-exit.cjs +129 -0
  216. package/eslint/rules/no-manual-path-normalize.cjs +146 -96
  217. package/eslint/rules/no-os-tmpdir.cjs +8 -0
  218. package/eslint/rules/no-path-operations-in-comparisons.cjs +7 -2
  219. package/eslint/rules/no-path-sep-in-strings.cjs +7 -2
  220. package/eslint/rules/no-path-startswith.cjs +7 -2
  221. package/eslint/rules/no-process-exit-in-phase.cjs +127 -0
  222. package/eslint/rules/no-raw-node-path.cjs +563 -0
  223. package/eslint/rules/no-raw-text-decode.cjs +305 -0
  224. package/eslint/rules/no-registry-count-pin.cjs +255 -0
  225. package/eslint/rules/no-self-package-import.cjs +168 -0
  226. package/eslint/rules/no-test-scoped-functions.cjs +10 -3
  227. package/eslint/rules/no-unix-shell-commands.cjs +6 -2
  228. package/eslint/rules/no-unsafe-root-join.cjs +23 -2
  229. package/eslint/rules/no-url-pathname-for-fs.cjs +5 -1
  230. package/eslint/rules/no-version-literal.cjs +151 -0
  231. package/eslint/rules/prefer-startswith-over-regex.cjs +33 -1
  232. package/eslint/rules/require-justified-skip.cjs +8 -2
  233. package/eslint/rules/safe-import.cjs +1 -1
  234. package/package.json +16 -16
  235. package/dist/link-auth/build-headers.d.ts +0 -34
  236. package/dist/link-auth/build-headers.d.ts.map +0 -1
  237. package/dist/link-auth/build-headers.js +0 -58
  238. package/dist/link-auth/build-headers.js.map +0 -1
  239. package/dist/link-auth/expand-macro.d.ts +0 -38
  240. package/dist/link-auth/expand-macro.d.ts.map +0 -1
  241. package/dist/link-auth/expand-macro.js +0 -133
  242. package/dist/link-auth/expand-macro.js.map +0 -1
  243. package/dist/link-auth/macros.yaml +0 -50
  244. package/dist/link-auth/resolve-token.d.ts +0 -83
  245. package/dist/link-auth/resolve-token.d.ts.map +0 -1
  246. package/dist/link-auth/resolve-token.js +0 -101
  247. package/dist/link-auth/resolve-token.js.map +0 -1
  248. package/dist/link-auth/resolve.d.ts +0 -102
  249. package/dist/link-auth/resolve.d.ts.map +0 -1
  250. package/dist/link-auth/resolve.js +0 -66
  251. package/dist/link-auth/resolve.js.map +0 -1
  252. package/dist/link-auth/rewrite.d.ts +0 -52
  253. package/dist/link-auth/rewrite.d.ts.map +0 -1
  254. package/dist/link-auth/rewrite.js +0 -102
  255. package/dist/link-auth/rewrite.js.map +0 -1
  256. package/dist/link-auth/select-provider.d.ts +0 -30
  257. package/dist/link-auth/select-provider.d.ts.map +0 -1
  258. package/dist/link-auth/select-provider.js +0 -55
  259. package/dist/link-auth/select-provider.js.map +0 -1
  260. package/dist/link-auth/template.d.ts +0 -40
  261. package/dist/link-auth/template.d.ts.map +0 -1
  262. package/dist/link-auth/template.js +0 -89
  263. package/dist/link-auth/template.js.map +0 -1
  264. package/dist/link-auth/transforms.d.ts +0 -46
  265. package/dist/link-auth/transforms.d.ts.map +0 -1
  266. package/dist/link-auth/transforms.js +0 -52
  267. package/dist/link-auth/transforms.js.map +0 -1
  268. package/dist/template-entry.d.ts +0 -10
  269. package/dist/template-entry.d.ts.map +0 -1
  270. package/dist/template-entry.js +0 -10
  271. package/dist/template-entry.js.map +0 -1
  272. package/dist/template.d.ts +0 -7
  273. package/dist/template.d.ts.map +0 -1
  274. package/dist/template.js +0 -18
  275. package/dist/template.js.map +0 -1
  276. package/eslint/rules/no-path-join.cjs +0 -14
  277. package/eslint/rules/no-path-relative.cjs +0 -14
  278. package/eslint/rules/no-path-resolve.cjs +0 -14
  279. package/eslint/rules/path-function-rule-factory.cjs +0 -417
@@ -1,7 +1,13 @@
1
1
  import fs from 'node:fs';
2
2
  import picomatch from 'picomatch';
3
+ import { directoryRefusalFor, listingFailure, } from './fs-utils.js';
3
4
  import { gitFindRoot, gitLsFiles } from './git-utils.js';
5
+ import { requireUnreadablePolicy, settleRefusal } from './listing-refusal.js';
4
6
  import { toForwardSlash, safePath } from './path-utils.js';
7
+ // The refusal vocabulary lives in `listing-refusal.ts` so `git-utils.ts` can
8
+ // share it without importing this module (which imports that one). Re-exported
9
+ // here because `./crawl` is where every caller of the walk already looks.
10
+ export { DirectoryListingRefusedError, refusedListingMessage, settleRefusal, } from './listing-refusal.js';
5
11
  /**
6
12
  * Glob options shared by every pattern this module compiles.
7
13
  *
@@ -71,6 +77,35 @@ export const NEVER_CRAWL_GLOBS = [
71
77
  */
72
78
  export const BUILD_OUTPUT_GLOBS = ['**/dist/**'];
73
79
  const DEFAULT_EXCLUDE = [...NEVER_CRAWL_GLOBS, ...BUILD_OUTPUT_GLOBS];
80
+ /**
81
+ * THE include/exclude decision, compiled once — "is this base-relative path a
82
+ * member of the crawl?"
83
+ *
84
+ * Extracted because it is asked from three places that must never drift: the
85
+ * `git ls-files` branch below, the manual walk below it, and — since the
86
+ * projection-backed lane landed — a population that arrives already enumerated
87
+ * and still has to be narrowed to what the caller's globs admit. A lane that
88
+ * approximated this with its own picomatch call would be measuring the corpus
89
+ * with a different matcher than the one that ships, which is how a "clean" A/B
90
+ * comes to compare two different questions.
91
+ *
92
+ * The `+ '/'` retry is the load-bearing subtlety, not a defensive extra: a
93
+ * directory-shaped exclude like `docs/legacy/**` does not match the bare path
94
+ * `docs/legacy`, so without it an excluded directory is admitted as a member
95
+ * whenever a caller enumerates directories as well as files.
96
+ *
97
+ * @param include - Include globs, base-relative, forward-slashed
98
+ * @param exclude - Exclude globs, same basis; an empty list excludes nothing
99
+ * @returns A predicate over base-relative, forward-slashed paths
100
+ */
101
+ export function crawlPathFilter(include, exclude) {
102
+ const isIncluded = picomatch([...include], PICOMATCH_OPTIONS);
103
+ const isExcluded = exclude.length > 0 ? picomatch([...exclude], PICOMATCH_OPTIONS) : () => false;
104
+ return (relativePath) => {
105
+ const normalized = toForwardSlash(relativePath);
106
+ return isIncluded(normalized) && !isExcluded(normalized) && !isExcluded(`${normalized}/`);
107
+ };
108
+ }
74
109
  /**
75
110
  * Crawl a directory tree and return matching files (async)
76
111
  *
@@ -90,6 +125,21 @@ const DEFAULT_EXCLUDE = [...NEVER_CRAWL_GLOBS, ...BUILD_OUTPUT_GLOBS];
90
125
  export async function crawlDirectory(options) {
91
126
  return crawlDirectorySync(options);
92
127
  }
128
+ /**
129
+ * Refuse a symlink observer on the `git ls-files` route.
130
+ *
131
+ * That route admits links as members and declines none, so the observer would
132
+ * compile, be accepted, and never be called. Refused up front rather than only
133
+ * when a git root happens to be found, so the contract is static.
134
+ *
135
+ * @param observer - The caller's `onSymlinkNotFollowed`
136
+ * @param respectGitignore - Whether the git route may be taken
137
+ */
138
+ function requireWalkForSymlinkObserver(observer, respectGitignore) {
139
+ if (observer !== undefined && respectGitignore) {
140
+ throw new TypeError('crawlDirectory: onSymlinkNotFollowed requires respectGitignore: false — the git ls-files route declines no link');
141
+ }
142
+ }
93
143
  /**
94
144
  * Crawl a directory tree and return matching files (synchronous)
95
145
  *
@@ -107,17 +157,36 @@ export async function crawlDirectory(options) {
107
157
  * });
108
158
  */
109
159
  export function crawlDirectorySync(options) {
110
- const { baseDir, include = ['**/*'], exclude = DEFAULT_EXCLUDE, followSymlinks = false, absolute = true, filesOnly = true, respectGitignore = true, includeUntracked = false, } = options;
160
+ const { baseDir, include = ['**/*'], exclude = DEFAULT_EXCLUDE, followSymlinks = false, absolute = true, filesOnly = true, respectGitignore = true, includeUntracked = false, unreadable, onSymlinkNotFollowed, } = options;
161
+ requireUnreadablePolicy(unreadable, 'crawlDirectory');
162
+ requireWalkForSymlinkObserver(onSymlinkNotFollowed, respectGitignore);
111
163
  const picoOptions = PICOMATCH_OPTIONS;
112
164
  // Resolve base directory to absolute path
113
165
  const resolvedBaseDir = safePath.resolve(baseDir);
166
+ // Compiled once, ahead of the route choice, because BOTH routes ask it: the
167
+ // walk before it lists a directory, the git route before it reports one git
168
+ // could not list. One matcher is what keeps "is this directory excluded?"
169
+ // answered the same way on both sides of the fork.
170
+ const isExcluded = exclude.length > 0 ? picomatch(exclude, picoOptions) : () => false;
171
+ /**
172
+ * Check if a path should be excluded based on patterns
173
+ */
174
+ function shouldExclude(normalizedPath) {
175
+ // Check explicit exclude patterns
176
+ return isExcluded(normalizedPath) || isExcluded(normalizedPath + '/');
177
+ }
178
+ /**
179
+ * A refused listing surfaces under the caller's policy — thrown, or handed
180
+ * over. Never a silent skip, on either route.
181
+ */
182
+ function raiseRefusal(refusal) {
183
+ settleRefusal(unreadable, refusal);
184
+ }
114
185
  // Ensure base directory exists
115
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- baseDir is from controlled config, not user input
116
186
  if (!fs.existsSync(resolvedBaseDir)) {
117
187
  throw new Error(`Base directory does not exist: ${resolvedBaseDir}`);
118
188
  }
119
189
  // Ensure base directory is actually a directory
120
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- resolved path validated above
121
190
  const baseStat = fs.statSync(resolvedBaseDir);
122
191
  if (!baseStat.isDirectory()) {
123
192
  throw new Error(`Base path is not a directory: ${resolvedBaseDir}`);
@@ -132,17 +201,51 @@ export function crawlDirectorySync(options) {
132
201
  const gitFiles = gitLsFiles({
133
202
  cwd: resolvedBaseDir,
134
203
  includeUntracked,
204
+ // The SAME decision the walk makes, in the same order: a directory
205
+ // outside this crawl's base or dropped by `exclude` is never listed by
206
+ // the walk, so a refusal on it is not this crawl's gap; anything else
207
+ // is, and goes where the walk's would go.
208
+ // `degrade` from git's side only: the refusal is filtered to this
209
+ // crawl's territory and then settled under the CALLER's policy, which
210
+ // may well be `refuse`.
211
+ unreadable: {
212
+ degrade: (refusal) => {
213
+ // Strictly beneath the base: a repository is often an ancestor of
214
+ // the crawl, and git names every refusal in the whole worktree.
215
+ if (!toForwardSlash(refusal.directory).startsWith(`${toForwardSlash(resolvedBaseDir)}/`))
216
+ return;
217
+ if (shouldExclude(toForwardSlash(safePath.relative(resolvedBaseDir, refusal.directory))))
218
+ return;
219
+ raiseRefusal(refusal);
220
+ },
221
+ },
135
222
  });
136
223
  if (gitFiles !== null) {
224
+ // ⚠️ KNOWN DIVERGENCE: this branch ignores `followSymlinks`.
225
+ //
226
+ // `git ls-files` returns symlinks as mode-120000 entries like any other
227
+ // path, and nothing below filters them out — whereas the manual walk
228
+ // (see `processSymlink`) returns early unless `followSymlinks` is set.
229
+ // So the SAME tree with the SAME options has a different population
230
+ // depending only on whether a `.git` exists above it.
231
+ //
232
+ // A committed dangling `*.md` symlink returned from here no longer
233
+ // terminates the command: `ResourceRegistry.addResources` records the
234
+ // read failure and reports it as RESOURCE_UNREADABLE. The population
235
+ // divergence itself is still live.
236
+ //
237
+ // Pinned (as today's behaviour, deliberately) by
238
+ // packages/cli/test/integration/enumeration-symlink-divergence.integration.test.ts.
239
+ // Making the two routes agree changes enumeration on real corpora — in
240
+ // one of two opposite directions, since excluding symlinks here drops
241
+ // committed content while including them in the walk grows the off-git
242
+ // population — so it is a product decision with its own changelog
243
+ // entry, not a drive-by fix here.
244
+ //
137
245
  // Git ls-files succeeded - filter using glob patterns
138
- const isIncluded = picomatch(include, picoOptions);
139
- const isExcluded = exclude.length > 0 ? picomatch(exclude, picoOptions) : () => false;
246
+ const isMember = crawlPathFilter(include, exclude);
140
247
  return gitFiles
141
- .filter((relativePath) => {
142
- const normalizedPath = toForwardSlash(relativePath);
143
- // Check both include and exclude patterns
144
- return isIncluded(normalizedPath) && !isExcluded(normalizedPath) && !isExcluded(normalizedPath + '/');
145
- })
248
+ .filter((relativePath) => isMember(relativePath))
146
249
  .map((relativePath) => {
147
250
  // git ls-files returns paths relative to cwd
148
251
  return absolute ? safePath.resolve(resolvedBaseDir, relativePath) : relativePath;
@@ -154,15 +257,24 @@ export function crawlDirectorySync(options) {
154
257
  // Fall back to manual directory crawling (not in git repo or git ls-files failed)
155
258
  // Compile glob patterns using picomatch
156
259
  const isIncluded = picomatch(include, picoOptions);
157
- const isExcluded = exclude.length > 0 ? picomatch(exclude, picoOptions) : () => false;
158
260
  const results = [];
159
261
  /**
160
- * Check if a path should be excluded based on patterns
262
+ * Real paths of directories already walked, maintained ONLY when following
263
+ * symlinks — it is what makes a directory symlink safe to traverse.
264
+ *
265
+ * A symlinked directory can be re-entered without limit (`a/loop -> a`) and
266
+ * can be reached under two names (`alias-one -> real`, `alias-two -> real`).
267
+ * Both enumerate one blob many times under distinct paths, so each row gets
268
+ * its own generated id and its own bundle entry. The loop terminated before
269
+ * this guard existed, but only because the kernel eventually refuses to
270
+ * resolve further symlinks — a limit that is 32 on macOS and 40 on Linux, so
271
+ * the POPULATION depended on the operating system, and the walk ended inside
272
+ * `processSymlink`'s catch for BROKEN links, which reported nothing.
273
+ *
274
+ * Left empty when `followSymlinks` is false: without symlink traversal a
275
+ * directory cannot be reached twice, so the default path pays no `realpath`.
161
276
  */
162
- function shouldExclude(normalizedPath) {
163
- // Check explicit exclude patterns
164
- return isExcluded(normalizedPath) || isExcluded(normalizedPath + '/');
165
- }
277
+ const visitedRealDirs = new Set();
166
278
  /**
167
279
  * Add a path to results if it matches include patterns
168
280
  */
@@ -171,21 +283,76 @@ export function crawlDirectorySync(options) {
171
283
  results.push(absolute ? fullPath : relativePath);
172
284
  }
173
285
  }
286
+ /**
287
+ * What a failed `readdirSync` / `statSync` means for the walk.
288
+ *
289
+ * Absence (`ENOENT` / `ENOTDIR`) is an entry that vanished between being
290
+ * enumerated by its parent and being asked about itself — not in the
291
+ * population, nothing to report. Anything else is a refusal, and a refusal
292
+ * is a GAP: handed to the caller if it asked, thrown otherwise. Never a
293
+ * silent `return` — that is the shorter list this walk used to hand back.
294
+ *
295
+ * @param error - What the filesystem threw
296
+ * @param target - The directory (or link) it was asked about
297
+ * @returns True when the entry is simply gone and the walk should move on
298
+ */
299
+ function reportOrSkip(error, target) {
300
+ const listing = listingFailure(error);
301
+ if (listing.outcome === 'absent')
302
+ return true;
303
+ if (listing.outcome === 'unreadable')
304
+ raiseRefusal(directoryRefusalFor(listing, target));
305
+ return false;
306
+ }
307
+ /**
308
+ * Record a directory as walked, reporting whether it had already been seen.
309
+ *
310
+ * Identity is `realpathSync.native`, not the traversal path: two names for
311
+ * one directory must collide here or the alias is enumerated twice. A
312
+ * directory whose real path cannot be read is not descended into — refusing
313
+ * to walk something we cannot identify is the safe side of a guard whose
314
+ * whole job is bounding traversal — but it is NOT silently "already walked":
315
+ * a directory that vanished is skipped like any other absence, and one the
316
+ * OS refused to canonicalise is a gap, surfaced under the caller's policy
317
+ * exactly as a refused listing is (see `reportOrSkip`).
318
+ *
319
+ * @param dir - Directory about to be walked
320
+ * @returns True when this directory must not be walked (seen before, gone, or refused)
321
+ */
322
+ function alreadyWalked(dir) {
323
+ let realPath;
324
+ try {
325
+ realPath = fs.realpathSync.native(dir);
326
+ }
327
+ catch (error) {
328
+ reportOrSkip(error, dir);
329
+ return true;
330
+ }
331
+ if (visitedRealDirs.has(realPath)) {
332
+ return true;
333
+ }
334
+ visitedRealDirs.add(realPath);
335
+ return false;
336
+ }
174
337
  /**
175
338
  * Process a symbolic link entry
176
339
  */
177
340
  function processSymlink(fullPath, normalizedPath, relativePath) {
178
341
  if (!followSymlinks) {
342
+ if (onSymlinkNotFollowed !== undefined && isIncluded(normalizedPath)) {
343
+ onSymlinkNotFollowed(absolute ? fullPath : relativePath);
344
+ }
179
345
  return;
180
346
  }
181
347
  // Resolve symlink and check if it's a directory or file
182
348
  let targetStat;
183
349
  try {
184
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- path constructed from validated baseDir + entries
185
350
  targetStat = fs.statSync(fullPath);
186
351
  }
187
- catch {
188
- // Skip broken symlinks
352
+ catch (error) {
353
+ // A broken symlink is absence and is skipped; a target the OS refused to
354
+ // stat is a gap, and goes the same way a refused listing does.
355
+ reportOrSkip(error, fullPath);
189
356
  return;
190
357
  }
191
358
  if (targetStat.isDirectory()) {
@@ -216,15 +383,32 @@ export function crawlDirectorySync(options) {
216
383
  * Recursively walk directory tree
217
384
  */
218
385
  function walkDirectory(currentDir) {
386
+ if (followSymlinks && alreadyWalked(currentDir)) {
387
+ return;
388
+ }
219
389
  let entries;
220
390
  try {
221
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- path constructed from validated baseDir, recursively walking
222
391
  entries = fs.readdirSync(currentDir, { withFileTypes: true });
223
392
  }
224
- catch {
225
- // Skip directories we don't have permission to read
393
+ catch (error) {
394
+ // 🚨 Not a silent skip. A directory that refused to be listed is a gap in
395
+ // the population this walk defines — see `reportOrSkip`.
396
+ reportOrSkip(error, currentDir);
226
397
  return;
227
398
  }
399
+ // Two passes, not one: `readdirSync` order is filesystem-defined (see
400
+ // the comment on `alreadyWalked`), and both a real directory and a
401
+ // symlink alias pointing at it go through the same `alreadyWalked` gate,
402
+ // keyed only on realpath. Whichever spelling is dispatched FIRST claims
403
+ // that realpath in `visitedRealDirs`, and the second is then skipped as
404
+ // "already walked" — so if the alias happened to sort first, the
405
+ // directory's contents would be recorded under the alias's name and the
406
+ // real directory would be skipped entirely. Walking every non-symlink
407
+ // entry to completion before any symlink entry guarantees a real
408
+ // directory always claims its own realpath first; an alias reaching the
409
+ // same realpath afterward is correctly skipped by `alreadyWalked`. Only
410
+ // the ORDER of dispatch changes here — per-entry logic is unchanged.
411
+ const symlinkEntries = [];
228
412
  for (const entry of entries) {
229
413
  const fullPath = safePath.join(currentDir, entry.name);
230
414
  const relativePath = safePath.relative(resolvedBaseDir, fullPath);
@@ -233,9 +417,9 @@ export function crawlDirectorySync(options) {
233
417
  if (shouldExclude(normalizedPath)) {
234
418
  continue;
235
419
  }
236
- // Dispatch to appropriate handler based on entry type
420
+ // Defer symlinks to the second pass; dispatch everything else now.
237
421
  if (entry.isSymbolicLink()) {
238
- processSymlink(fullPath, normalizedPath, relativePath);
422
+ symlinkEntries.push(entry);
239
423
  }
240
424
  else if (entry.isDirectory()) {
241
425
  processDirectory(fullPath, normalizedPath, relativePath);
@@ -244,6 +428,12 @@ export function crawlDirectorySync(options) {
244
428
  processFile(normalizedPath, fullPath, relativePath);
245
429
  }
246
430
  }
431
+ for (const entry of symlinkEntries) {
432
+ const fullPath = safePath.join(currentDir, entry.name);
433
+ const relativePath = safePath.relative(resolvedBaseDir, fullPath);
434
+ const normalizedPath = toForwardSlash(relativePath);
435
+ processSymlink(fullPath, normalizedPath, relativePath);
436
+ }
247
437
  }
248
438
  // Start recursive walk from base directory
249
439
  walkDirectory(resolvedBaseDir);
@@ -1 +1 @@
1
- {"version":3,"file":"file-crawler.js","sourceRoot":"","sources":["../src/file-crawler.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"file-crawler.js","sourceRoot":"","sources":["../src/file-crawler.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"file-hash.d.ts","sourceRoot":"","sources":["../../src/fs/file-hash.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIpD"}
1
+ {"version":3,"file":"file-hash.d.ts","sourceRoot":"","sources":["../../src/fs/file-hash.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGpD"}
@@ -16,7 +16,6 @@ import { readFileSync } from 'node:fs';
16
16
  * // '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824'
17
17
  */
18
18
  export function fileContentHash(path) {
19
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- path is caller-supplied; callers are responsible for path safety
20
19
  const bytes = readFileSync(path);
21
20
  return createHash('sha256').update(bytes).digest('hex');
22
21
  }
@@ -1 +1 @@
1
- {"version":3,"file":"file-hash.js","sourceRoot":"","sources":["../../src/fs/file-hash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,uIAAuI;IACvI,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IACjC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC1D,CAAC"}
1
+ {"version":3,"file":"file-hash.js","sourceRoot":"","sources":["../../src/fs/file-hash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IACjC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC1D,CAAC"}