treezip 1.1.0 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +121 -64
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,22 +1,42 @@
|
|
|
1
1
|
# treezip
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
compressed directory trees for llm prompts. zero dependencies, aggressively de-noised.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
```bash
|
|
6
|
+
npx treezip ./my-project
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## the problem
|
|
10
|
+
|
|
11
|
+
you paste a `tree` dump into an llm so it understands your codebase. two things go wrong:
|
|
12
|
+
|
|
13
|
+
1. **it's verbose.** every file gets its own line, its own indent guides, its own copy of the extension. a 400-file repo eats thousands of tokens on pure structure.
|
|
14
|
+
2. **it's full of junk.** `node_modules`, `DerivedData`, `__pycache__`, `.turbo`, coverage reports, `.DS_Store` — none of it helps the model, all of it costs tokens. one xcode build folder can turn a 100-line tree into an 11,000-line one. (that's a real number — see below.)
|
|
15
|
+
|
|
16
|
+
`treezip` fixes both. it compresses the structure losslessly with brace notation, and it hardcodes a multi-ecosystem noise filter so build artifacts never reach the output in the first place.
|
|
17
|
+
|
|
18
|
+
real-world result on a swift app with a checked-in derived-data dir and agent worktrees lying around:
|
|
19
|
+
|
|
20
|
+
| | lines |
|
|
21
|
+
|---|---|
|
|
22
|
+
| plain `tree` | 11,261 |
|
|
23
|
+
| `treezip` | **98** |
|
|
24
|
+
|
|
25
|
+
same source files visible. 99% less noise.
|
|
6
26
|
|
|
7
|
-
##
|
|
27
|
+
## install
|
|
8
28
|
|
|
9
29
|
```bash
|
|
10
30
|
npm install -g treezip
|
|
11
31
|
```
|
|
12
32
|
|
|
13
|
-
|
|
33
|
+
or don't install at all:
|
|
14
34
|
|
|
15
35
|
```bash
|
|
16
36
|
npx treezip ./my-project
|
|
17
37
|
```
|
|
18
38
|
|
|
19
|
-
##
|
|
39
|
+
## before / after
|
|
20
40
|
|
|
21
41
|
**`tree` (43 lines):**
|
|
22
42
|
|
|
@@ -69,7 +89,7 @@ my-project/
|
|
|
69
89
|
└── tsconfig.json
|
|
70
90
|
```
|
|
71
91
|
|
|
72
|
-
**`treezip` (
|
|
92
|
+
**`treezip` (12 lines):**
|
|
73
93
|
|
|
74
94
|
```
|
|
75
95
|
my-project/
|
|
@@ -86,60 +106,29 @@ my-project/
|
|
|
86
106
|
└── {Button,Card,Header,Modal,Sidebar}.test.tsx
|
|
87
107
|
```
|
|
88
108
|
|
|
89
|
-
|
|
109
|
+
every brace group decompresses to exactly one path per entry — the compression is lossless (except explicit `...` truncation, which is marked so the model knows more files exist).
|
|
90
110
|
|
|
91
|
-
##
|
|
111
|
+
## the format
|
|
92
112
|
|
|
93
|
-
|
|
|
113
|
+
| pattern | meaning | example |
|
|
94
114
|
|---|---|---|
|
|
95
|
-
| `{a,b,c}.ext` |
|
|
96
|
-
| `{a.x,b.y}` |
|
|
97
|
-
| `dir/{stems}.ext` |
|
|
98
|
-
| `dir/file` |
|
|
99
|
-
| `{a,b,...}.ext` |
|
|
100
|
-
|
|
101
|
-
###
|
|
102
|
-
|
|
103
|
-
1. **
|
|
104
|
-
2. **
|
|
105
|
-
3. **
|
|
106
|
-
4. **
|
|
107
|
-
5. **
|
|
108
|
-
|
|
109
|
-
###
|
|
110
|
-
|
|
111
|
-
|
|
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`.
|
|
139
|
-
|
|
140
|
-
## Output header
|
|
141
|
-
|
|
142
|
-
Every output includes a self-documenting header:
|
|
115
|
+
| `{a,b,c}.ext` | siblings sharing an extension | `{Button,Card,Modal}.tsx` |
|
|
116
|
+
| `{a.x,b.y}` | siblings with mixed extensions | `{README.md,tsconfig.json}` |
|
|
117
|
+
| `dir/{stems}.ext` | whole dir shares one extension | `utils/{api,auth,format}.ts` |
|
|
118
|
+
| `dir/file` | single-child chain collapsed | `scripts/build.sh` |
|
|
119
|
+
| `{a,b,...}.ext` | truncated — more files exist | `images/{logo,hero,...}.png` |
|
|
120
|
+
|
|
121
|
+
### compression rules
|
|
122
|
+
|
|
123
|
+
1. **mixed directories** (files + subdirs): all files fold into one `{...}` set, subdirs listed after
|
|
124
|
+
2. **files-only directories**: grouped by shared extension, compound-aware — `.test.ts` groups separately from `.ts`, `.config.js` from `.js`
|
|
125
|
+
3. **inline collapse**: a directory that compresses to a single item renders on one line
|
|
126
|
+
4. **single-child chains**: `a/b/c.txt` when each level has exactly one child
|
|
127
|
+
5. **truncation**: groups over 8 items show the first 5 + `...`
|
|
128
|
+
|
|
129
|
+
### self-documenting header
|
|
130
|
+
|
|
131
|
+
every output starts with a 3-line legend:
|
|
143
132
|
|
|
144
133
|
```
|
|
145
134
|
# COMPRESSED FILE TREE — AI READ GUIDE
|
|
@@ -147,20 +136,88 @@ Every output includes a self-documenting header:
|
|
|
147
136
|
# {...}.ext → truncated list (more exist) | decompress: expand braces × each entry = 1 path
|
|
148
137
|
```
|
|
149
138
|
|
|
150
|
-
|
|
139
|
+
paste the output anywhere — the model gets the decoding instructions for free, no system-prompt setup needed.
|
|
140
|
+
|
|
141
|
+
## the noise filter
|
|
142
|
+
|
|
143
|
+
this is the part most tree tools don't do. ripgrep, fd, tokei, eza all delegate to `.gitignore` — which is great until the repo has no gitignore, or the junk is inside a worktree, or someone pointed `-derivedDataPath` at a folder inside the repo. `treezip` ships its own opinionated filter, built from the github/gitignore templates, vendor docs, and the default-ignore lists of repomix/gitingest. it works in three layers:
|
|
144
|
+
|
|
145
|
+
### layer 1 — basename blocklist
|
|
146
|
+
|
|
147
|
+
distinctive names that are noise in any repo, skipped unconditionally:
|
|
148
|
+
|
|
149
|
+
- **vcs internals**: `.git` (dir *and* the worktree/submodule pointer file), `.hg`, `.svn`, `.bzr`, `.jj`, `.sapling`
|
|
150
|
+
- **os junk**: `.DS_Store`, `.Spotlight-V100`, `.Trashes`, `.fseventsd`, `Thumbs.db`, `desktop.ini`, `$RECYCLE.BIN`, `__MACOSX`, `._*` appledouble files
|
|
151
|
+
- **js/ts**: `node_modules`, `.pnpm`, `.next`, `.nuxt`, `.output`, `.turbo`, `.parcel-cache`, `.vite`, `.svelte-kit`, `.astro`, `.angular`, `.docusaurus`, `.expo`, `.wrangler`, `.vercel`, `.netlify`, `.serverless`, `coverage`, `.nyc_output`, `storybook-static`, `*.tsbuildinfo`, `.eslintcache`
|
|
152
|
+
- **python**: `__pycache__`, `.venv`, `venv`, `.tox`, `.nox`, `.pytest_cache`, `.mypy_cache`, `.ruff_cache`, `.hypothesis`, `.ipynb_checkpoints`, `*.egg-info`, `htmlcov`, `*.pyc`, `.coverage`
|
|
153
|
+
- **swift/xcode**: `DerivedData`, `.build`, `.swiftpm`, `xcuserdata`, `Carthage`, `SourcePackages`, `Index.noindex`, `ModuleCache.noindex`, `Intermediates.noindex`, `CompilationCache.noindex`, `SDKStatCaches.noindex`
|
|
154
|
+
- **everything else**: `.gradle` (jvm), `zig-cache`/`.zig-cache`/`zig-out`, `_build`/`.elixir_ls` (elixir), `.dart_tool` (flutter), `CMakeFiles`/`_deps`/`cmake-build-*` (cmake), `.vs`/`.idea` (ide caches), `.bundle`/`.yardoc` (ruby), `.terraform`/`.terragrunt-cache`/`.vagrant`/`.direnv` (infra)
|
|
151
155
|
|
|
152
|
-
|
|
156
|
+
### layer 2 — content markers
|
|
157
|
+
|
|
158
|
+
build roots can be named *anything*. xcode's `-derivedDataPath` lets you point builds at `.test-app-build/` or `whatever/`; python venvs get called `env`, `myenv`, `env310`. name-matching can't catch these — so `treezip` peeks one level inside every directory:
|
|
159
|
+
|
|
160
|
+
- contains `pyvenv.cfg` → it's a venv, skip it
|
|
161
|
+
- contains `Index.noindex` / `ModuleCache.noindex` / `SDKStatCaches.noindex` / `CompilationCache.noindex` → it's a derived-data root, skip it
|
|
162
|
+
|
|
163
|
+
whatever it's named, however deep it hides.
|
|
164
|
+
|
|
165
|
+
### layer 3 — context-aware rules
|
|
166
|
+
|
|
167
|
+
generic words like `target` and `build` are dangerous to blanket-ignore — a docs folder named `build/` is real content. so these only get skipped when a sibling file proves the ecosystem:
|
|
168
|
+
|
|
169
|
+
| dir | skipped only next to |
|
|
170
|
+
|---|---|
|
|
171
|
+
| `target/` | `Cargo.toml` or `pom.xml` |
|
|
172
|
+
| `build/` | `build.gradle`, `build.gradle.kts`, `settings.gradle(.kts)` |
|
|
173
|
+
| `deps/` | `mix.exs` |
|
|
174
|
+
| `bin/`, `obj/` | a `.csproj`/`.fsproj`/`.vbproj`/`.sln` |
|
|
175
|
+
|
|
176
|
+
plus parent-scoped rules where a dot-dir is half config, half cache:
|
|
177
|
+
|
|
178
|
+
- `.claude/worktrees` hidden, `.claude/skills` kept
|
|
179
|
+
- `.yarn/cache`, `.yarn/unplugged` hidden, `.yarn/patches`, `.yarn/releases` kept
|
|
180
|
+
|
|
181
|
+
### what never gets hidden
|
|
182
|
+
|
|
183
|
+
signal stays, always:
|
|
184
|
+
|
|
185
|
+
- lockfiles — `package-lock.json`, `pnpm-lock.yaml`, `yarn.lock`, `bun.lock`, `Cargo.lock`, `Package.resolved`, `poetry.lock`, `uv.lock`
|
|
186
|
+
- `.env`, `.envrc` — you want to *know* those exist
|
|
187
|
+
- `.vscode`, `.github` — committed config, often load-bearing
|
|
188
|
+
- `.xcodeproj` bundles — project definition is source (only the `xcuserdata` inside is junk)
|
|
189
|
+
|
|
190
|
+
## programmatic api
|
|
153
191
|
|
|
154
192
|
```ts
|
|
155
193
|
import { scan, compress, render, countNodes } from "treezip";
|
|
156
194
|
|
|
157
|
-
const tree = scan("./my-project");
|
|
158
|
-
const compressed = compress(tree);
|
|
159
|
-
const { dirs, files } = countNodes(tree);
|
|
195
|
+
const tree = scan("./my-project"); // filtered TreeNode
|
|
196
|
+
const compressed = compress(tree); // CompressedItem[]
|
|
197
|
+
const { dirs, files } = countNodes(tree); // post-filter counts
|
|
160
198
|
const output = render("my-project/", compressed);
|
|
161
199
|
console.log(output);
|
|
162
200
|
```
|
|
163
201
|
|
|
164
|
-
|
|
202
|
+
the noise predicates are exported too, if you want the filter without the tree:
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
import { isNoiseDir, isNoiseFile, hasNoiseMarker, NOISE_DIRS, NOISE_FILES } from "treezip";
|
|
206
|
+
|
|
207
|
+
isNoiseDir("node_modules", "my-project", []); // true
|
|
208
|
+
isNoiseDir("build", "repo", ["build.gradle"]); // true (gradle proven)
|
|
209
|
+
isNoiseDir("build", "repo", ["README.md"]); // false (could be real)
|
|
210
|
+
hasNoiseMarker(["pyvenv.cfg", "bin", "lib"]); // true (it's a venv)
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## design notes
|
|
214
|
+
|
|
215
|
+
- **zero dependencies.** stdlib `fs` + `path`, nothing else. install is instant, supply chain is empty.
|
|
216
|
+
- **symlinks are skipped**, so cycles can't happen and the tree never leaves the root.
|
|
217
|
+
- **deterministic output**: files first, then dirs, both alphabetical — same input, same bytes out.
|
|
218
|
+
- **the root is never filtered.** if you explicitly run `treezip ./node_modules`, you get `node_modules` — you asked, treezip answers.
|
|
219
|
+
- releases are automated: every push to `main` runs the test suite, bumps the patch version, tags, and publishes to npm.
|
|
220
|
+
|
|
221
|
+
## license
|
|
165
222
|
|
|
166
|
-
|
|
223
|
+
mit
|