codebase-snapshot 0.0.0-stage → 0.9.0

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.
@@ -0,0 +1,384 @@
1
+ # Troubleshooting
2
+
3
+ Every message below is the real text the tool prints. Find your symptom, then the cause and
4
+ the fix.
5
+
6
+ ## The command does nothing
7
+
8
+ **Symptom** — `npx codebase-snapshot generate` exits `0` and prints nothing. No
9
+ `snapshot/` directory appears.
10
+
11
+ **Cause** — a **stale global install** is shadowing `npx`. When a global copy exists, npm
12
+ runs it instead of the published package, and a leftover `npm link` (pointing at a
13
+ development checkout) resolves to a binary that is not there.
14
+
15
+ **Fix**
16
+
17
+ ```bash
18
+ npm ls -g --depth=0 | grep codebase-snapshot
19
+ npm rm -g codebase-snapshot
20
+ npx codebase-snapshot generate
21
+ ```
22
+
23
+ If nothing is installed globally, clear the npx cache (`npm cache clean --force`) and retry.
24
+ This is a local environment problem, not a package problem: on a machine without a global
25
+ copy the same command works.
26
+
27
+ ## `command not found`
28
+
29
+ **Cause** — when the package is installed as a project dependency, its binary lives in
30
+ `node_modules/.bin` and is only on `PATH` inside a client runner or a package script.
31
+
32
+ **Fix** — use `npx codebase-snapshot`, `pnpm exec codebase-snapshot`,
33
+ `yarn codebase-snapshot`, `bun run codebase-snapshot`, or `./node_modules/.bin/codebase-snapshot`
34
+ in scripts.
35
+
36
+ ## Exit code 2 — the command itself was wrong
37
+
38
+ Exit `2` always means "retrying this unchanged will fail the same way". The message names the
39
+ problem, and for an option the command does not take it also names the commands that do:
40
+
41
+ ```
42
+ ❌ --watch does not apply to `report` and was not applied. It is accepted by: `generate`.
43
+ ❌ --cwd does not apply to `describe` and was not applied. It is accepted by: `generate`, `validate`, `report`, `init`, `explain`, `skill`.
44
+ ```
45
+
46
+ Nothing is silently ignored — that is the point of the message. The per-option `Applies to`
47
+ column is in the [CLI reference](cli.md#options).
48
+
49
+ | Message | Fix |
50
+ | :--- | :--- |
51
+ | `Unknown command "frobnicate". Expected one of: …` | Use one of the commands in the [CLI reference](cli.md). |
52
+ | `--preset does not apply to \`generate\` and was not applied. It is accepted by: \`init\`. To scope a run, set include/excludePatterns/preset in snapshot.config.ts (or run \`codebase-snapshot init\` to generate one).` | `--preset`, `--include`, `--exclude`, `--yes` and `--force` configure `init`, which writes them into the config. Set them at generation time in `snapshot.config.ts` instead. |
53
+ | `--install does not apply to \`generate\` and was not applied. It is accepted by: \`skill\`.` | `--install` and `--target` configure `skill` only. |
54
+ | `--cwd directory does not exist: …` / `--cwd is not a directory: …` | Point `--cwd` at an existing repository directory. |
55
+ | `--out-dir requires a value (received an empty string).` | An unset shell variable expanded to nothing. Quote it or drop the flag. Every string option rejects `""` rather than silently using the default. |
56
+ | `--max-part-bytes received a value that looks like another flag (for example a negative number). Use the "--option=value" form, and note that sizes must be positive.` | A negative number reads as a flag. Use `--max-part-bytes=-5`, and note that sizes must be positive. |
57
+ | `Unknown preset "nope". Expected one of: …` | The message lists all 25 valid names. |
58
+ | `--target must be one of: claude, cursor, all (received "bogus").` | Fix the value. |
59
+ | `diff expects exactly two arguments …` / `explain expects exactly one path.` | Fix the arity. |
60
+
61
+ The `agents` target was removed in 4.1.1: `skill --install` writes only into the two registries
62
+ agent tools read by name (`.claude/skills/` and `.cursor/rules/`) and never writes `AGENTS.md`,
63
+ which is your repository's own agent contract. To put the skill there, print it and paste it
64
+ yourself: `codebase-snapshot skill >> AGENTS.md`.
65
+
66
+ ## Exit code 1 — the state was wrong
67
+
68
+ ### `Refusing to replace outDir …`
69
+
70
+ ```
71
+ ❌ Failed to generate snapshot: Refusing to replace outDir /repo/src: it holds no .codebase-snapshot marker (found app.ts). Remove the directory yourself, or point outDir at a new or empty directory.
72
+ ```
73
+
74
+ **Cause** — the output directory contains files this tool did not write, and it cannot prove
75
+ it wrote them. This guard exists because a mistyped `outDir` used to replace the directory:
76
+ `outDir: "src"` deleted source code while reporting success.
77
+
78
+ Ownership is proved by a `.codebase-snapshot` marker that parses as ours — a JSON object
79
+ naming this generator, carrying a schema version this build understands, a per-snapshot `id`
80
+ and the digest of the manifest it shipped. A file with the right name is not enough:
81
+
82
+ | The directory | What the tool says |
83
+ | :--- | :--- |
84
+ | has files, no marker | `it holds no .codebase-snapshot marker (found app.ts)` |
85
+ | has a zero-byte marker | `its .codebase-snapshot marker is empty (found .codebase-snapshot, app.ts)` |
86
+ | has a marker that is not JSON | `its .codebase-snapshot marker is not valid JSON (found .codebase-snapshot, app.ts)` |
87
+ | was written by 3.0.0 | `its .codebase-snapshot marker carries no snapshot id (found .codebase-snapshot, manifest.json)` |
88
+ | carries our marker but you added files | `it carries this generator's ownership marker, but these entries are not part of the snapshot it recorded: custom.txt. Remove them yourself, or point outDir at a new or empty directory.` |
89
+
90
+ **Fix** — point `--out-dir` somewhere new, or delete the directory yourself. Do not delete
91
+ your source. Accepted without complaint: a missing directory, an empty directory, one holding
92
+ nothing but `.DS_Store`, and a complete snapshot this build wrote.
93
+
94
+ `.gitkeep` and `.gitignore` are **not** accepted, even in a directory that holds nothing else.
95
+ Both are files you create — the generator never writes either into an output directory — so a
96
+ directory holding one is somebody's data and is refused rather than replaced. Move the file out,
97
+ or point `--out-dir` somewhere new.
98
+
99
+ A snapshot written by 3.0.0 or earlier is **not** upgraded in place: its marker has three keys
100
+ and no `id`, so the new build cannot prove it wrote the directory and refuses. Delete the old
101
+ snapshot directory and generate again.
102
+
103
+ ### `Refusing to write into outDir …: it exists but is not a directory.`
104
+
105
+ ```
106
+ ❌ Failed to generate snapshot: Refusing to write into outDir /repo/notes.txt: it exists but is not a directory.
107
+ ```
108
+
109
+ A regular file has the same name as the output directory. Rename or remove it.
110
+
111
+ ### `The config file could not be read`
112
+
113
+ ```
114
+ ⚠ Config: Could not parse /repo/snapshot.config.json: Expected property name or '}' …
115
+ ❌ The config file could not be read. Fix it, or delete it to use the defaults.
116
+ ```
117
+
118
+ **Cause** — a config file exists but does not parse, or a `.ts` config cannot be loaded. This
119
+ is fatal on purpose: skipping a broken config would generate a snapshot with none of your
120
+ settings and still report success.
121
+
122
+ **Fix** — fix the syntax, or delete the file to run on defaults. Check that the config is
123
+ loadable on your Node version: a `.ts` config needs Node ≥ 22.6 **and** an ES-module project.
124
+ `codebase-snapshot init` picks the right filename for the project automatically.
125
+
126
+ ### `This directory is a git repository, but git could not list its files.`
127
+
128
+ ```
129
+ ❌ Failed to generate snapshot: This directory is a git repository, but git could not list its files. A plain walk honours .gitignore files, but git also applies .git/info/exclude and your global excludes file, includes files that are tracked although a .gitignore now matches them, and treats a submodule as a single entry. Fix the repository (git status should work), run outside git, or pass --discovery walk to accept those differences.
130
+ ```
131
+
132
+ `git` is present and a `.git` directory exists, but it cannot list the files — an empty or
133
+ half-initialised `.git`, a permissions problem, or a damaged index. The tool refuses rather
134
+ than silently choosing between two discovery methods: which files are in the snapshot would
135
+ otherwise depend on whether git happened to work.
136
+
137
+ **Fix** — make `git status` work in that directory (repair or re-clone the repository), generate
138
+ from a copy that has no `.git` directory, or pass `--discovery walk` to scan the tree anyway.
139
+ The walk reads every `.gitignore` it meets, nested ones included, and still skips credential
140
+ files; what it cannot reproduce is git's own ignore sources (`.git/info/exclude` and your global
141
+ `core.excludesFile`), git's inclusion of files that are tracked although a `.gitignore` now
142
+ matches them, and its treatment of a submodule as one entry instead of a tree.
143
+
144
+ `--discovery git` says the opposite — require git — so it refuses in a directory that has no
145
+ usable repository:
146
+
147
+ ```
148
+ ❌ Failed to generate snapshot: Discovery mode is `git`, but git could not list this directory's files. Run inside a working repository, or pass --discovery walk (or auto) to scan the tree directly.
149
+ ```
150
+
151
+ ### `Another generate run is already using the staging directory …`
152
+
153
+ ```
154
+ ❌ Failed to generate snapshot: Another generate run is already using the staging directory /repo/snapshot.tmp. Wait for it to finish, or delete /repo/codebase-snapshot-1a2b3c4d5e6f7890.lock if no other run is active.
155
+ ```
156
+
157
+ A second `generate` against the same repository started while the first still held the staging
158
+ directory — two terminals, or a CI job and a developer at once. The lock is advisory and on
159
+ disk, so a run that was killed cannot release it.
160
+
161
+ **Fix** — wait for the other run, then retry. If no other run is active, the named lock file is
162
+ stale: delete it, and the next run proceeds.
163
+
164
+ ### `Refusing to generate: …`
165
+
166
+ ```
167
+ ❌ Failed to generate snapshot: Refusing to generate: outDir is the working directory or one of its ancestors. outDir=/repo tmpDir=/repo/snapshot.tmp cwd=/repo
168
+ ```
169
+
170
+ The output paths are unsafe: `outDir` or `tmpDir` is the working directory, an ancestor, the
171
+ filesystem root, or they contain one another. The message prints the resolved paths so you
172
+ can see why. Use a subdirectory.
173
+
174
+ ### Budget errors
175
+
176
+ A budget set in a **config file** exits `1`, and names the setting as you wrote it:
177
+
178
+ | Message | Fix |
179
+ | :--- | :--- |
180
+ | `maxPartBytes must be a positive byte count (received 0).` | Zero and negative values are errors, not "use the default". |
181
+ | `maxPartBytes must be at least 4096 bytes (received 1000).` | Raise it to 4096 or more (`"120k"` is 122880). |
182
+ | `maxPartBytes must be a byte count such as 120000, "120k" or "1.5mb" (received "abc").` | The value is not a number or a supported size string. |
183
+
184
+ The same mistake on the **command line** is a usage error, exits `2`, names the flag, and is
185
+ followed by the usage text:
186
+
187
+ | Message | Fix |
188
+ | :--- | :--- |
189
+ | `--max-part-bytes must be at least 4096, received "1000".` | Raise it to 4096 or more. `--max-part-bytes 0` and `--max-part-bytes 1000` both produce this form. |
190
+ | `--max-part-bytes expects a byte count such as 120000 or "120k", received "abc".` | The value is not a number or a supported size string. `--max-part-bytes=-5` produces this form too. |
191
+
192
+ `--large-file-bytes` and `--max-monolith-bytes` share the second message, with their own flag
193
+ name, and their minimum is 1 rather than 4096:
194
+ `--large-file-bytes must be at least 1, received "0".`
195
+
196
+ ### `Cannot write the snapshot: permission denied under …`
197
+
198
+ The output directory is not writable (or a parent is not readable). The previous snapshot is
199
+ left intact. Fix the permissions, or choose another `--out-dir`.
200
+
201
+ ### `Snapshot validation failed` (from `validate`)
202
+
203
+ Exit `1` with one line per class of drift:
204
+
205
+ | Line | Meaning |
206
+ | :--- | :--- |
207
+ | `- modified: path` | The file changed since the snapshot. |
208
+ | `- deleted: path` | The file is in the snapshot but no longer on disk. |
209
+ | `- new file: path` | The file exists on disk and is not in the snapshot. |
210
+ | `- part: part-001.md (missing from snapshot/parts)` | The part file was deleted or renamed. |
211
+ | `- part: … (size mismatch …)` | The part was edited by hand. |
212
+ | `- config: …` | A setting differs from the one recorded in the manifest. |
213
+ | `- counts: …` | The manifest's own bookkeeping disagrees with its contents. |
214
+
215
+ **Fix** — re-run `generate`. If it persists, the snapshot directory is being modified by
216
+ something else.
217
+
218
+ ### `report` exits 1 (only when it cannot read the snapshot)
219
+
220
+ - `Manifest not found at … Run generate first.` — nothing has been generated yet.
221
+ - `… is not a snapshot manifest: missing \`counts\`` — the file parsed as JSON but is not a
222
+ manifest this tool wrote. Wrong `--out-dir`, or a foreign file named `manifest.json`.
223
+
224
+ ### `diff` exits 1
225
+
226
+ - `No snapshot found at X: the path does not exist.`
227
+ - `Could not read a manifest at X.`
228
+ - `… is not a snapshot manifest (no "counts" object).`
229
+
230
+ Point it at a snapshot directory or a `manifest.json` written by this tool.
231
+
232
+ ## A file is missing from the snapshot
233
+
234
+ Run `explain` on it — it names the exact reason and the rule responsible:
235
+
236
+ ```bash
237
+ npx codebase-snapshot explain src/generated/schema.ts
238
+ ```
239
+
240
+ `manifest.json` records the same for every file in `skipped[]`:
241
+
242
+ | `reason` | Meaning | Fix |
243
+ | :--- | :--- | :--- |
244
+ | `sensitive-file` | `.env`, `*.pem`, `.npmrc`, `.ssh/`, `.tfstate`, … | `--include-sensitive-files` if you really want it. |
245
+ | `sensitive-path` | The path itself matched a secret rule, so the file is skipped and its path is masked in every artifact. | Rename the file; a path that looks like a key is treated as one. |
246
+ | `binary` | Not text: it contains a NUL byte outside a UTF-16 stream, or is more than 1% control characters. The `detail` names the file size and the reason — including the offset of the first NUL when that byte sits past the 8 MB head scan. | Nothing to do. A UTF-16 file is *not* binary — it is transcoded and reported with `encoding`, not skipped. |
247
+ | `default-ignore` | A built-in rule, the preset's ecosystem ignores (`node_modules`, `__pycache__`, `target`, `vendor`, …), or the output directory. | A preset's ignores are not configurable; a custom `excludePatterns` can only remove more. |
248
+ | `excluded` | Your own `excludePatterns`, or a path outside a non-empty `include` allow-list. | Remove the pattern, or widen `include`. |
249
+ | `symlink` | Links are not followed (a directory link names its target in `detail`). | Snapshot the target. |
250
+ | `outside-root` | A link whose target resolves outside the scanned root. | Keep the target inside the repository, or snapshot it directly. |
251
+ | `gitignore` | A `.gitignore` excluded it, in a scan that is not reading git's index. | Remove the entry, or add the path to `include`. |
252
+ | `read-error` | Unreadable file. | Check permissions. |
253
+ | `not-file` | A directory, socket, FIFO. | Expected. |
254
+ | `nested-repository` | A nested checkout or submodule — a directory tracked by a different repository. | Expected; snapshot that repository separately if you need its code. |
255
+
256
+ Inside a git repository the candidate list comes from git, which applies `.gitignore` before
257
+ the scanner runs; those ignored paths are listed separately and recorded too, so a `.gitignore`d
258
+ file or directory appears here with reason `gitignore` in either mode.
259
+
260
+ A skipped directory is recorded, but the files inside it are never descended to and so have no
261
+ record of their own. For those, `explain` reports the **nearest skipped ancestor** and names it
262
+ in `detail`, whatever the reason — including built-in ignores:
263
+
264
+ ```
265
+ node_modules/dep/index.js
266
+ included no
267
+ reason default-ignore
268
+ detail built-in ignore: directory "node_modules" (not descended) — inherited from the skipped directory `node_modules`
269
+ ```
270
+
271
+ The same applies to a `.gitignore`d tree, where `detail` reads
272
+ ``ignored by .gitignore — inherited from the skipped directory `ignored` ``.
273
+
274
+ `explain` can report two more, which never appear in `skipped[]` because they are about the
275
+ path rather than about a candidate file:
276
+
277
+ | `reason` | Meaning | Fix |
278
+ | :--- | :--- | :--- |
279
+ | `missing` | `no such file or directory, relative to the configured cwd` — nothing is at that path. | Check the path and `--cwd`. |
280
+ | `not-discovered` | `exists but is not tracked or is otherwise not a candidate` — the path exists but nothing accounts for it: no rule, glob or skipped ancestor explains it. The tool's own output directory is pruned this way, so a file under `snapshot/` reports it. | If it sits in a skipped directory you will normally get that directory's reason instead; otherwise copy it into the repository, since `include` cannot rescue a pruned directory. |
281
+
282
+ Also check `include`: if it is non-empty it acts as an allow-list, and everything else is
283
+ excluded.
284
+
285
+ ## The snapshot is too big
286
+
287
+ - Lower the budget: `--max-part-bytes 120k`.
288
+ - Exclude whole trees: `excludePatterns: ["vendor/**", "**/*.min.js"]` in the config.
289
+ - Per-layer budgets: `budgets: { default: "120k", byLayer: { docs: 40000 } }`.
290
+ - Skip artifacts you do not read: `--skip-artifacts navigation,projectTree,audit`.
291
+ - Large files are already previewed rather than inlined; tune with `--large-file-bytes`.
292
+
293
+ ## Warnings that are working as intended
294
+
295
+ These come from `generate`. They are recorded in `manifest.warnings` and repeated on stderr
296
+ under a `- Warnings: N` line in the summary, and none of them stops a run:
297
+
298
+ - `manifest.json is disabled: \`validate\` and \`report\` cannot inspect this snapshot.` — you
299
+ used `--skip-artifacts manifest`.
300
+ - `parts are disabled but navigation.md is enabled: its links point at part files that were not written.`
301
+ - `every artifact is disabled: only the .codebase-snapshot ownership marker will be written.`
302
+ - A `hooks.afterGenerate` error is downgraded to a warning: the snapshot it was called for is
303
+ already written and is not lost.
304
+ - `N file(s) exceed 2 MB and were not parsed for a structural summary: …` — the named files are
305
+ emitted as head/tail previews without an `imports`/`exports` summary. Parsing them costs several
306
+ times their own size in memory, which is how a single 50 MB source file used to reach 3.3 GB of
307
+ resident memory. Nothing to fix; raise the part budget if the preview is too small.
308
+ - `N file(s) are UTF-16LE and were transcoded to UTF-8: …` — those files were UTF-16 and are
309
+ now ordinary text in the snapshot. They are listed in `manifest.files[]` with
310
+ `encoding: "utf-16le"`, or `"utf-16be"`. Nothing to fix; the warning exists because the bytes
311
+ in the artifact are not the bytes on disk.
312
+ - `N file(s) are not valid UTF-8; M undecodable byte(s) became U+FFFD: …` — the named files are
313
+ not UTF-8 at all (Latin-1 is the usual cause). They are kept, with `encoding: "utf-8-lossy"`,
314
+ but the undecodable spans are substituted, so **do not hand the emitted body to a model as if
315
+ it were the source**. Convert the file to UTF-8 (`iconv -f WINDOWS-1252 -t UTF-8 file > file.tmp`)
316
+ and regenerate. The snapshot still validates as current, because the hash covers the raw bytes.
317
+
318
+ ## Redactions in the summary are not warnings
319
+
320
+ If your run printed something like
321
+
322
+ ```
323
+ - Warnings: 0
324
+ - Redacted: 78 line(s) — values masked; see snapshot/reports/audit.md
325
+ ```
326
+
327
+ then nothing is wrong. That line counts **lines whose values were masked**, once per line even
328
+ when two rules matched the same line, and the per-line detail — file, line, every rule that
329
+ fired — is the table in `reports/audit.md` (section 1). The same number is
330
+ `manifest.totals.redactedLines`.
331
+
332
+ Three things surprise people about it, and all three are the intended trade-off:
333
+
334
+ - **`.env.example` and docs templates are masked too.** A placeholder such as
335
+ `JWT_SECRET=replace-with-base64url-48-bytes` is masked exactly like a live key, because the
336
+ rule cannot tell them apart — guessing which values are throwaway is how a real key escapes.
337
+ The template is therefore less readable in the snapshot than on disk. Narrow it with
338
+ `customPatterns` and `disableDefaultPatterns` (see
339
+ [Configuration](configuration.md#secrets-redaction)).
340
+ - **Test fixtures count.** `process.env.DB_SECRET = "test-value"` is a credential assignment by
341
+ shape, so it is masked and counted.
342
+ - **A long list is not a truncated list.** The `Warnings:` preview stops at ten rows and prints
343
+ `… and N more in manifest.warnings[]`; redactions are never previewed in the summary at all —
344
+ the count is stated and the table is the list.
345
+
346
+ If `reports/audit.md` was disabled with `--skip-artifacts audit`, the manifest keeps the count and
347
+ warns that the per-line detail was not written, rather than dropping it silently.
348
+
349
+ `validate` produces its own warnings, printed with a `⚠ ` prefix and never written to the
350
+ manifest, because they describe the state of the repository rather than the snapshot:
351
+
352
+ - `⚠ Working tree has uncommitted changes.`
353
+ - `⚠ Snapshot was generated on branch "zeta" but the configured branch is "main".` — the
354
+ branch moved since the snapshot was written.
355
+
356
+ ## Detached HEAD
357
+
358
+ On a detached HEAD (normal in CI) `manifest.git.branch` records the configured branch —
359
+ `main` by default — because a detached HEAD cannot name one. Set `branch` in the config if
360
+ your CI convention differs. This is not a warning condition.
361
+
362
+ ## Not a git repository
363
+
364
+ Outside a repository the tool does not fail; it records what it can:
365
+
366
+ ```json
367
+ { "branch": "main", "commit": "unknown", "isDirty": false }
368
+ ```
369
+
370
+ `branch` is the configured branch, `commit` is `"unknown"`, and `isDirty` is `false` because
371
+ there is no tree to compare — not because the tree is clean. Read `commit` when you need to
372
+ tell "no repository" from "clean repository", and do not treat `isDirty: false` alone as
373
+ evidence that the snapshot was taken from committed state.
374
+
375
+ ## Still stuck
376
+
377
+ ```bash
378
+ npx codebase-snapshot describe --format json > contract.json
379
+ npx codebase-snapshot generate --dry-run --format json > dry-run.json
380
+ ```
381
+
382
+ The first is the machine-readable contract, including a troubleshooting table. The second
383
+ computes a full snapshot without writing anything, so you can inspect
384
+ `config`, `counts`, `skipped` and `warnings` before committing to a run.
@@ -0,0 +1,66 @@
1
+ # codebase-snapshot vs code2prompt
2
+
3
+ `code2prompt` is a Rust CLI that renders a codebase through a Handlebars template into a prompt. The
4
+ templating is the feature — it decides the shape of what you get. This tool decides the *size and
5
+ the freshness* of what you get, and leaves the shape to a manifest. They are close to opposites,
6
+ which makes choosing between them straightforward.
7
+
8
+ > Written by the author of `codebase-snapshot`. code2prompt is a separate project; the summary
9
+ > reflects its commonly documented behaviour at the time of writing rather than a benchmark.
10
+
11
+ ## The short version
12
+
13
+ **code2prompt** — you write a template, it renders your repository into it. Output shape is entirely
14
+ yours.
15
+
16
+ **codebase-snapshot** — output shape is fixed and predictable: budgeted Markdown parts, plus a JSON
17
+ manifest with hashes. You tune what goes in, not what the page looks like.
18
+
19
+ If the prompt format is the thing you care about: **use code2prompt.**
20
+
21
+ ## Side by side
22
+
23
+ | | codebase-snapshot | code2prompt |
24
+ | :--- | :--- | :--- |
25
+ | Output shape | Fixed: parts + `manifest.json` + reports | **Yours** — Handlebars template |
26
+ | Custom prompt scaffolding | No — that is out of scope | **Yes**, the core feature |
27
+ | Hard size ceiling | **Yes** — `maxPartBytes` | Not to this project's knowledge |
28
+ | Staleness check | `validate` exits non-zero on drift | Not a feature |
29
+ | Reproducible output | Byte-identical parts, with a fingerprint | Template-driven; no determinism guarantee |
30
+ | Distribution | Node.js, npm | Single Rust binary, no runtime |
31
+ | Machine index | `manifest.json` with hashes and skip reasons | Template output only |
32
+ | Language awareness | 25 presets, AST/structural/heuristic summaries | Template access to file contents |
33
+ | Secret handling | 30 defaults, value-span masking, audit table | Ignore rules |
34
+
35
+ ## Where code2prompt is the better choice
36
+
37
+ - **You need a specific prompt structure** — a system preamble, per-file headers, a task framing.
38
+ Templates are the right abstraction and this tool has no answer to them.
39
+ - **You want a single binary with no runtime.** Rust means no Node install on the target machine.
40
+ - **You are generating prompts, not context artifacts.** If the output is a prompt, you want a
41
+ templating engine.
42
+
43
+ ## Where this is the better choice
44
+
45
+ - **You need the output to fit a budget.** `maxPartBytes` is a ceiling: an oversized file is
46
+ truncated with an omission marker naming the budget rather than silently blowing the limit.
47
+ - **You need to detect staleness.** `validate` compares every file hash plus a digest of the
48
+ generation settings, so a changed budget counts as drift. Prompt generators generally have no
49
+ concept of a snapshot that can expire.
50
+ - **You need reproducibility.** Frozen output means the artifact can be diffed in review or cached by
51
+ hash — not just regenerated and hoped over.
52
+ - **You need to explain exclusions.** `manifest.skipped[]` names a reason for every path left out;
53
+ `explain <path>` answers for one.
54
+ - **You need an agent to run it unaided.** `describe --format json` is the whole contract as data.
55
+
56
+ ## Both at once
57
+
58
+ Legitimate and common: generate the parts with this tool, then reference those parts from a template
59
+ you render yourself. The two are not competing for the same slot — one produces a payload, the other
60
+ produces a prompt that might contain it.
61
+
62
+ ## Honest summary
63
+
64
+ If your problem is *"my prompt is not structured the way I want"*, code2prompt solves it and this
65
+ tool does not. If your problem is *"my context does not fit, and I cannot tell whether it is still
66
+ accurate"*, that is this tool's entire reason to exist.
@@ -0,0 +1,69 @@
1
+ # codebase-snapshot vs gitingest
2
+
3
+ `gitingest` is the fastest path from *a GitHub URL* to *text in your clipboard*: paste a link on a
4
+ web page and get a digest back, with no install at all. This page says where that is better than
5
+ this tool, and where it is not.
6
+
7
+ > Written by the author of `codebase-snapshot`. gitingest is a separate project; the summary below
8
+ > reflects how it is commonly used at the time of writing rather than a benchmark of it.
9
+
10
+ ## The short version
11
+
12
+ **gitingest** is a web service plus a Python package that turns a repository into a single digest
13
+ you can paste into a prompt. Its great strength is that it needs nothing installed and no thought.
14
+
15
+ **codebase-snapshot** runs locally, writes partitioned output under a hard byte budget, and can tell
16
+ you later whether the snapshot still matches the code.
17
+
18
+ If you want to paste a repo into a chat right now: **use gitingest.**
19
+
20
+ ## Side by side
21
+
22
+ | | codebase-snapshot | gitingest |
23
+ | :--- | :--- | :--- |
24
+ | Getting started | `npx codebase-snapshot setup` (Node.js ≥ 18) | Open a website and paste a URL |
25
+ | Install required | Yes | **No**, for the hosted version |
26
+ | Works offline | **Yes** — local filesystem only | The hosted flow needs the network |
27
+ | Output shape | Parts under a hard byte budget, plus a JSON manifest | One digest |
28
+ | Hard size ceiling | **Yes** — `maxPartBytes` | Not to this project's knowledge |
29
+ | Staleness check | `validate` exits non-zero on drift | Not a feature |
30
+ | Reproducible output | Byte-identical parts across runs, with a fingerprint | Not a published guarantee |
31
+ | Excluded-file accounting | Every skipped path recorded with a reason | Ignore rules applied |
32
+ | Secret redaction | 30 default rules, value-span masking, audit table | Relies on ignore rules |
33
+ | Runtime | Node.js | Python |
34
+ | Repo URL input | No — needs a checkout | **Yes** |
35
+
36
+ ## Where gitingest is the better choice
37
+
38
+ - **You have not installed anything and do not want to.** Nothing here competes with a URL box.
39
+ - **You want one digest, not parts.** If the destination is a chat window, one blob is the right
40
+ shape.
41
+ - **You are in Python.** `pip install gitingest` fits an existing pipeline without adding a Node
42
+ runtime.
43
+ - **You are exploring.** Pointing at a repository you have never seen is what it is for.
44
+
45
+ ## Where this is the better choice
46
+
47
+ - **The repository must not leave your machine.** This tool never makes a network request; it reads
48
+ the local tree. If the code is private enough that a hosted digest is not allowed, that settles it.
49
+ - **You need a size guarantee.** A part either fits the budget or carries an explicit truncation
50
+ marker naming the budget. A digest that grows with the repository eventually stops fitting, and
51
+ discovering that at ingestion time is too late.
52
+ - **You need to prove the context is current.** `validate` re-hashes the tree and compares a digest
53
+ of the generation settings, so a stale snapshot fails instead of quietly misleading a model.
54
+ - **You need the exclusions explained.** Every skipped path carries a reason — `binary`, `gitignore`,
55
+ `sensitive-file`, `nested-repository`, `read-error` and so on — and `explain <path>` answers for
56
+ any single file.
57
+ - **You need automation.** Exit codes, a machine-readable contract and a manifest make this
58
+ gate-able; a copy-paste digest is not.
59
+
60
+ ## Both at once
61
+
62
+ They compose naturally: use gitingest to look at an unfamiliar repository quickly, and this tool
63
+ once the repository is one you maintain and want context generated reproducibly.
64
+
65
+ ## Honest summary
66
+
67
+ For a one-off look at someone else's repository, gitingest is better and always will be — it wins
68
+ on friction, and friction is what most people are short of. This tool is for codebases you own and
69
+ pipelines you maintain, where a ceiling, a drift check and an audit trail are the point.
@@ -0,0 +1,83 @@
1
+ # codebase-snapshot vs repomix
2
+
3
+ Both tools answer the same question — *how do I hand a whole repository to a model that cannot
4
+ clone it?* They disagree about almost everything else. This page states where each one is the
5
+ better choice, including where repomix clearly is.
6
+
7
+ > Written by the author of `codebase-snapshot`. Numbers are the maintained project's own claims at
8
+ > the time of writing, and the comparison is deliberately unkind to this project where it deserves
9
+ > it.
10
+
11
+ ## The short version
12
+
13
+ **repomix** packs a repository into one AI-friendly file. It is the established tool in this space
14
+ by a wide margin — tens of thousands of stars, an MCP server, remote repository URLs, and an
15
+ ecosystem of integrations.
16
+
17
+ **codebase-snapshot** produces *size-budgeted parts* rather than one file, and treats the snapshot
18
+ as something that can go stale and be proven current. Its distinguishing features are a hard byte
19
+ ceiling per part, deterministic output, and `validate`.
20
+
21
+ If you want the most popular, most integrated tool: **use repomix.**
22
+
23
+ ## Side by side
24
+
25
+ | | codebase-snapshot | repomix |
26
+ | :--- | :--- | :--- |
27
+ | Output shape | Multiple parts, each under a hard byte budget, plus a JSON manifest | A single packed file (XML, Markdown or JSON) |
28
+ | Hard size ceiling | **Yes** — `maxPartBytes` is a ceiling, not a target | Token counting and warnings; no per-artifact ceiling |
29
+ | Reproducible output | Parts are byte-identical across runs; a fingerprint covers them | Content is stable; no published determinism guarantee |
30
+ | Staleness check | `validate` re-hashes the repository and exits non-zero on drift | Not a feature |
31
+ | Remote repositories | No — operates on a local checkout | **Yes** — packs a GitHub URL directly |
32
+ | MCP server | No (planned) | **Yes** |
33
+ | Secret handling | 30 default rules, value-span masking, per-line audit table | Secretlint integration |
34
+ | Language awareness | 25 presets, AST or structural summaries labelled by confidence | Tree-sitter parsing with a compression mode |
35
+ | Adoption | New | **Mature** — large user base, many integrations |
36
+ | Runtime | Node.js ≥ 18, one dependency | Node.js, npm package |
37
+
38
+ ## Where repomix is the better choice
39
+
40
+ - **You want one file.** Pasting a single artifact into a chat is simpler than assembling parts, and
41
+ repomix is built around that workflow.
42
+ - **You need a remote URL.** `repomix --remote https://github.com/owner/repo` works without a clone.
43
+ This project has no equivalent.
44
+ - **You want MCP.** repomix ships an MCP server today; this project does not, yet.
45
+ - **You want the ecosystem.** More stars, more tutorials, more people who have already hit your
46
+ problem.
47
+ - **You are packing something enormous and prefer compression to budgets.** repomix's tree-sitter
48
+ compression mode is a different bet — shrink the content — and on very large repositories it may
49
+ suit you better than partitioning.
50
+
51
+ ## Where this is the better choice
52
+
53
+ - **You need a guarantee, not a best effort.** `maxPartBytes` promises no part exceeds the ceiling;
54
+ a file too large is truncated with an explicit marker naming the budget. If a downstream system
55
+ rejects payloads above a size, a warning is not enough.
56
+ - **You need to know the snapshot still matches the code.** `validate` compares every file hash
57
+ *and* a digest of the settings the snapshot was generated with, so a changed budget or secret
58
+ pattern counts as drift. That makes "is my context current?" a CI-gateable question.
59
+ - **You need byte-identical output.** Two runs over unchanged input produce identical `parts/`, so a
60
+ snapshot can live in review, be diffed, or be cached by hash.
61
+ - **You need a machine index.** `manifest.json` records every included file *and every excluded file
62
+ with a reason*, which is what makes `explain <path>` able to answer "why is this not in here?".
63
+ - **You want the tool to be drivable by an agent without documentation.** `describe --format json`
64
+ emits the whole contract, and `skill --install` writes a skill into the registries agent tools
65
+ read.
66
+
67
+ ## Both at once
68
+
69
+ There is no conflict. A common arrangement is to use this tool for the *budgeted, validated* case
70
+ (CI, RAG ingestion, anything automated) and repomix when a human wants one file to paste into a
71
+ chat. They read the same repository and write to different directories.
72
+
73
+ ```bash
74
+ npx codebase-snapshot setup # config, scripts, dependency, install, generate
75
+ npx codebase-snapshot validate # exit 0 means the snapshot matches the disk
76
+ ```
77
+
78
+ ## Honest summary
79
+
80
+ repomix is the safer recommendation for most people today, because maturity and integrations matter
81
+ more than any single feature. This tool exists for the narrower case where a size ceiling, a drift
82
+ check and reproducibility are requirements rather than nice-to-haves — and for the case where an
83
+ agent has to operate it without a human reading the docs first.