@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.
- package/README.md +81 -19
- package/dist/asset-reference.js +3 -11
- package/dist/asset-reference.js.map +1 -1
- package/dist/compare-code-units.d.ts +28 -0
- package/dist/compare-code-units.d.ts.map +1 -0
- package/dist/compare-code-units.js +33 -0
- package/dist/compare-code-units.js.map +1 -0
- package/dist/crawl-timing.d.ts +657 -0
- package/dist/crawl-timing.d.ts.map +1 -0
- package/dist/crawl-timing.js +765 -0
- package/dist/crawl-timing.js.map +1 -0
- package/dist/crawl.d.ts +2 -4
- package/dist/crawl.d.ts.map +1 -1
- package/dist/crawl.js +2 -4
- package/dist/crawl.js.map +1 -1
- package/dist/dirent-kind.d.ts +83 -0
- package/dist/dirent-kind.d.ts.map +1 -0
- package/dist/dirent-kind.js +129 -0
- package/dist/dirent-kind.js.map +1 -0
- package/dist/entrypoint.d.ts +54 -0
- package/dist/entrypoint.d.ts.map +1 -0
- package/dist/entrypoint.js +71 -0
- package/dist/entrypoint.js.map +1 -0
- package/dist/env-flag.d.ts +51 -0
- package/dist/env-flag.d.ts.map +1 -0
- package/dist/env-flag.js +62 -0
- package/dist/env-flag.js.map +1 -0
- package/dist/errors/errno.d.ts +51 -0
- package/dist/errors/errno.d.ts.map +1 -0
- package/dist/errors/errno.js +112 -0
- package/dist/errors/errno.js.map +1 -0
- package/dist/errors/vat-error.d.ts +60 -0
- package/dist/errors/vat-error.d.ts.map +1 -0
- package/dist/errors/vat-error.js +86 -0
- package/dist/errors/vat-error.js.map +1 -0
- package/dist/file-crawler.d.ts +60 -0
- package/dist/file-crawler.d.ts.map +1 -1
- package/dist/file-crawler.js +214 -24
- package/dist/file-crawler.js.map +1 -1
- package/dist/fs/file-hash.d.ts.map +1 -1
- package/dist/fs/file-hash.js +0 -1
- package/dist/fs/file-hash.js.map +1 -1
- package/dist/fs-utils.d.ts +628 -38
- package/dist/fs-utils.d.ts.map +1 -1
- package/dist/fs-utils.js +770 -71
- package/dist/fs-utils.js.map +1 -1
- package/dist/fs.d.ts +5 -1
- package/dist/fs.d.ts.map +1 -1
- package/dist/fs.js +9 -1
- package/dist/fs.js.map +1 -1
- package/dist/git-root-cache.d.ts +44 -0
- package/dist/git-root-cache.d.ts.map +1 -0
- package/dist/git-root-cache.js +68 -0
- package/dist/git-root-cache.js.map +1 -0
- package/dist/git-run.d.ts +167 -0
- package/dist/git-run.d.ts.map +1 -0
- package/dist/git-run.js +154 -0
- package/dist/git-run.js.map +1 -0
- package/dist/git-snapshot.d.ts +199 -0
- package/dist/git-snapshot.d.ts.map +1 -0
- package/dist/git-snapshot.js +250 -0
- package/dist/git-snapshot.js.map +1 -0
- package/dist/git-tracker.d.ts +166 -3
- package/dist/git-tracker.d.ts.map +1 -1
- package/dist/git-tracker.js +250 -22
- package/dist/git-tracker.js.map +1 -1
- package/dist/git-utils.d.ts +95 -1
- package/dist/git-utils.d.ts.map +1 -1
- package/dist/git-utils.js +227 -81
- package/dist/git-utils.js.map +1 -1
- package/dist/git.d.ts +13 -4
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js +12 -4
- package/dist/git.js.map +1 -1
- package/dist/gitignore-checker.d.ts.map +1 -1
- package/dist/gitignore-checker.js +15 -6
- package/dist/gitignore-checker.js.map +1 -1
- package/dist/glob/glob-pattern.js +5 -5
- package/dist/glob/glob-pattern.js.map +1 -1
- package/dist/index.d.ts +33 -19
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +113 -33
- package/dist/index.js.map +1 -1
- package/dist/listing-refusal.d.ts +112 -0
- package/dist/listing-refusal.d.ts.map +1 -0
- package/dist/listing-refusal.js +105 -0
- package/dist/listing-refusal.js.map +1 -0
- package/dist/numeric-args.d.ts +24 -0
- package/dist/numeric-args.d.ts.map +1 -0
- package/dist/numeric-args.js +37 -0
- package/dist/numeric-args.js.map +1 -0
- package/dist/path-containment.d.ts +64 -0
- package/dist/path-containment.d.ts.map +1 -0
- package/dist/path-containment.js +111 -0
- package/dist/path-containment.js.map +1 -0
- package/dist/path-core.d.ts +143 -18
- package/dist/path-core.d.ts.map +1 -1
- package/dist/path-core.js +171 -23
- package/dist/path-core.js.map +1 -1
- package/dist/path-utils.d.ts.map +1 -1
- package/dist/path-utils.js +42 -47
- package/dist/path-utils.js.map +1 -1
- package/dist/path.d.ts +1 -1
- package/dist/path.d.ts.map +1 -1
- package/dist/path.js +1 -1
- package/dist/path.js.map +1 -1
- package/dist/process.d.ts +7 -0
- package/dist/process.d.ts.map +1 -1
- package/dist/process.js +7 -0
- package/dist/process.js.map +1 -1
- package/dist/project-utils.d.ts +7 -1
- package/dist/project-utils.d.ts.map +1 -1
- package/dist/project-utils.js +24 -10
- package/dist/project-utils.js.map +1 -1
- package/dist/project.d.ts +7 -5
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +7 -5
- package/dist/project.js.map +1 -1
- package/dist/safe-exec.d.ts +40 -11
- package/dist/safe-exec.d.ts.map +1 -1
- package/dist/safe-exec.js +111 -25
- package/dist/safe-exec.js.map +1 -1
- package/dist/skill-targets.d.ts +2 -2
- package/dist/skill-targets.js +2 -2
- package/dist/skill-test/auth-resolver.d.ts +10 -2
- package/dist/skill-test/auth-resolver.d.ts.map +1 -1
- package/dist/skill-test/auth-resolver.js +26 -11
- package/dist/skill-test/auth-resolver.js.map +1 -1
- package/dist/skill-test/index.d.ts +1 -1
- package/dist/skill-test/index.d.ts.map +1 -1
- package/dist/skill-test/index.js +1 -1
- package/dist/skill-test/index.js.map +1 -1
- package/dist/skill-test/spawn-claude.d.ts +30 -1
- package/dist/skill-test/spawn-claude.d.ts.map +1 -1
- package/dist/skill-test/spawn-claude.js +71 -5
- package/dist/skill-test/spawn-claude.js.map +1 -1
- package/dist/skill-test/transcript.d.ts +41 -2
- package/dist/skill-test/transcript.d.ts.map +1 -1
- package/dist/skill-test/transcript.js +55 -10
- package/dist/skill-test/transcript.js.map +1 -1
- package/dist/spawn-hardened.js +0 -1
- package/dist/spawn-hardened.js.map +1 -1
- package/dist/stdio-blocking.js +13 -14
- package/dist/stdio-blocking.js.map +1 -1
- package/dist/test-helpers.d.ts +130 -79
- package/dist/test-helpers.d.ts.map +1 -1
- package/dist/test-helpers.js +229 -124
- package/dist/test-helpers.js.map +1 -1
- package/dist/testing/executables.d.ts +32 -0
- package/dist/testing/executables.d.ts.map +1 -0
- package/dist/testing/executables.js +70 -0
- package/dist/testing/executables.js.map +1 -0
- package/dist/testing/hostile-tree.d.ts +79 -0
- package/dist/testing/hostile-tree.d.ts.map +1 -0
- package/dist/testing/hostile-tree.js +164 -0
- package/dist/testing/hostile-tree.js.map +1 -0
- package/dist/testing/platform-gates.d.ts +31 -0
- package/dist/testing/platform-gates.d.ts.map +1 -0
- package/dist/testing/platform-gates.js +31 -0
- package/dist/testing/platform-gates.js.map +1 -0
- package/dist/testing/temp-dir.d.ts +255 -0
- package/dist/testing/temp-dir.d.ts.map +1 -0
- package/dist/testing/temp-dir.js +363 -0
- package/dist/testing/temp-dir.js.map +1 -0
- package/dist/testing.d.ts +94 -3
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +119 -3
- package/dist/testing.js.map +1 -1
- package/dist/text-content.d.ts +197 -0
- package/dist/text-content.d.ts.map +1 -0
- package/dist/text-content.js +354 -0
- package/dist/text-content.js.map +1 -0
- package/dist/text-file.d.ts +45 -0
- package/dist/text-file.d.ts.map +1 -0
- package/dist/text-file.js +51 -0
- package/dist/text-file.js.map +1 -0
- package/dist/text.d.ts +16 -0
- package/dist/text.d.ts.map +1 -0
- package/dist/text.js +16 -0
- package/dist/text.js.map +1 -0
- package/dist/timing-dump.d.ts +136 -0
- package/dist/timing-dump.d.ts.map +1 -0
- package/dist/timing-dump.js +229 -0
- package/dist/timing-dump.js.map +1 -0
- package/dist/windows-shell.d.ts +0 -1
- package/dist/windows-shell.d.ts.map +1 -1
- package/dist/windows-shell.js +0 -1
- package/dist/windows-shell.js.map +1 -1
- package/dist/zod-introspection.js +2 -2
- package/dist/zod-introspection.js.map +1 -1
- package/eslint/README.md +224 -50
- package/eslint/index.cjs +111 -97
- package/eslint/index.d.cts +16 -6
- package/eslint/rules/callee-name.cjs +23 -0
- package/eslint/rules/commands-import-boundary.cjs +148 -0
- package/eslint/rules/dead-import.cjs +62 -12
- package/eslint/rules/dirent-type-needs-symlink-check.cjs +282 -0
- package/eslint/rules/eslint-rule-factory.cjs +21 -3
- package/eslint/rules/exempt-path-matcher.cjs +2 -1
- package/eslint/rules/explicit-zod-strictness.cjs +120 -0
- package/eslint/rules/no-bare-dynamic-import-path.cjs +5 -1
- package/eslint/rules/no-bare-executable-spawn.cjs +70 -0
- package/eslint/rules/no-bare-symlink-in-tests.cjs +213 -0
- package/eslint/rules/no-blind-catch.cjs +171 -0
- package/eslint/rules/no-child-process-execSync.cjs +8 -0
- package/eslint/rules/no-decaying-referent.cjs +217 -0
- package/eslint/rules/no-dotdot-containment.cjs +160 -0
- package/eslint/rules/no-file-url-string-concat.cjs +4 -1
- package/eslint/rules/no-fragile-entrypoint-guard.cjs +228 -0
- package/eslint/rules/no-fs-mkdirSync.cjs +8 -0
- package/eslint/rules/no-fs-promises-cp.cjs +7 -0
- package/eslint/rules/no-fs-realpathSync.cjs +8 -0
- package/eslint/rules/no-hardcoded-path-split.cjs +29 -18
- package/eslint/rules/no-io-in-unit-tier.cjs +129 -0
- package/eslint/rules/no-literal-process-exit.cjs +129 -0
- package/eslint/rules/no-manual-path-normalize.cjs +146 -96
- package/eslint/rules/no-os-tmpdir.cjs +8 -0
- package/eslint/rules/no-path-operations-in-comparisons.cjs +7 -2
- package/eslint/rules/no-path-sep-in-strings.cjs +7 -2
- package/eslint/rules/no-path-startswith.cjs +7 -2
- package/eslint/rules/no-process-exit-in-phase.cjs +127 -0
- package/eslint/rules/no-raw-node-path.cjs +563 -0
- package/eslint/rules/no-raw-text-decode.cjs +305 -0
- package/eslint/rules/no-registry-count-pin.cjs +255 -0
- package/eslint/rules/no-self-package-import.cjs +168 -0
- package/eslint/rules/no-test-scoped-functions.cjs +10 -3
- package/eslint/rules/no-unix-shell-commands.cjs +6 -2
- package/eslint/rules/no-unsafe-root-join.cjs +23 -2
- package/eslint/rules/no-url-pathname-for-fs.cjs +5 -1
- package/eslint/rules/no-version-literal.cjs +151 -0
- package/eslint/rules/prefer-startswith-over-regex.cjs +33 -1
- package/eslint/rules/require-justified-skip.cjs +8 -2
- package/eslint/rules/safe-import.cjs +1 -1
- package/package.json +16 -16
- package/dist/link-auth/build-headers.d.ts +0 -34
- package/dist/link-auth/build-headers.d.ts.map +0 -1
- package/dist/link-auth/build-headers.js +0 -58
- package/dist/link-auth/build-headers.js.map +0 -1
- package/dist/link-auth/expand-macro.d.ts +0 -38
- package/dist/link-auth/expand-macro.d.ts.map +0 -1
- package/dist/link-auth/expand-macro.js +0 -133
- package/dist/link-auth/expand-macro.js.map +0 -1
- package/dist/link-auth/macros.yaml +0 -50
- package/dist/link-auth/resolve-token.d.ts +0 -83
- package/dist/link-auth/resolve-token.d.ts.map +0 -1
- package/dist/link-auth/resolve-token.js +0 -101
- package/dist/link-auth/resolve-token.js.map +0 -1
- package/dist/link-auth/resolve.d.ts +0 -102
- package/dist/link-auth/resolve.d.ts.map +0 -1
- package/dist/link-auth/resolve.js +0 -66
- package/dist/link-auth/resolve.js.map +0 -1
- package/dist/link-auth/rewrite.d.ts +0 -52
- package/dist/link-auth/rewrite.d.ts.map +0 -1
- package/dist/link-auth/rewrite.js +0 -102
- package/dist/link-auth/rewrite.js.map +0 -1
- package/dist/link-auth/select-provider.d.ts +0 -30
- package/dist/link-auth/select-provider.d.ts.map +0 -1
- package/dist/link-auth/select-provider.js +0 -55
- package/dist/link-auth/select-provider.js.map +0 -1
- package/dist/link-auth/template.d.ts +0 -40
- package/dist/link-auth/template.d.ts.map +0 -1
- package/dist/link-auth/template.js +0 -89
- package/dist/link-auth/template.js.map +0 -1
- package/dist/link-auth/transforms.d.ts +0 -46
- package/dist/link-auth/transforms.d.ts.map +0 -1
- package/dist/link-auth/transforms.js +0 -52
- package/dist/link-auth/transforms.js.map +0 -1
- package/dist/template-entry.d.ts +0 -10
- package/dist/template-entry.d.ts.map +0 -1
- package/dist/template-entry.js +0 -10
- package/dist/template-entry.js.map +0 -1
- package/dist/template.d.ts +0 -7
- package/dist/template.d.ts.map +0 -1
- package/dist/template.js +0 -18
- package/dist/template.js.map +0 -1
- package/eslint/rules/no-path-join.cjs +0 -14
- package/eslint/rules/no-path-relative.cjs +0 -14
- package/eslint/rules/no-path-resolve.cjs +0 -14
- package/eslint/rules/path-function-rule-factory.cjs +0 -417
package/dist/git-utils.d.ts
CHANGED
|
@@ -2,9 +2,56 @@
|
|
|
2
2
|
* Centralized git command wrapper.
|
|
3
3
|
* All git commands should go through this module for consistency and testability.
|
|
4
4
|
*/
|
|
5
|
+
import { type UnreadablePolicy } from './listing-refusal.js';
|
|
6
|
+
/**
|
|
7
|
+
* The directories a git listing was REFUSED, read off its stderr.
|
|
8
|
+
*
|
|
9
|
+
* Pure. Returns each refused directory once, worktree-relative and without
|
|
10
|
+
* git's trailing slash, with the errno its reason maps to. A directory git
|
|
11
|
+
* could not open because it no longer exists is not returned: that is the
|
|
12
|
+
* `absent` outcome of {@link listingFailure}, decided there and not here, so
|
|
13
|
+
* the two routes classify a vanished directory the same way.
|
|
14
|
+
*
|
|
15
|
+
* @param stderr - What the listing wrote to stderr
|
|
16
|
+
* @returns The refusals, in the order git printed them, deduplicated
|
|
17
|
+
*/
|
|
18
|
+
export declare function unlistableDirectoriesIn(stderr: string): {
|
|
19
|
+
directory: string;
|
|
20
|
+
code: string;
|
|
21
|
+
}[];
|
|
22
|
+
/**
|
|
23
|
+
* The policy every working-tree listing here REQUIRES.
|
|
24
|
+
*
|
|
25
|
+
* A refused directory is otherwise invisible: git's exit status is 0 and its
|
|
26
|
+
* stdout is simply shorter. Each directory git could not open is settled as
|
|
27
|
+
* the SAME {@link DirectoryRefusal} the filesystem walk produces
|
|
28
|
+
* (`file-crawler.ts`) — absolute, forward-slashed, with `transient` derived
|
|
29
|
+
* beside the errno list in `fs-utils.ts` — under the same
|
|
30
|
+
* {@link UnreadablePolicy}, so a caller decides once for both routes.
|
|
31
|
+
*
|
|
32
|
+
* 🪤 This was `onUnreadable?`, and when it was omitted the refusals git printed
|
|
33
|
+
* were DROPPED — not thrown, as the walk did, but read off stderr and ignored.
|
|
34
|
+
* The one lane whose only witness to the gap is stderr was the one lane that
|
|
35
|
+
* defaulted to silence. There is no default now.
|
|
36
|
+
*/
|
|
37
|
+
export interface GitListingOptions {
|
|
38
|
+
/** What to do with a directory git could not open while walking the working tree. */
|
|
39
|
+
unreadable: UnreadablePolicy;
|
|
40
|
+
}
|
|
5
41
|
/**
|
|
6
42
|
* Find the git repository root by walking up from the given directory.
|
|
7
43
|
*
|
|
44
|
+
* Memoized module-wide via `git-root-cache.ts`: the walk seeds an entry for
|
|
45
|
+
* every directory it climbs, not just for `startDir`, because the redundancy
|
|
46
|
+
* this removes is overlapping walks from *different* start directories rather
|
|
47
|
+
* than repeated calls with the same one. A `null` answer is memoized too — that
|
|
48
|
+
* is the case that costs a walk to the filesystem root.
|
|
49
|
+
*
|
|
50
|
+
* The memo therefore does NOT see a `.git` directory created or removed after
|
|
51
|
+
* the fact. Anything that mutates repositories in-process (tests; a host that
|
|
52
|
+
* re-enters a command) must call `resetProjectRootCaches()`, which clears this
|
|
53
|
+
* cache along with the project-root one.
|
|
54
|
+
*
|
|
8
55
|
* @param startDir - Directory to start searching from
|
|
9
56
|
* @returns Path to git root, or null if not in a git repository
|
|
10
57
|
*/
|
|
@@ -16,6 +63,8 @@ export declare function gitFindRoot(startDir: string): string | null;
|
|
|
16
63
|
* @param options.cwd - Working directory (git repository root or subdirectory)
|
|
17
64
|
* @param options.patterns - Optional glob patterns to filter files (e.g., '*.md', 'docs/**\/*.ts')
|
|
18
65
|
* @param options.includeUntracked - Include untracked files that aren't gitignored (default: false)
|
|
66
|
+
* @param options.unreadable - See {@link GitListingOptions}. Only the untracked
|
|
67
|
+
* listing walks the working tree, so only it can have skipped a directory
|
|
19
68
|
* @returns Array of file paths relative to the git root, or null if not in a git repo
|
|
20
69
|
*
|
|
21
70
|
* @example
|
|
@@ -27,11 +76,56 @@ export declare function gitFindRoot(startDir: string): string | null;
|
|
|
27
76
|
* const allFiles = gitLsFiles({ cwd: '/project', includeUntracked: true });
|
|
28
77
|
* ```
|
|
29
78
|
*/
|
|
30
|
-
export declare function gitLsFiles(options: {
|
|
79
|
+
export declare function gitLsFiles(options: GitListingOptions & {
|
|
31
80
|
cwd: string;
|
|
32
81
|
patterns?: string[];
|
|
33
82
|
includeUntracked?: boolean;
|
|
34
83
|
}): string[] | null;
|
|
84
|
+
/**
|
|
85
|
+
* List paths git does NOT track, with each wholly-untracked directory collapsed
|
|
86
|
+
* to a single entry.
|
|
87
|
+
*
|
|
88
|
+
* This is the **prune list**, not a file list, and the distinction is the whole
|
|
89
|
+
* value. `--others --ignored --exclude-standard` alone returns every ignored file
|
|
90
|
+
* individually: measured on an 8,496-path adopter working tree that is 533,557
|
|
91
|
+
* paths in 1.19 s — worse than the crawl it was meant to replace, because
|
|
92
|
+
* `.turbo/cache` alone contributed 418,518 of them. Adding `--directory`
|
|
93
|
+
* collapses each wholly-ignored directory to one entry: **369 entries in 60 ms**.
|
|
94
|
+
* A caller can then decide per directory whether to descend, and skip a
|
|
95
|
+
* half-million paths by name without ever entering them.
|
|
96
|
+
*
|
|
97
|
+
* A collapsed directory entry is returned with a trailing `/`, exactly as git
|
|
98
|
+
* spells it. That is how a caller tells "this whole subtree" from "this one
|
|
99
|
+
* file", so it is deliberately not normalized away here.
|
|
100
|
+
*
|
|
101
|
+
* @param options - Configuration options
|
|
102
|
+
* @param options.cwd - Working directory inside the repository
|
|
103
|
+
* @param options.ignored - Restrict to ignored paths. Off, the listing is
|
|
104
|
+
* untracked-but-not-ignored paths — which is the only way to see an EMPTY
|
|
105
|
+
* untracked directory, since a directory with no files in it is invisible to
|
|
106
|
+
* `ls-files` and to any tree object
|
|
107
|
+
* @param options.directory - Collapse a wholly-untracked directory to one entry
|
|
108
|
+
* @param options.unreadable - See {@link GitListingOptions}. ⚠️ The two
|
|
109
|
+
* listings meet different refusals: without `--ignored`, git collapses a
|
|
110
|
+
* WHOLLY-untracked directory without opening it, but it still opens a
|
|
111
|
+
* non-ignored directory that holds tracked files (to look for untracked
|
|
112
|
+
* siblings) and warns when it cannot; the `ignored` listing opens every
|
|
113
|
+
* non-ignored directory and so meets every refusal the first one does and
|
|
114
|
+
* more (measured on git 2.50)
|
|
115
|
+
* @returns Paths relative to the git root, or null if git did not answer
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```typescript
|
|
119
|
+
* // The prune list: where the ignored territory is, without enumerating it.
|
|
120
|
+
* const prune = gitLsOthers({ cwd: root, ignored: true, directory: true });
|
|
121
|
+
* // → ['dist/', 'node_modules/', '.turbo/', 'notes.local.md']
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
export declare function gitLsOthers(options: GitListingOptions & {
|
|
125
|
+
cwd: string;
|
|
126
|
+
ignored?: boolean;
|
|
127
|
+
directory?: boolean;
|
|
128
|
+
}): string[] | null;
|
|
35
129
|
/**
|
|
36
130
|
* Check if a file path is ignored by git
|
|
37
131
|
*
|
package/dist/git-utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git-utils.d.ts","sourceRoot":"","sources":["../src/git-utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"git-utils.d.ts","sourceRoot":"","sources":["../src/git-utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,OAAO,EAA0C,KAAK,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAmDrG;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,CAW7F;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,iBAAiB;IAChC,qFAAqF;IACrF,UAAU,EAAE,gBAAgB,CAAC;CAC9B;AAkDD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAkB3D;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG;IACtD,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,GAAG,MAAM,EAAE,GAAG,IAAI,CAqBlB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,iBAAiB,GAAG;IACvD,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,GAAG,MAAM,EAAE,GAAG,IAAI,CAiBlB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,GAAE,MAAsB,GAAG,OAAO,CAiDnF"}
|
package/dist/git-utils.js
CHANGED
|
@@ -2,29 +2,153 @@
|
|
|
2
2
|
* Centralized git command wrapper.
|
|
3
3
|
* All git commands should go through this module for consistency and testability.
|
|
4
4
|
*/
|
|
5
|
-
import { spawnSync } from 'node:child_process';
|
|
6
5
|
import { existsSync } from 'node:fs';
|
|
7
6
|
import { dirname, parse } from 'node:path';
|
|
8
|
-
import
|
|
7
|
+
import { directoryRefusalFor, listingFailure } from './fs-utils.js';
|
|
8
|
+
import { lookupGitRoot, rememberGitRoot } from './git-root-cache.js';
|
|
9
|
+
import { runGit } from './git-run.js';
|
|
10
|
+
import { requireUnreadablePolicy, settleRefusal } from './listing-refusal.js';
|
|
9
11
|
import { safePath } from './path-utils.js';
|
|
12
|
+
/**
|
|
13
|
+
* One line of git's stderr that means "I skipped this directory".
|
|
14
|
+
*
|
|
15
|
+
* `git ls-files --others` (and `--ignored`, and `git add --all`) walks the
|
|
16
|
+
* working tree to find untracked files. On a directory it cannot open it prints
|
|
17
|
+
* this warning, exits 0, and omits the subtree — so stdout alone is a shorter
|
|
18
|
+
* list that nothing can tell from a complete one. The path is spelled
|
|
19
|
+
* **relative to the worktree root** with a trailing slash, even when git was
|
|
20
|
+
* run from a subdirectory (measured: cwd `docs/open`, warning names
|
|
21
|
+
* `'docs/open/sub/'`).
|
|
22
|
+
*
|
|
23
|
+
* Anchored to the line so an unrelated warning naming a directory in passing
|
|
24
|
+
* is not read as a refusal. The reason after the colon is `strerror` text —
|
|
25
|
+
* see {@link STRERROR_TO_ERRNO}.
|
|
26
|
+
*/
|
|
27
|
+
const UNLISTABLE_DIRECTORY_LINE = /^warning: could not open directory '(.+?)\/?': (.*)$/gm;
|
|
28
|
+
/**
|
|
29
|
+
* C-locale `strerror` text → errno, for the reasons git can print here.
|
|
30
|
+
*
|
|
31
|
+
* git does not print the errno name, only its message; every listing below
|
|
32
|
+
* runs with `LC_ALL=C` so the message is the C-locale one and this table can
|
|
33
|
+
* be short. An unlisted reason is still a refusal — it becomes `UNKNOWN`, the
|
|
34
|
+
* same answer `listingFailure` gives an error carrying no errno — because
|
|
35
|
+
* dropping the line would reinstate the silent gap for exactly the errnos
|
|
36
|
+
* nobody thought of.
|
|
37
|
+
*
|
|
38
|
+
* The two ABSENCE reasons are listed so that `listingFailure` — the one owner
|
|
39
|
+
* of the absent/unreadable split — can recognise them and the line can be
|
|
40
|
+
* skipped: git prints "No such file or directory" for an untracked directory
|
|
41
|
+
* deleted between its parent's `readdir` and its own `opendir` (a concurrent
|
|
42
|
+
* `rm -rf tmp/`), and the walk route treats that same race as "no longer in
|
|
43
|
+
* the population" rather than as a refusal. Left unmapped it read as an
|
|
44
|
+
* `UNKNOWN` refusal, and the git route aborted a run the walk completed.
|
|
45
|
+
*/
|
|
46
|
+
const STRERROR_TO_ERRNO = new Map([
|
|
47
|
+
['Permission denied', 'EACCES'],
|
|
48
|
+
['Too many open files', 'EMFILE'],
|
|
49
|
+
['Too many open files in system', 'ENFILE'],
|
|
50
|
+
['Too many levels of symbolic links', 'ELOOP'],
|
|
51
|
+
['Resource temporarily unavailable', 'EAGAIN'],
|
|
52
|
+
['Input/output error', 'EIO'],
|
|
53
|
+
['Stale file handle', 'ESTALE'],
|
|
54
|
+
['Stale NFS file handle', 'ESTALE'],
|
|
55
|
+
['No such file or directory', 'ENOENT'],
|
|
56
|
+
['Not a directory', 'ENOTDIR'],
|
|
57
|
+
]);
|
|
58
|
+
/**
|
|
59
|
+
* The directories a git listing was REFUSED, read off its stderr.
|
|
60
|
+
*
|
|
61
|
+
* Pure. Returns each refused directory once, worktree-relative and without
|
|
62
|
+
* git's trailing slash, with the errno its reason maps to. A directory git
|
|
63
|
+
* could not open because it no longer exists is not returned: that is the
|
|
64
|
+
* `absent` outcome of {@link listingFailure}, decided there and not here, so
|
|
65
|
+
* the two routes classify a vanished directory the same way.
|
|
66
|
+
*
|
|
67
|
+
* @param stderr - What the listing wrote to stderr
|
|
68
|
+
* @returns The refusals, in the order git printed them, deduplicated
|
|
69
|
+
*/
|
|
70
|
+
export function unlistableDirectoriesIn(stderr) {
|
|
71
|
+
const seen = new Set();
|
|
72
|
+
const refusals = [];
|
|
73
|
+
for (const match of stderr.matchAll(UNLISTABLE_DIRECTORY_LINE)) {
|
|
74
|
+
const directory = match[1] ?? '';
|
|
75
|
+
if (directory === '' || seen.has(directory))
|
|
76
|
+
continue;
|
|
77
|
+
seen.add(directory);
|
|
78
|
+
const listing = listingFailure({ code: STRERROR_TO_ERRNO.get((match[2] ?? '').trim()) });
|
|
79
|
+
if (listing.outcome === 'unreadable')
|
|
80
|
+
refusals.push({ directory, code: listing.code });
|
|
81
|
+
}
|
|
82
|
+
return refusals;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Run one NUL-delimited `ls-files` listing and report what git skipped.
|
|
86
|
+
*
|
|
87
|
+
* `LC_ALL=C` so the stderr reason is the C-locale `strerror` text the parser
|
|
88
|
+
* knows; `trim: false` because the output is NUL-delimited (see the callers).
|
|
89
|
+
*
|
|
90
|
+
* @param args - The `ls-files` argv
|
|
91
|
+
* @param cwd - Where to run it
|
|
92
|
+
* @param api - The entry point, for the sentence an omitted policy gets
|
|
93
|
+
* @param unreadable - What a skipped directory means to the caller
|
|
94
|
+
* @returns The listed paths, or null when git did not answer
|
|
95
|
+
*/
|
|
96
|
+
function runListing(args, cwd, api, unreadable) {
|
|
97
|
+
// Before the spawn: an omitted policy is a programming error, not a fact
|
|
98
|
+
// about the tree, and a tree with nothing unreadable must not hide it.
|
|
99
|
+
requireUnreadablePolicy(unreadable, api);
|
|
100
|
+
const result = runGit(args, { cwd, trim: false, env: { LC_ALL: 'C' } });
|
|
101
|
+
// Every failure means the same thing to this caller — no listing. That covers
|
|
102
|
+
// "not a repository" (128), git missing entirely, and a listing too large for
|
|
103
|
+
// the buffer, which `ok` folds in because a truncated answer here is files
|
|
104
|
+
// silently missing rather than a short list.
|
|
105
|
+
if (!result.ok) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
if (result.stderr !== '') {
|
|
109
|
+
// The warning is worktree-root-relative regardless of `cwd`.
|
|
110
|
+
const worktreeRoot = gitFindRoot(cwd) ?? cwd;
|
|
111
|
+
for (const { directory, code } of unlistableDirectoriesIn(result.stderr)) {
|
|
112
|
+
settleRefusal(unreadable, directoryRefusalFor({ outcome: 'unreadable', code }, safePath.resolve(worktreeRoot, directory)));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// NUL-separated (from -z), so no path can ever need quote-unescaping — a
|
|
116
|
+
// trailing NUL just produces one empty string at the end, dropped here.
|
|
117
|
+
return result.stdout.split('\0').filter((line) => line.length > 0);
|
|
118
|
+
}
|
|
10
119
|
/**
|
|
11
120
|
* Find the git repository root by walking up from the given directory.
|
|
12
121
|
*
|
|
122
|
+
* Memoized module-wide via `git-root-cache.ts`: the walk seeds an entry for
|
|
123
|
+
* every directory it climbs, not just for `startDir`, because the redundancy
|
|
124
|
+
* this removes is overlapping walks from *different* start directories rather
|
|
125
|
+
* than repeated calls with the same one. A `null` answer is memoized too — that
|
|
126
|
+
* is the case that costs a walk to the filesystem root.
|
|
127
|
+
*
|
|
128
|
+
* The memo therefore does NOT see a `.git` directory created or removed after
|
|
129
|
+
* the fact. Anything that mutates repositories in-process (tests; a host that
|
|
130
|
+
* re-enters a command) must call `resetProjectRootCaches()`, which clears this
|
|
131
|
+
* cache along with the project-root one.
|
|
132
|
+
*
|
|
13
133
|
* @param startDir - Directory to start searching from
|
|
14
134
|
* @returns Path to git root, or null if not in a git repository
|
|
15
135
|
*/
|
|
16
136
|
export function gitFindRoot(startDir) {
|
|
17
137
|
let currentDir = safePath.resolve(startDir);
|
|
18
138
|
const root = parse(currentDir).root;
|
|
139
|
+
const climbed = [];
|
|
19
140
|
while (currentDir !== root) {
|
|
141
|
+
const memoized = lookupGitRoot(currentDir);
|
|
142
|
+
if (memoized !== undefined)
|
|
143
|
+
return rememberGitRoot(climbed, memoized);
|
|
144
|
+
climbed.push(currentDir);
|
|
20
145
|
const gitDir = safePath.join(currentDir, '.git');
|
|
21
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename -- walking up from validated startDir
|
|
22
146
|
if (existsSync(gitDir)) {
|
|
23
|
-
return currentDir;
|
|
147
|
+
return rememberGitRoot(climbed, currentDir);
|
|
24
148
|
}
|
|
25
149
|
currentDir = dirname(currentDir);
|
|
26
150
|
}
|
|
27
|
-
return null;
|
|
151
|
+
return rememberGitRoot(climbed, null);
|
|
28
152
|
}
|
|
29
153
|
/**
|
|
30
154
|
* List files tracked by git, optionally filtered by patterns.
|
|
@@ -33,6 +157,8 @@ export function gitFindRoot(startDir) {
|
|
|
33
157
|
* @param options.cwd - Working directory (git repository root or subdirectory)
|
|
34
158
|
* @param options.patterns - Optional glob patterns to filter files (e.g., '*.md', 'docs/**\/*.ts')
|
|
35
159
|
* @param options.includeUntracked - Include untracked files that aren't gitignored (default: false)
|
|
160
|
+
* @param options.unreadable - See {@link GitListingOptions}. Only the untracked
|
|
161
|
+
* listing walks the working tree, so only it can have skipped a directory
|
|
36
162
|
* @returns Array of file paths relative to the git root, or null if not in a git repo
|
|
37
163
|
*
|
|
38
164
|
* @example
|
|
@@ -45,41 +171,79 @@ export function gitFindRoot(startDir) {
|
|
|
45
171
|
* ```
|
|
46
172
|
*/
|
|
47
173
|
export function gitLsFiles(options) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
// Add patterns if provided
|
|
57
|
-
if (options.patterns && options.patterns.length > 0) {
|
|
58
|
-
args.push('--', ...options.patterns);
|
|
59
|
-
}
|
|
60
|
-
const result = spawnSync(gitPath, args, {
|
|
61
|
-
cwd: options.cwd,
|
|
62
|
-
encoding: 'utf-8',
|
|
63
|
-
stdio: 'pipe',
|
|
64
|
-
shell: false, // No shell interpreter for security
|
|
65
|
-
});
|
|
66
|
-
// Exit code 128 typically means not a git repository
|
|
67
|
-
if (result.status === 128 || result.error) {
|
|
68
|
-
return null;
|
|
69
|
-
}
|
|
70
|
-
if (result.status !== 0) {
|
|
71
|
-
return null;
|
|
72
|
-
}
|
|
73
|
-
// Parse output into array of file paths
|
|
74
|
-
return result.stdout
|
|
75
|
-
.split('\n')
|
|
76
|
-
.map((line) => line.trim())
|
|
77
|
-
.filter((line) => line.length > 0);
|
|
174
|
+
// -z emits NUL-separated, UNQUOTED paths regardless of byte content. Without
|
|
175
|
+
// it, git quotes any path containing non-ASCII bytes (wraps it in double
|
|
176
|
+
// quotes with octal escapes, e.g. `café.md` -> `"caf\303\251.md"`), which is
|
|
177
|
+
// unusable to any exact-string lookup against the real filename.
|
|
178
|
+
const args = ['ls-files', '-z'];
|
|
179
|
+
// Include untracked files that aren't gitignored
|
|
180
|
+
if (options.includeUntracked) {
|
|
181
|
+
args.push('--cached', '--others', '--exclude-standard');
|
|
78
182
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
183
|
+
// Add patterns if provided
|
|
184
|
+
if (options.patterns && options.patterns.length > 0) {
|
|
185
|
+
args.push('--', ...options.patterns);
|
|
186
|
+
}
|
|
187
|
+
// `trim: false` (inside `runListing`) because the output is NUL-delimited:
|
|
188
|
+
// git sorts by byte value, so a path beginning with a space sorts FIRST and a
|
|
189
|
+
// trim would silently rename it to a path that does not exist.
|
|
190
|
+
return runListing(args, options.cwd, 'gitLsFiles', options.unreadable);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* List paths git does NOT track, with each wholly-untracked directory collapsed
|
|
194
|
+
* to a single entry.
|
|
195
|
+
*
|
|
196
|
+
* This is the **prune list**, not a file list, and the distinction is the whole
|
|
197
|
+
* value. `--others --ignored --exclude-standard` alone returns every ignored file
|
|
198
|
+
* individually: measured on an 8,496-path adopter working tree that is 533,557
|
|
199
|
+
* paths in 1.19 s — worse than the crawl it was meant to replace, because
|
|
200
|
+
* `.turbo/cache` alone contributed 418,518 of them. Adding `--directory`
|
|
201
|
+
* collapses each wholly-ignored directory to one entry: **369 entries in 60 ms**.
|
|
202
|
+
* A caller can then decide per directory whether to descend, and skip a
|
|
203
|
+
* half-million paths by name without ever entering them.
|
|
204
|
+
*
|
|
205
|
+
* A collapsed directory entry is returned with a trailing `/`, exactly as git
|
|
206
|
+
* spells it. That is how a caller tells "this whole subtree" from "this one
|
|
207
|
+
* file", so it is deliberately not normalized away here.
|
|
208
|
+
*
|
|
209
|
+
* @param options - Configuration options
|
|
210
|
+
* @param options.cwd - Working directory inside the repository
|
|
211
|
+
* @param options.ignored - Restrict to ignored paths. Off, the listing is
|
|
212
|
+
* untracked-but-not-ignored paths — which is the only way to see an EMPTY
|
|
213
|
+
* untracked directory, since a directory with no files in it is invisible to
|
|
214
|
+
* `ls-files` and to any tree object
|
|
215
|
+
* @param options.directory - Collapse a wholly-untracked directory to one entry
|
|
216
|
+
* @param options.unreadable - See {@link GitListingOptions}. ⚠️ The two
|
|
217
|
+
* listings meet different refusals: without `--ignored`, git collapses a
|
|
218
|
+
* WHOLLY-untracked directory without opening it, but it still opens a
|
|
219
|
+
* non-ignored directory that holds tracked files (to look for untracked
|
|
220
|
+
* siblings) and warns when it cannot; the `ignored` listing opens every
|
|
221
|
+
* non-ignored directory and so meets every refusal the first one does and
|
|
222
|
+
* more (measured on git 2.50)
|
|
223
|
+
* @returns Paths relative to the git root, or null if git did not answer
|
|
224
|
+
*
|
|
225
|
+
* @example
|
|
226
|
+
* ```typescript
|
|
227
|
+
* // The prune list: where the ignored territory is, without enumerating it.
|
|
228
|
+
* const prune = gitLsOthers({ cwd: root, ignored: true, directory: true });
|
|
229
|
+
* // → ['dist/', 'node_modules/', '.turbo/', 'notes.local.md']
|
|
230
|
+
* ```
|
|
231
|
+
*/
|
|
232
|
+
export function gitLsOthers(options) {
|
|
233
|
+
// `--exclude-standard` is unconditional: `--ignored` is rejected outright by
|
|
234
|
+
// git without an exclude source, and without it the not-ignored listing would
|
|
235
|
+
// report every ignored path as merely untracked — the exact inversion of what
|
|
236
|
+
// either caller asked for.
|
|
237
|
+
const args = ['ls-files', '-z', '--others', '--exclude-standard'];
|
|
238
|
+
if (options.ignored) {
|
|
239
|
+
args.push('--ignored');
|
|
240
|
+
}
|
|
241
|
+
if (options.directory) {
|
|
242
|
+
args.push('--directory');
|
|
82
243
|
}
|
|
244
|
+
// See `gitLsFiles`: NUL-delimited output must not be trimmed, or a path
|
|
245
|
+
// beginning with a space — which git sorts FIRST — comes back renamed.
|
|
246
|
+
return runListing(args, options.cwd, 'gitLsOthers', options.unreadable);
|
|
83
247
|
}
|
|
84
248
|
/**
|
|
85
249
|
* Check if a file path is ignored by git
|
|
@@ -117,51 +281,33 @@ export function isGitIgnored(filePath, cwd = process.cwd()) {
|
|
|
117
281
|
if (gitFindRoot(cwd) === null) {
|
|
118
282
|
return false;
|
|
119
283
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
const ancestorResult = spawnSync(gitPath, [...checkIgnoreArgs, current], {
|
|
144
|
-
cwd,
|
|
145
|
-
encoding: 'utf-8',
|
|
146
|
-
stdio: 'pipe',
|
|
147
|
-
shell: false,
|
|
148
|
-
});
|
|
149
|
-
if (ancestorResult.status === 0) {
|
|
150
|
-
return true;
|
|
151
|
-
}
|
|
152
|
-
// If this ancestor check also fails fatally, keep walking up
|
|
153
|
-
// If it returns 1 (not ignored), the parent is tracked — stop walking
|
|
154
|
-
if (ancestorResult.status === 1) {
|
|
155
|
-
break;
|
|
156
|
-
}
|
|
157
|
-
current = dirname(current);
|
|
284
|
+
const checkIgnoreArgs = ['check-ignore', '-q'];
|
|
285
|
+
// git check-ignore returns exit code 0 if file is ignored, 1 if not
|
|
286
|
+
const result = runGit([...checkIgnoreArgs, filePath], { cwd });
|
|
287
|
+
if (result.status === 0) {
|
|
288
|
+
return true;
|
|
289
|
+
}
|
|
290
|
+
// Exit code 128 = fatal error (e.g., path beyond a symbolic link).
|
|
291
|
+
// Walk up ancestor directories to check if a parent is gitignored.
|
|
292
|
+
// Example: data/ is gitignored, data/symlink/deep/file.md fails with 128,
|
|
293
|
+
// but checking data/ directly returns 0.
|
|
294
|
+
if (result.status !== 1) {
|
|
295
|
+
const resolvedCwd = safePath.resolve(cwd);
|
|
296
|
+
const resolvedFile = safePath.resolve(cwd, filePath);
|
|
297
|
+
let current = dirname(resolvedFile);
|
|
298
|
+
while (current !== resolvedCwd && !current.endsWith(parse(current).root)) {
|
|
299
|
+
const ancestorResult = runGit([...checkIgnoreArgs, current], { cwd });
|
|
300
|
+
if (ancestorResult.status === 0) {
|
|
301
|
+
return true;
|
|
302
|
+
}
|
|
303
|
+
// If this ancestor check also fails fatally, keep walking up
|
|
304
|
+
// If it returns 1 (not ignored), the parent is tracked — stop walking
|
|
305
|
+
if (ancestorResult.status === 1) {
|
|
306
|
+
break;
|
|
158
307
|
}
|
|
308
|
+
current = dirname(current);
|
|
159
309
|
}
|
|
160
|
-
return false;
|
|
161
|
-
}
|
|
162
|
-
catch {
|
|
163
|
-
// If git is not available or other error, assume not ignored
|
|
164
|
-
return false;
|
|
165
310
|
}
|
|
311
|
+
return false;
|
|
166
312
|
}
|
|
167
313
|
//# sourceMappingURL=git-utils.js.map
|
package/dist/git-utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git-utils.js","sourceRoot":"","sources":["../src/git-utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"git-utils.js","sourceRoot":"","sources":["../src/git-utils.ts"],"names":[],"mappings":"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"}
|
package/dist/git.d.ts
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @vibe-agent-toolkit/utils/git
|
|
3
3
|
*
|
|
4
|
-
* Git primitives for build guards and gates: root discovery,
|
|
5
|
-
* enumeration, gitignore evaluation, and git URL
|
|
6
|
-
* shells out to `git` and reads the filesystem.
|
|
4
|
+
* Git primitives for build guards and gates: command execution, root discovery,
|
|
5
|
+
* tracked-file enumeration, tree snapshots, gitignore evaluation, and git URL
|
|
6
|
+
* parsing. Node-only — shells out to `git` and reads the filesystem.
|
|
7
|
+
*
|
|
8
|
+
* This is the ONLY published route to `runGit`. `safeExecSync`/`safeExecResult`
|
|
9
|
+
* on `./process` refuse the `git` binary outright and point here, so a caller
|
|
10
|
+
* that wants git gets the scrubbed environment rather than the ambient one by
|
|
11
|
+
* construction. The scrub itself, and dirty-corrected tree snapshots, come from
|
|
12
|
+
* `@vibe-validate/git`.
|
|
7
13
|
*
|
|
8
14
|
* The re-exports below are explicit rather than `export *` on purpose. A
|
|
9
15
|
* blanket re-export surfaced TWO root finders under two names — `gitFindRoot`
|
|
@@ -16,7 +22,10 @@
|
|
|
16
22
|
* old code rather than maintain two APIs). `gitFindRoot` is the only root
|
|
17
23
|
* finder in the package.
|
|
18
24
|
*/
|
|
19
|
-
export {
|
|
25
|
+
export { runGit, runGitOrThrow, type GitRunOptions, type GitRunResult, } from './git-run.js';
|
|
26
|
+
export { gitFindRoot, gitLsFiles, gitLsOthers, isGitIgnored } from './git-utils.js';
|
|
27
|
+
export type { RefuseListingContext, UnreadablePolicy } from './listing-refusal.js';
|
|
28
|
+
export { freshGitTreeSnapshot, gitTreeSnapshot, peekGitTreeSnapshot, withGitSnapshotCache, type GitSnapshotEntry, type GitTreeSnapshot, } from './git-snapshot.js';
|
|
20
29
|
export { loadGitignoreRules } from './gitignore-checker.js';
|
|
21
30
|
export { GitTracker, type GitTrackerInitOptions } from './git-tracker.js';
|
|
22
31
|
export { isGitUrl, nonInteractiveGitOverrides, parseGitUrl, type NonInteractiveGitOverrides, type ParsedGitUrl, } from './git-url.js';
|
package/dist/git.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EACL,MAAM,EACN,aAAa,EACb,KAAK,aAAa,EAClB,KAAK,YAAY,GAClB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAIpF,YAAY,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACnF,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,mBAAmB,EACnB,oBAAoB,EACpB,KAAK,gBAAgB,EACrB,KAAK,eAAe,GACrB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,KAAK,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EACL,QAAQ,EACR,0BAA0B,EAC1B,WAAW,EACX,KAAK,0BAA0B,EAC/B,KAAK,YAAY,GAClB,MAAM,cAAc,CAAC"}
|
package/dist/git.js
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @vibe-agent-toolkit/utils/git
|
|
3
3
|
*
|
|
4
|
-
* Git primitives for build guards and gates: root discovery,
|
|
5
|
-
* enumeration, gitignore evaluation, and git URL
|
|
6
|
-
* shells out to `git` and reads the filesystem.
|
|
4
|
+
* Git primitives for build guards and gates: command execution, root discovery,
|
|
5
|
+
* tracked-file enumeration, tree snapshots, gitignore evaluation, and git URL
|
|
6
|
+
* parsing. Node-only — shells out to `git` and reads the filesystem.
|
|
7
|
+
*
|
|
8
|
+
* This is the ONLY published route to `runGit`. `safeExecSync`/`safeExecResult`
|
|
9
|
+
* on `./process` refuse the `git` binary outright and point here, so a caller
|
|
10
|
+
* that wants git gets the scrubbed environment rather than the ambient one by
|
|
11
|
+
* construction. The scrub itself, and dirty-corrected tree snapshots, come from
|
|
12
|
+
* `@vibe-validate/git`.
|
|
7
13
|
*
|
|
8
14
|
* The re-exports below are explicit rather than `export *` on purpose. A
|
|
9
15
|
* blanket re-export surfaced TWO root finders under two names — `gitFindRoot`
|
|
@@ -16,7 +22,9 @@
|
|
|
16
22
|
* old code rather than maintain two APIs). `gitFindRoot` is the only root
|
|
17
23
|
* finder in the package.
|
|
18
24
|
*/
|
|
19
|
-
export {
|
|
25
|
+
export { runGit, runGitOrThrow, } from './git-run.js';
|
|
26
|
+
export { gitFindRoot, gitLsFiles, gitLsOthers, isGitIgnored } from './git-utils.js';
|
|
27
|
+
export { freshGitTreeSnapshot, gitTreeSnapshot, peekGitTreeSnapshot, withGitSnapshotCache, } from './git-snapshot.js';
|
|
20
28
|
export { loadGitignoreRules } from './gitignore-checker.js';
|
|
21
29
|
export { GitTracker } from './git-tracker.js';
|
|
22
30
|
export { isGitUrl, nonInteractiveGitOverrides, parseGitUrl, } from './git-url.js';
|
package/dist/git.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EACL,MAAM,EACN,aAAa,GAGd,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAKpF,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,mBAAmB,EACnB,oBAAoB,GAGrB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,UAAU,EAA8B,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EACL,QAAQ,EACR,0BAA0B,EAC1B,WAAW,GAGZ,MAAM,cAAc,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gitignore-checker.d.ts","sourceRoot":"","sources":["../src/gitignore-checker.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAe,EAAE,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"gitignore-checker.d.ts","sourceRoot":"","sources":["../src/gitignore-checker.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAe,EAAE,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAC;AAM7C;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA8CnF"}
|
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
import fs from 'node:fs';
|
|
9
9
|
import path from 'node:path';
|
|
10
10
|
import ignore from 'ignore';
|
|
11
|
+
import { isPathAbsentError } from './errors/errno.js';
|
|
11
12
|
import { safePath, toForwardSlash } from './path-utils.js';
|
|
13
|
+
import { readTextContentSync } from './text-file.js';
|
|
12
14
|
/**
|
|
13
15
|
* Load and parse .gitignore files from git root to baseDir.
|
|
14
16
|
* Returns an ignore instance configured with all applicable .gitignore rules.
|
|
@@ -39,15 +41,22 @@ export function loadGitignoreRules(gitRoot, baseDir) {
|
|
|
39
41
|
// Load .gitignore files from git root down to baseDir
|
|
40
42
|
for (const dir of dirsToCheck) {
|
|
41
43
|
const gitignorePath = safePath.join(dir, '.gitignore');
|
|
42
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename -- constructed from validated gitRoot and baseDir
|
|
43
44
|
if (fs.existsSync(gitignorePath)) {
|
|
44
45
|
try {
|
|
45
|
-
//
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
// Through the decoding seam. A `.gitignore` written by PowerShell's `>`
|
|
47
|
+
// is UTF-16LE, and `readFileSync(p, 'utf-8')` would hand `ignore` a
|
|
48
|
+
// string of NUL-interleaved garbage — every pattern silently wrong, with
|
|
49
|
+
// no error anywhere.
|
|
50
|
+
ig.add(readTextContentSync(gitignorePath).text);
|
|
48
51
|
}
|
|
49
|
-
catch {
|
|
50
|
-
//
|
|
52
|
+
catch (error) {
|
|
53
|
+
// Gone between `existsSync` and the read: no rules here. A `.gitignore`
|
|
54
|
+
// the OS refuses to read (`EACCES`), or one that is a directory
|
|
55
|
+
// (`EISDIR`), holds rules this checker cannot honour — and a crawl that
|
|
56
|
+
// quietly proceeded without them enumerated the ignored tree with
|
|
57
|
+
// nothing anywhere saying so. Those stay loud.
|
|
58
|
+
if (!isPathAbsentError(error))
|
|
59
|
+
throw error;
|
|
51
60
|
}
|
|
52
61
|
}
|
|
53
62
|
}
|