treezip 1.0.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -1
- package/dist/cli.js +0 -0
- package/dist/ignore.d.ts +10 -0
- package/dist/ignore.js +205 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/scanner.d.ts +1 -1
- package/dist/scanner.js +28 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -108,7 +108,34 @@ my-project/
|
|
|
108
108
|
|
|
109
109
|
### Default ignores
|
|
110
110
|
|
|
111
|
-
|
|
111
|
+
treezip hardcodes an aggressive noise filter tuned for LLM context — build
|
|
112
|
+
artifacts, caches, and OS junk never reach the output:
|
|
113
|
+
|
|
114
|
+
- **VCS**: `.git` (dir and worktree pointer file), `.hg`, `.svn`, `.jj`, …
|
|
115
|
+
- **OS**: `.DS_Store`, `Thumbs.db`, `desktop.ini`, `._*` AppleDouble files, …
|
|
116
|
+
- **JS/TS**: `node_modules`, `.next`, `.nuxt`, `.turbo`, `.vite`, `.svelte-kit`,
|
|
117
|
+
`.astro`, `coverage`, `.wrangler`, `.vercel`, `*.tsbuildinfo`, …
|
|
118
|
+
- **Python**: `__pycache__`, `.venv`/`venv`, `.pytest_cache`, `.mypy_cache`,
|
|
119
|
+
`.ruff_cache`, `*.egg-info`, `*.pyc`, …
|
|
120
|
+
- **Swift/Xcode**: `DerivedData`, `.build`, `.swiftpm`, `xcuserdata`,
|
|
121
|
+
`Index.noindex` and friends, …
|
|
122
|
+
- **Others**: `.gradle`, `zig-out`, `_build` (Elixir), `.dart_tool`,
|
|
123
|
+
`CMakeFiles`, `cmake-build-*`, `.terraform`, `.idea`, `.vs`, …
|
|
124
|
+
|
|
125
|
+
Three smarter rules on top of the basename list:
|
|
126
|
+
|
|
127
|
+
1. **Content markers** — a directory containing `pyvenv.cfg` is a venv, one
|
|
128
|
+
containing `Index.noindex` is an Xcode DerivedData root; both are skipped
|
|
129
|
+
*whatever they're named* (catches custom `-derivedDataPath` dirs).
|
|
130
|
+
2. **Sibling confirmation** — generic names are only skipped when the
|
|
131
|
+
ecosystem is proven: `target/` next to `Cargo.toml`/`pom.xml`, `build/`
|
|
132
|
+
next to `build.gradle`, `bin`/`obj` next to a `.csproj`. A docs folder
|
|
133
|
+
named `build/` stays visible.
|
|
134
|
+
3. **Scoped rules** — `.claude/worktrees` is hidden but `.claude/skills`
|
|
135
|
+
stays; `.yarn/cache` is hidden but `.yarn/patches` stays.
|
|
136
|
+
|
|
137
|
+
Meaningful files are never hidden: lockfiles (`package-lock.json`,
|
|
138
|
+
`Package.resolved`, `uv.lock`, …), `.env`, `.envrc`, `.vscode`, `.github`.
|
|
112
139
|
|
|
113
140
|
## Output header
|
|
114
141
|
|
package/dist/cli.js
CHANGED
|
File without changes
|
package/dist/ignore.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Directory basenames that are always noise. */
|
|
2
|
+
export declare const NOISE_DIRS: Set<string>;
|
|
3
|
+
/** File basenames that are always noise. */
|
|
4
|
+
export declare const NOISE_FILES: Set<string>;
|
|
5
|
+
/** True if a directory entry should be skipped, judged by name + context. */
|
|
6
|
+
export declare function isNoiseDir(name: string, parentName: string, siblings: string[]): boolean;
|
|
7
|
+
/** True if a file entry should be skipped. */
|
|
8
|
+
export declare function isNoiseFile(name: string, parentName: string): boolean;
|
|
9
|
+
/** True if a directory's own entry list marks the whole dir as noise. */
|
|
10
|
+
export declare function hasNoiseMarker(entries: string[]): boolean;
|
package/dist/ignore.js
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
// Hardcoded noise filtering — build artifacts, caches, and OS junk that
|
|
2
|
+
// should never appear in an LLM-context tree. Sources: github/gitignore
|
|
3
|
+
// templates, vendor docs, and the default-ignore lists shipped by
|
|
4
|
+
// repomix/gitingest (the closest prior art).
|
|
5
|
+
//
|
|
6
|
+
// Design rules:
|
|
7
|
+
// 1. Only distinctive basenames are ignored unconditionally. Generic
|
|
8
|
+
// names (dist, build, out, bin, obj, vendor, Pods) are kept visible
|
|
9
|
+
// unless corroborated by a sibling marker (e.g. target + Cargo.toml).
|
|
10
|
+
// 2. Content markers catch arbitrarily-named build roots: a dir that
|
|
11
|
+
// directly contains pyvenv.cfg is a venv; one containing
|
|
12
|
+
// Index.noindex / ModuleCache.noindex is an Xcode DerivedData root
|
|
13
|
+
// (custom -derivedDataPath dirs can be named anything).
|
|
14
|
+
// 3. Lockfiles (package-lock.json, Package.resolved, uv.lock, ...) and
|
|
15
|
+
// meaningful dotfiles (.env, .envrc, .vscode) are never hidden.
|
|
16
|
+
/** Directory basenames that are always noise. */
|
|
17
|
+
export const NOISE_DIRS = new Set([
|
|
18
|
+
// VCS internals
|
|
19
|
+
".git",
|
|
20
|
+
".hg",
|
|
21
|
+
".svn",
|
|
22
|
+
".bzr",
|
|
23
|
+
".jj",
|
|
24
|
+
".sapling",
|
|
25
|
+
// OS junk
|
|
26
|
+
".Spotlight-V100",
|
|
27
|
+
".Trashes",
|
|
28
|
+
".fseventsd",
|
|
29
|
+
".TemporaryItems",
|
|
30
|
+
".DocumentRevisions-V100",
|
|
31
|
+
"$RECYCLE.BIN",
|
|
32
|
+
"__MACOSX",
|
|
33
|
+
// JavaScript / TypeScript
|
|
34
|
+
"node_modules",
|
|
35
|
+
".pnpm",
|
|
36
|
+
".pnpm-store",
|
|
37
|
+
".next",
|
|
38
|
+
".nuxt",
|
|
39
|
+
".output",
|
|
40
|
+
".turbo",
|
|
41
|
+
".parcel-cache",
|
|
42
|
+
".cache",
|
|
43
|
+
".vite",
|
|
44
|
+
".svelte-kit",
|
|
45
|
+
".astro",
|
|
46
|
+
".angular",
|
|
47
|
+
".docusaurus",
|
|
48
|
+
".expo",
|
|
49
|
+
".expo-shared",
|
|
50
|
+
".wrangler",
|
|
51
|
+
".vercel",
|
|
52
|
+
".netlify",
|
|
53
|
+
".serverless",
|
|
54
|
+
".rpt2_cache",
|
|
55
|
+
".sass-cache",
|
|
56
|
+
"coverage",
|
|
57
|
+
".nyc_output",
|
|
58
|
+
"storybook-static",
|
|
59
|
+
// Python
|
|
60
|
+
"__pycache__",
|
|
61
|
+
".venv",
|
|
62
|
+
"venv",
|
|
63
|
+
".tox",
|
|
64
|
+
".nox",
|
|
65
|
+
".pytest_cache",
|
|
66
|
+
".mypy_cache",
|
|
67
|
+
".ruff_cache",
|
|
68
|
+
".hypothesis",
|
|
69
|
+
".ipynb_checkpoints",
|
|
70
|
+
".eggs",
|
|
71
|
+
"site-packages",
|
|
72
|
+
"htmlcov",
|
|
73
|
+
"__pypackages__",
|
|
74
|
+
".pytype",
|
|
75
|
+
".pyre",
|
|
76
|
+
"cython_debug",
|
|
77
|
+
// Swift / Xcode
|
|
78
|
+
"DerivedData",
|
|
79
|
+
".build",
|
|
80
|
+
".swiftpm",
|
|
81
|
+
"xcuserdata",
|
|
82
|
+
"Carthage",
|
|
83
|
+
"SourcePackages",
|
|
84
|
+
"Index.noindex",
|
|
85
|
+
"ModuleCache.noindex",
|
|
86
|
+
"Intermediates.noindex",
|
|
87
|
+
"CompilationCache.noindex",
|
|
88
|
+
"SDKStatCaches.noindex",
|
|
89
|
+
// JVM
|
|
90
|
+
".gradle",
|
|
91
|
+
// Zig
|
|
92
|
+
".zig-cache",
|
|
93
|
+
"zig-cache",
|
|
94
|
+
"zig-out",
|
|
95
|
+
// Elixir
|
|
96
|
+
"_build",
|
|
97
|
+
".elixir_ls",
|
|
98
|
+
// Dart / Flutter
|
|
99
|
+
".dart_tool",
|
|
100
|
+
// C / CMake
|
|
101
|
+
"CMakeFiles",
|
|
102
|
+
"_deps",
|
|
103
|
+
// .NET / IDE caches (.vscode/.idea configs are often committed — .vs never is)
|
|
104
|
+
".vs",
|
|
105
|
+
".idea",
|
|
106
|
+
// Ruby
|
|
107
|
+
".bundle",
|
|
108
|
+
".yardoc",
|
|
109
|
+
// Infra
|
|
110
|
+
".terraform",
|
|
111
|
+
".terragrunt-cache",
|
|
112
|
+
".vagrant",
|
|
113
|
+
".direnv",
|
|
114
|
+
]);
|
|
115
|
+
/** File basenames that are always noise. */
|
|
116
|
+
export const NOISE_FILES = new Set([
|
|
117
|
+
".DS_Store",
|
|
118
|
+
"Thumbs.db",
|
|
119
|
+
"desktop.ini",
|
|
120
|
+
".directory",
|
|
121
|
+
".git", // worktree/submodule pointer file
|
|
122
|
+
".eslintcache",
|
|
123
|
+
".stylelintcache",
|
|
124
|
+
".node_repl_history",
|
|
125
|
+
".yarn-integrity",
|
|
126
|
+
".coverage",
|
|
127
|
+
"coverage.xml",
|
|
128
|
+
".dmypy.json",
|
|
129
|
+
"dmypy.json",
|
|
130
|
+
"nosetests.xml",
|
|
131
|
+
"Package.pins",
|
|
132
|
+
]);
|
|
133
|
+
const NOISE_FILE_PREFIXES = [
|
|
134
|
+
"._", // AppleDouble resource forks
|
|
135
|
+
"npm-debug.log",
|
|
136
|
+
"yarn-debug.log",
|
|
137
|
+
"yarn-error.log",
|
|
138
|
+
"lerna-debug.log",
|
|
139
|
+
];
|
|
140
|
+
const NOISE_FILE_SUFFIXES = [".pyc", ".pyo", ".tsbuildinfo"];
|
|
141
|
+
const NOISE_DIR_PREFIXES = ["cmake-build-"];
|
|
142
|
+
const NOISE_DIR_SUFFIXES = [".egg-info"];
|
|
143
|
+
/**
|
|
144
|
+
* Generic dir names that are only noise when a sibling file proves the
|
|
145
|
+
* ecosystem (predicate runs against each sibling basename).
|
|
146
|
+
*/
|
|
147
|
+
const SIBLING_CONFIRMED_DIRS = {
|
|
148
|
+
target: (s) => s === "Cargo.toml" || s === "pom.xml",
|
|
149
|
+
build: (s) => s === "build.gradle" ||
|
|
150
|
+
s === "build.gradle.kts" ||
|
|
151
|
+
s === "settings.gradle" ||
|
|
152
|
+
s === "settings.gradle.kts",
|
|
153
|
+
deps: (s) => s === "mix.exs",
|
|
154
|
+
bin: (s) => /\.(cs|fs|vb)proj$|\.sln$/.test(s),
|
|
155
|
+
obj: (s) => /\.(cs|fs|vb)proj$|\.sln$/.test(s),
|
|
156
|
+
};
|
|
157
|
+
/** Child names that are noise only inside a specific parent dir. */
|
|
158
|
+
const SCOPED_NOISE = {
|
|
159
|
+
".claude": new Set(["worktrees"]),
|
|
160
|
+
".yarn": new Set(["cache", "unplugged", "install-state.gz", "build-state.yml"]),
|
|
161
|
+
};
|
|
162
|
+
/**
|
|
163
|
+
* If a directory directly contains any of these entries, the entire
|
|
164
|
+
* directory is noise regardless of its name.
|
|
165
|
+
*/
|
|
166
|
+
const CONTENT_MARKERS = new Set([
|
|
167
|
+
"pyvenv.cfg", // Python venv (catches env/, myenv/, any name)
|
|
168
|
+
"Index.noindex", // Xcode DerivedData root (default or custom -derivedDataPath)
|
|
169
|
+
"ModuleCache.noindex",
|
|
170
|
+
"SDKStatCaches.noindex",
|
|
171
|
+
"CompilationCache.noindex",
|
|
172
|
+
]);
|
|
173
|
+
/** True if a directory entry should be skipped, judged by name + context. */
|
|
174
|
+
export function isNoiseDir(name, parentName, siblings) {
|
|
175
|
+
if (NOISE_DIRS.has(name))
|
|
176
|
+
return true;
|
|
177
|
+
if (NOISE_DIR_PREFIXES.some((p) => name.startsWith(p)))
|
|
178
|
+
return true;
|
|
179
|
+
if (NOISE_DIR_SUFFIXES.some((s) => name.endsWith(s)))
|
|
180
|
+
return true;
|
|
181
|
+
const scoped = SCOPED_NOISE[parentName];
|
|
182
|
+
if (scoped?.has(name))
|
|
183
|
+
return true;
|
|
184
|
+
const confirm = SIBLING_CONFIRMED_DIRS[name];
|
|
185
|
+
if (confirm && siblings.some(confirm))
|
|
186
|
+
return true;
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
/** True if a file entry should be skipped. */
|
|
190
|
+
export function isNoiseFile(name, parentName) {
|
|
191
|
+
if (NOISE_FILES.has(name))
|
|
192
|
+
return true;
|
|
193
|
+
if (NOISE_FILE_PREFIXES.some((p) => name.startsWith(p)))
|
|
194
|
+
return true;
|
|
195
|
+
if (NOISE_FILE_SUFFIXES.some((s) => name.endsWith(s)))
|
|
196
|
+
return true;
|
|
197
|
+
const scoped = SCOPED_NOISE[parentName];
|
|
198
|
+
if (scoped?.has(name))
|
|
199
|
+
return true;
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
/** True if a directory's own entry list marks the whole dir as noise. */
|
|
203
|
+
export function hasNoiseMarker(entries) {
|
|
204
|
+
return entries.some((e) => CONTENT_MARKERS.has(e));
|
|
205
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,3 +3,4 @@ export type { TreeNode } from "./scanner.js";
|
|
|
3
3
|
export { compress } from "./compress.js";
|
|
4
4
|
export type { CompressedItem } from "./compress.js";
|
|
5
5
|
export { render } from "./renderer.js";
|
|
6
|
+
export { NOISE_DIRS, NOISE_FILES, isNoiseDir, isNoiseFile, hasNoiseMarker } from "./ignore.js";
|
package/dist/index.js
CHANGED
package/dist/scanner.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export interface TreeNode {
|
|
|
3
3
|
isDir: boolean;
|
|
4
4
|
children: TreeNode[];
|
|
5
5
|
}
|
|
6
|
-
export declare function scan(rootPath: string
|
|
6
|
+
export declare function scan(rootPath: string): TreeNode;
|
|
7
7
|
/** Count total files and directories in a TreeNode (excludes root) */
|
|
8
8
|
export declare function countNodes(node: TreeNode): {
|
|
9
9
|
dirs: number;
|
package/dist/scanner.js
CHANGED
|
@@ -1,25 +1,23 @@
|
|
|
1
|
-
import { readdirSync,
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
".git",
|
|
6
|
-
".DS_Store",
|
|
7
|
-
".Spotlight-V100",
|
|
8
|
-
".Trashes",
|
|
9
|
-
"__pycache__",
|
|
10
|
-
".pnpm",
|
|
11
|
-
]);
|
|
12
|
-
export function scan(rootPath, ignores = DEFAULT_IGNORES) {
|
|
1
|
+
import { readdirSync, lstatSync, statSync } from "node:fs";
|
|
2
|
+
import { join, basename } from "node:path";
|
|
3
|
+
import { isNoiseDir, isNoiseFile, hasNoiseMarker } from "./ignore.js";
|
|
4
|
+
export function scan(rootPath) {
|
|
13
5
|
const stat = statSync(rootPath);
|
|
14
6
|
const name = rootPath.split("/").filter(Boolean).pop() ?? rootPath;
|
|
15
7
|
if (!stat.isDirectory()) {
|
|
16
8
|
return { name, isDir: false, children: [] };
|
|
17
9
|
}
|
|
18
|
-
|
|
10
|
+
// Never bail on the root itself, even if it looks like a build dir —
|
|
11
|
+
// the user explicitly asked for it.
|
|
12
|
+
return scanDir(rootPath, name);
|
|
13
|
+
}
|
|
14
|
+
function scanDir(dirPath, name) {
|
|
15
|
+
const entries = readdirSync(dirPath).sort();
|
|
16
|
+
const parentName = basename(dirPath);
|
|
19
17
|
const files = [];
|
|
20
18
|
const dirs = [];
|
|
21
19
|
for (const entry of entries) {
|
|
22
|
-
const fullPath = join(
|
|
20
|
+
const fullPath = join(dirPath, entry);
|
|
23
21
|
let entryStat;
|
|
24
22
|
try {
|
|
25
23
|
entryStat = lstatSync(fullPath);
|
|
@@ -31,9 +29,24 @@ export function scan(rootPath, ignores = DEFAULT_IGNORES) {
|
|
|
31
29
|
if (entryStat.isSymbolicLink())
|
|
32
30
|
continue;
|
|
33
31
|
if (entryStat.isDirectory()) {
|
|
34
|
-
|
|
32
|
+
if (isNoiseDir(entry, parentName, entries))
|
|
33
|
+
continue;
|
|
34
|
+
// Content-marker check: a dir whose entries reveal it as a venv or
|
|
35
|
+
// DerivedData root is noise regardless of what it's named.
|
|
36
|
+
let childEntries;
|
|
37
|
+
try {
|
|
38
|
+
childEntries = readdirSync(fullPath);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (hasNoiseMarker(childEntries))
|
|
44
|
+
continue;
|
|
45
|
+
dirs.push(scanDir(fullPath, entry));
|
|
35
46
|
}
|
|
36
47
|
else if (entryStat.isFile()) {
|
|
48
|
+
if (isNoiseFile(entry, parentName))
|
|
49
|
+
continue;
|
|
37
50
|
files.push({ name: entry, isDir: false, children: [] });
|
|
38
51
|
}
|
|
39
52
|
}
|