@ankurhalder/codebase-snapshot 2.2.0 → 4.0.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.
- package/README.md +161 -309
- package/dist/bin.js +1946 -577
- package/dist/{chunk-5I564BGX.js → chunk-FM4OOQPF.js} +1558 -390
- package/dist/cli.cjs +1914 -544
- package/dist/cli.d.cts +3 -1
- package/dist/cli.d.ts +3 -1
- package/dist/cli.js +272 -71
- package/dist/index.cjs +1557 -387
- package/dist/index.d.cts +22 -28
- package/dist/index.d.ts +22 -28
- package/dist/index.js +1 -1
- package/dist/{types-CJqkePM4.d.cts → types-BdpT_4b8.d.cts} +37 -4
- package/dist/{types-CJqkePM4.d.ts → types-BdpT_4b8.d.ts} +37 -4
- package/docs/agent.md +78 -38
- package/docs/cli.md +315 -0
- package/docs/configuration.md +254 -226
- package/docs/install.md +115 -84
- package/docs/output.md +213 -0
- package/docs/troubleshooting.md +277 -0
- package/package.json +5 -3
package/docs/install.md
CHANGED
|
@@ -1,39 +1,33 @@
|
|
|
1
1
|
# Installation
|
|
2
2
|
|
|
3
|
-
`@ankurhalder/codebase-snapshot` is published to the public npm registry, so it installs
|
|
4
|
-
|
|
5
|
-
registry to add.
|
|
3
|
+
`@ankurhalder/codebase-snapshot` is published to the public npm registry, so it installs with
|
|
4
|
+
**any** npm-compatible client. There is no second registry to add and nothing to configure.
|
|
6
5
|
|
|
7
6
|
Requires **Node.js 18 or newer**.
|
|
8
7
|
|
|
9
|
-
## One-off use
|
|
8
|
+
## One-off use
|
|
10
9
|
|
|
11
10
|
```bash
|
|
12
11
|
npx @ankurhalder/codebase-snapshot generate
|
|
13
12
|
```
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
This is the shortest path and needs no install. The command is also available as
|
|
15
|
+
`codebase-snapshot` and, once installed, as the short alias `snap`.
|
|
16
|
+
|
|
17
|
+
Pin the version when you need the same build on every machine:
|
|
19
18
|
|
|
20
19
|
```bash
|
|
21
20
|
npx @ankurhalder/codebase-snapshot@latest generate
|
|
22
|
-
npx @ankurhalder/codebase-snapshot@
|
|
21
|
+
npx @ankurhalder/codebase-snapshot@4 generate
|
|
23
22
|
```
|
|
24
23
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
Equivalent one-liners for other clients:
|
|
24
|
+
**Why the bare name works.** npm resolves which binary to run from the package's `bin`
|
|
25
|
+
entries. This package declares `codebase-snapshot` and `snap` pointing at the *same file*, so
|
|
26
|
+
npm treats them as aliases and can always resolve one. Keep them aliases: two *different*
|
|
27
|
+
executables under a scoped package name would make the bare form unresolvable.
|
|
30
28
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
yarn global add @ankurhalder/codebase-snapshot # yarn classic (installs globally)
|
|
34
|
-
pnpm dlx @ankurhalder/codebase-snapshot generate
|
|
35
|
-
bunx @ankurhalder/codebase-snapshot generate
|
|
36
|
-
```
|
|
29
|
+
If a command exits with no output at all, a stale global install is the usual cause — see
|
|
30
|
+
[Troubleshooting](#troubleshooting).
|
|
37
31
|
|
|
38
32
|
## Add to a project
|
|
39
33
|
|
|
@@ -44,18 +38,17 @@ pnpm add -D @ankurhalder/codebase-snapshot
|
|
|
44
38
|
bun add -d @ankurhalder/codebase-snapshot
|
|
45
39
|
```
|
|
46
40
|
|
|
47
|
-
Then run it through your client so
|
|
41
|
+
Then run it through your client so the binary resolves from the project:
|
|
48
42
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
43
|
+
| Client | Run |
|
|
44
|
+
| :--- | :--- |
|
|
45
|
+
| npm | `npx codebase-snapshot generate` |
|
|
46
|
+
| yarn (classic and Berry) | `yarn codebase-snapshot generate` |
|
|
47
|
+
| pnpm | `pnpm exec codebase-snapshot generate` |
|
|
48
|
+
| bun | `bun run codebase-snapshot generate` |
|
|
55
49
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
Adding it to `package.json` scripts works the same everywhere:
|
|
50
|
+
`snap` works everywhere `codebase-snapshot` does. Adding it to `package.json` scripts is the
|
|
51
|
+
most reproducible option:
|
|
59
52
|
|
|
60
53
|
```json
|
|
61
54
|
{
|
|
@@ -66,85 +59,123 @@ Adding it to `package.json` scripts works the same everywhere:
|
|
|
66
59
|
}
|
|
67
60
|
```
|
|
68
61
|
|
|
69
|
-
|
|
62
|
+
`codebase-snapshot init` writes these entries for you.
|
|
63
|
+
|
|
64
|
+
## One-off runners for other clients
|
|
70
65
|
|
|
71
66
|
```bash
|
|
72
|
-
|
|
73
|
-
pnpm
|
|
74
|
-
|
|
67
|
+
yarn dlx -p @ankurhalder/codebase-snapshot codebase-snapshot generate # yarn Berry
|
|
68
|
+
pnpm dlx @ankurhalder/codebase-snapshot generate
|
|
69
|
+
bunx @ankurhalder/codebase-snapshot generate
|
|
75
70
|
```
|
|
76
71
|
|
|
77
|
-
|
|
78
|
-
`-D`/`--save-dev` examples above) is the better default: snapshots are only reproducible
|
|
79
|
-
if the generator version is reproducible.
|
|
72
|
+
## Agent skill
|
|
80
73
|
|
|
81
|
-
|
|
74
|
+
To make a repository discoverable by an AI agent — so it finds this tool without being told
|
|
75
|
+
it exists — run:
|
|
82
76
|
|
|
83
77
|
```bash
|
|
84
|
-
npx @ankurhalder/codebase-snapshot
|
|
85
|
-
pnpm dlx @ankurhalder/codebase-snapshot@2.2.0 generate
|
|
78
|
+
npx @ankurhalder/codebase-snapshot skill --install
|
|
86
79
|
```
|
|
87
80
|
|
|
81
|
+
That writes an `AGENTS.md` section, `.claude/skills/codebase-snapshot/SKILL.md` and
|
|
82
|
+
`.cursor/rules/codebase-snapshot.mdc`. Re-running only replaces its own marked block. See
|
|
83
|
+
[Using this with an AI agent](agent.md).
|
|
84
|
+
|
|
88
85
|
## Verified clients
|
|
89
86
|
|
|
90
|
-
Every client below
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
the build rather than a user's terminal.
|
|
87
|
+
Every client below installs the published package and runs it successfully; the release check
|
|
88
|
+
enforces this on every pull request, so a change that breaks one fails the build rather than
|
|
89
|
+
a user's terminal.
|
|
94
90
|
|
|
95
|
-
| Client |
|
|
96
|
-
| :--- | :--- |
|
|
97
|
-
| npm
|
|
98
|
-
| yarn
|
|
99
|
-
| yarn
|
|
100
|
-
| pnpm
|
|
101
|
-
| bun
|
|
91
|
+
| Client | Run installed binary with |
|
|
92
|
+
| :--- | :--- |
|
|
93
|
+
| npm 10–11 | `npx codebase-snapshot` |
|
|
94
|
+
| yarn classic 1.22 | `yarn codebase-snapshot` |
|
|
95
|
+
| yarn Berry 4.6 (`nodeLinker: pnp`) | `yarn codebase-snapshot` |
|
|
96
|
+
| pnpm 10 | `pnpm exec codebase-snapshot` |
|
|
97
|
+
| bun 1.4 | `bun run codebase-snapshot` |
|
|
102
98
|
|
|
103
|
-
Yarn Berry's default **strict PnP**
|
|
104
|
-
|
|
99
|
+
Yarn Berry's default **strict PnP** works: there is no `node_modules` directory, and the
|
|
100
|
+
runtime `typescript` dependency still resolves through yarn's `.pnp.cjs` loader.
|
|
105
101
|
|
|
106
|
-
##
|
|
102
|
+
## Before scanning a repository you do not own
|
|
107
103
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
104
|
+
A `snapshot.config.ts`, `.js`, `.mjs` or `.cjs` in the target repository is **imported and
|
|
105
|
+
executed** when the tool runs there — that is what lets a config express a `RegExp` or a
|
|
106
|
+
`match` function. Pointing `--cwd` at a checkout you do not trust therefore runs its code.
|
|
107
|
+
`--no-config` ignores every config file and uses the built-in defaults plus the detected
|
|
108
|
+
preset, and JSON configs are parsed rather than executed. See
|
|
109
|
+
[the trust boundary](../SECURITY.md#trust-boundary).
|
|
111
110
|
|
|
112
|
-
##
|
|
111
|
+
## Global install
|
|
113
112
|
|
|
114
|
-
|
|
115
|
-
|
|
113
|
+
```bash
|
|
114
|
+
npm install -g @ankurhalder/codebase-snapshot
|
|
115
|
+
```
|
|
116
116
|
|
|
117
|
-
|
|
117
|
+
Convenient, but prefer pinning per project: snapshots are only reproducible if the generator
|
|
118
|
+
version is. A global install also shadows `npx` for this package — npm will run your global
|
|
119
|
+
copy instead of the published one, which is confusing when the two differ. That is exactly
|
|
120
|
+
the situation described under [Troubleshooting](#troubleshooting).
|
|
118
121
|
|
|
119
|
-
|
|
120
|
-
prebuilt executable rather than JavaScript, and are tracked as future work in
|
|
121
|
-
[docs/plans/distribution](../plans/distribution/README.md). Until then, any machine
|
|
122
|
-
running the tool needs Node 18+.
|
|
123
|
-
- **Deno.** The API can be imported under Deno's npm compatibility, but running the CLI
|
|
124
|
-
needs a `./bin` subpath export that is not published yet.
|
|
122
|
+
## Workspaces
|
|
125
123
|
|
|
126
|
-
|
|
124
|
+
The binary resolves from the workspace it is installed in. In a monorepo, install it in the
|
|
125
|
+
package that runs the snapshot (usually the root), or use a one-off runner
|
|
126
|
+
(`npx` / `pnpm dlx` / `yarn dlx` / `bunx`) which works from any subdirectory.
|
|
127
127
|
|
|
128
|
-
|
|
129
|
-
in `node_modules/.bin` and is only on your `PATH` inside a package script or a client
|
|
130
|
-
runner. Use `npx snap`, `pnpm exec snap`, `yarn snap`, or `bun run snap`.
|
|
128
|
+
## Upgrading
|
|
131
129
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
130
|
+
```bash
|
|
131
|
+
npm install -D @ankurhalder/codebase-snapshot@latest
|
|
132
|
+
npx codebase-snapshot generate # rewrite the snapshot with the new version
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
`manifest.generator.version` records which version produced a snapshot. Re-run `generate`
|
|
136
|
+
after an upgrade so the snapshot, and the version it records, match the build you are now
|
|
137
|
+
running.
|
|
135
138
|
|
|
136
|
-
|
|
137
|
-
|
|
139
|
+
A snapshot written by **3.0.0 or earlier** is refused rather than replaced: its
|
|
140
|
+
`.codebase-snapshot` marker has no per-snapshot id, so the current build cannot prove it wrote
|
|
141
|
+
the directory. Delete the old snapshot directory and generate again.
|
|
138
142
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
+
See [CHANGELOG](../CHANGELOG.md) for breaking changes — 3.0.0 removed nothing, but it briefly
|
|
144
|
+
did in error; 4.0.0 restored the `snap` alias.
|
|
145
|
+
|
|
146
|
+
## Uninstalling
|
|
147
|
+
|
|
148
|
+
Standard for your client (`npm uninstall`, `yarn remove`, `pnpm remove`, `bun remove`).
|
|
149
|
+
Snapshots already written to the output directory are ordinary files and are left alone.
|
|
150
|
+
If you installed globally, `npm rm -g @ankurhalder/codebase-snapshot` also removes the
|
|
151
|
+
shim that shadows `npx`.
|
|
152
|
+
|
|
153
|
+
## Troubleshooting
|
|
154
|
+
|
|
155
|
+
**`codebase-snapshot: command not found`** — when installed locally (not globally) the binary
|
|
156
|
+
lives in `node_modules/.bin` and is only on `PATH` inside a package script or a client
|
|
157
|
+
runner. Use `npx codebase-snapshot`, `pnpm exec codebase-snapshot`,
|
|
158
|
+
`yarn codebase-snapshot` or `bun run codebase-snapshot`.
|
|
159
|
+
|
|
160
|
+
**A command exits 0 and prints nothing** — almost always a stale **global** install
|
|
161
|
+
shadowing `npx`. Check and remove it:
|
|
143
162
|
|
|
144
163
|
```bash
|
|
145
|
-
|
|
146
|
-
|
|
164
|
+
npm ls -g --depth=0 | grep codebase-snapshot
|
|
165
|
+
npm rm -g @ankurhalder/codebase-snapshot
|
|
147
166
|
```
|
|
148
167
|
|
|
149
|
-
|
|
150
|
-
|
|
168
|
+
Then re-run. If the package is not installed globally, clear the npx cache
|
|
169
|
+
(`npm cache clean --force`) and retry.
|
|
170
|
+
|
|
171
|
+
**`Cannot find module 'typescript'`** — the package declares `typescript` as a runtime
|
|
172
|
+
dependency for AST analysis. If your client prunes it (some `--ignore-scripts` CI setups do),
|
|
173
|
+
rerun a clean install or pin `typescript@^5` yourself.
|
|
174
|
+
|
|
175
|
+
**`yarn codebase-snapshot` prints extra lines / breaks a script** — yarn classic wraps
|
|
176
|
+
everything: it echoes `yarn run 1.22.x`, the resolved command, your output, then
|
|
177
|
+
`Done in 0.37s.`. For anything that parses the output, call the binary directly
|
|
178
|
+
(`./node_modules/.bin/codebase-snapshot` or `npx codebase-snapshot`).
|
|
179
|
+
|
|
180
|
+
More symptoms — refusals, config errors, drift, missing files — are in
|
|
181
|
+
[Troubleshooting](troubleshooting.md).
|
package/docs/output.md
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# Snapshot output
|
|
2
|
+
|
|
3
|
+
A run writes one directory (default `./snapshot`) that is meant to be handed to a model as
|
|
4
|
+
context. It is self-describing: `manifest.json` indexes everything the parts contain and
|
|
5
|
+
explains everything they omit.
|
|
6
|
+
|
|
7
|
+
## Layout
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
snapshot/
|
|
11
|
+
├── .codebase-snapshot ownership marker (see Safety)
|
|
12
|
+
├── README.md human summary of this snapshot
|
|
13
|
+
├── manifest.json machine index: files, parts, config, hashes, skips
|
|
14
|
+
├── parts/
|
|
15
|
+
│ ├── part-001.md the code, grouped and size-budgeted
|
|
16
|
+
│ ├── part-002.md
|
|
17
|
+
│ └── …
|
|
18
|
+
└── reports/
|
|
19
|
+
├── project-tree.md directory tree of included files
|
|
20
|
+
├── navigation.md links between layers, kinds and parts
|
|
21
|
+
└── audit.md secrets, large files, parse diagnostics, git state
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
With `--include-monolith` you also get `full-codebase.json` and `full-codebase.md` — the
|
|
25
|
+
whole repository in one file. They are off by default because they grow without bound;
|
|
26
|
+
`--max-monolith-bytes` skips them when they would exceed a size you set.
|
|
27
|
+
|
|
28
|
+
Every artifact can be turned off. `--skip-artifacts parts` omits the parts,
|
|
29
|
+
`--only-artifacts manifest` writes nothing but the manifest, and `config.artifacts` does the
|
|
30
|
+
same persistently. The ownership marker is always written.
|
|
31
|
+
|
|
32
|
+
## Parts
|
|
33
|
+
|
|
34
|
+
Each `part-NNN.md` holds whole files under the byte budget (`maxPartBytes`, default 300 KB).
|
|
35
|
+
A file larger than the budget is not split mid-file: it is emitted as a head/tail preview and
|
|
36
|
+
marked as truncated. Each file section is tagged with its layer, kind, language and a
|
|
37
|
+
structural summary (imports, exports, classes, functions) so a reader can navigate without
|
|
38
|
+
opening every file.
|
|
39
|
+
|
|
40
|
+
Parts are packed in a stable order, so the same input produces the same output — which is
|
|
41
|
+
what makes `manifest.fingerprint` meaningful.
|
|
42
|
+
|
|
43
|
+
## Manifest
|
|
44
|
+
|
|
45
|
+
`manifest.json` is the index and the contract between runs. Top-level keys:
|
|
46
|
+
|
|
47
|
+
| Key | Contents |
|
|
48
|
+
| :--- | :--- |
|
|
49
|
+
| `schemaVersion` | Manifest format version (`3.0.0`). |
|
|
50
|
+
| `generatedAt` | ISO timestamp of the run. |
|
|
51
|
+
| `generator` | `{ name, version }` of the tool that wrote it. |
|
|
52
|
+
| `git` | `{ branch, commit, isDirty }`. `branch` falls back to the configured branch on a detached HEAD and outside a repository; `commit` is `"unknown"` and `isDirty` is `false` when git cannot report. A `false` there means "not known to be dirty", not "clean tree" — check `commit` to tell the two apart. |
|
|
53
|
+
| `budgets` | The effective byte limits for the run: `{ maxPartBytes, largeFileBytes }`. The per-layer overrides live at `config.budgets`, which is `{ default, byLayer }`. |
|
|
54
|
+
| `config` | The resolved configuration actually used (preset, artifacts, sizing, scoping). |
|
|
55
|
+
| `counts` | `{ totalFiles, totalSizeBytes, totalLines, partsCount, skippedCount, largeFileCount, truncatedPreviewCount }`. |
|
|
56
|
+
| `totals` | Bookkeeping: `{ candidateFiles, includedFiles, fullFiles, previewFiles, skippedFiles, totalSourceBytes, totalContentBytes, partCount, warningCount }`. |
|
|
57
|
+
| `fingerprint` | Stable hash of the snapshot content. Two runs over unchanged input produce the same value. |
|
|
58
|
+
| `validation` | `{ status: "passed" \| "failed", errors: [] }` for the run's own integrity check. |
|
|
59
|
+
| `artifacts` | The optional monolith files: `{ fullJson, fullMarkdown }`. Each value is `null` when that file was not written, otherwise `{ byteSize, sha256 }`. The object itself is always present. |
|
|
60
|
+
| `layers`, `kinds` | File counts per layer and per kind. |
|
|
61
|
+
| `indexes` | Cross-references: `routes`, `components`, `handlers`, `features`, `imports`, `directives`. |
|
|
62
|
+
| `parts` | One entry per part (below). |
|
|
63
|
+
| `files` | One entry per included file (below). |
|
|
64
|
+
| `skipped` | One entry per excluded file (below). |
|
|
65
|
+
| `warnings` | Non-fatal observations, including a throwing `hooks.afterGenerate`. |
|
|
66
|
+
|
|
67
|
+
### `parts[]`
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"index": 1,
|
|
72
|
+
"filename": "part-001.md",
|
|
73
|
+
"byteSize": 1432,
|
|
74
|
+
"sha256": "3472b9edcd504403f2416606e4222f326ec207d7916bf928ade4ec7de1406d2d",
|
|
75
|
+
"lines": 91,
|
|
76
|
+
"fileCount": 2,
|
|
77
|
+
"layersCovered": ["root", "src"],
|
|
78
|
+
"includedFiles": [
|
|
79
|
+
{ "relativePath": "package.json", "sourceBytes": 35, "includedBytes": 35, "lines": 1, "layer": "root", "kind": "data" },
|
|
80
|
+
{ "relativePath": "src/math.ts", "sourceBytes": 63, "includedBytes": 63, "lines": 3, "layer": "src", "kind": "other" }
|
|
81
|
+
]
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`byteSize` and `sha256` are what `validate` checks against the file on disk. Both are full
|
|
86
|
+
values — the hash is the whole 64-character digest, never elided.
|
|
87
|
+
|
|
88
|
+
### `files[]`
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"relativePath": "src/math.ts",
|
|
93
|
+
"sizeBytes": 63,
|
|
94
|
+
"lines": 3,
|
|
95
|
+
"hashSha256": "aa229260647049a42a0446f48f1b34f19657921b7a0a27a41c0b2a294a25c919",
|
|
96
|
+
"part": "part-001.md",
|
|
97
|
+
"layer": "src",
|
|
98
|
+
"kind": "other",
|
|
99
|
+
"language": "typescript",
|
|
100
|
+
"inclusionMode": "full",
|
|
101
|
+
"isLarge": false,
|
|
102
|
+
"isPreviewTruncated": false,
|
|
103
|
+
"originalLines": 3,
|
|
104
|
+
"includedLines": 3,
|
|
105
|
+
"includedBytes": 63,
|
|
106
|
+
"summary": {
|
|
107
|
+
"directives": [], "imports": [], "exports": ["add"], "reExports": [],
|
|
108
|
+
"functions": ["add"], "classes": [], "interfaces": [], "types": [],
|
|
109
|
+
"enums": [], "variables": [], "reactComponents": [], "hooks": [],
|
|
110
|
+
"routeHandlers": []
|
|
111
|
+
},
|
|
112
|
+
"summaryQuality": "ast"
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
- `isLarge` is a fact about the file: it is bigger than `largeFileBytes` (default 200 KB).
|
|
117
|
+
A large file is emitted as a head/tail excerpt rather than inlined whole.
|
|
118
|
+
- `isPreviewTruncated` is a fact about what was written: the body in this snapshot is **not**
|
|
119
|
+
the whole file, because lines were dropped.
|
|
120
|
+
- `inclusionMode` is `preview` exactly when `isPreviewTruncated` is `true`, and there are two
|
|
121
|
+
routes to it:
|
|
122
|
+
- **Over `largeFileBytes`** — the head/tail excerpt drops the middle, so
|
|
123
|
+
`isLarge: true`, `isPreviewTruncated: true`. If the head and tail happen to cover every
|
|
124
|
+
line, nothing is dropped and the entry stays `isLarge: true`, `inclusionMode: "full"`.
|
|
125
|
+
- **Under `largeFileBytes` but too big for its part** — the partitioner byte-truncates the
|
|
126
|
+
section to keep the part inside its budget, so `isLarge: false`,
|
|
127
|
+
`isPreviewTruncated: true`. This is the one that surprises people: the file is not "large"
|
|
128
|
+
by the configured threshold, yet its body is incomplete.
|
|
129
|
+
- Only `isPreviewTruncated` tells you content is missing; `isLarge` alone does not.
|
|
130
|
+
- `hashSha256` is the hash of the source file on disk — `validate` recomputes it to detect
|
|
131
|
+
drift. It is the full 64-character digest.
|
|
132
|
+
- `summary` and `summaryQuality` are **conditional**. Both keys are present for a language
|
|
133
|
+
with an analyser and both are absent for a shapeless one — a `.gitignore`, a `.txt`. Treat a
|
|
134
|
+
missing `summary` as "no structural information", not as an empty one.
|
|
135
|
+
- `summary` has one of three shapes. Code files get the thirteen keys above (`directives`,
|
|
136
|
+
`imports`, `exports`, `reExports`, `functions`, `classes`, `interfaces`, `types`, `enums`,
|
|
137
|
+
`variables`, `reactComponents`, `hooks`, `routeHandlers`), plus `parseErrors` when the parser
|
|
138
|
+
reported syntax errors. JSON gets `{ shape, itemCount, keys, sampleKeys }` and Markdown gets
|
|
139
|
+
`{ headings, frontmatterKeys }` — a shape description rather than a code summary.
|
|
140
|
+
- `summaryOmitted: true` means the file's summary was dropped because the summary alone would
|
|
141
|
+
not fit the part budget. The entry then has `summaryQuality` but no `summary`, and the
|
|
142
|
+
file's body is still emitted in full. It is only ever present when `true`.
|
|
143
|
+
- `summaryQuality` is `ast` (parsed by the TypeScript compiler), `structural` (JSON or
|
|
144
|
+
Markdown shape) or `heuristic` (pattern-matched). It tells you how much to trust the
|
|
145
|
+
summary: `heuristic` means a line scan, not a parse.
|
|
146
|
+
|
|
147
|
+
Querying this array is usually faster and cheaper than re-reading the parts.
|
|
148
|
+
|
|
149
|
+
### `skipped[]`
|
|
150
|
+
|
|
151
|
+
```json
|
|
152
|
+
{ "path": ".env", "reason": "sensitive-file", "detail": "credential-bearing file; enable includeSensitiveFiles to emit it" }
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Reasons, and what each one means:
|
|
156
|
+
|
|
157
|
+
| `reason` | The file was dropped because |
|
|
158
|
+
| :--- | :--- |
|
|
159
|
+
| `excluded` | It matched your own `excludePatterns`, or fell outside a non-empty `include` allow-list. |
|
|
160
|
+
| `default-ignore` | A built-in rule caught it, the preset's ecosystem ignores did (`node_modules`, `__pycache__`, `target`, …), or it sat inside an output directory. |
|
|
161
|
+
| `sensitive-file` | Its name is credential-bearing (`.env`, `*.pem`, `.npmrc`, …). |
|
|
162
|
+
| `sensitive-path` | The path itself matched a secret rule. The file is skipped and the path is masked in every artifact. |
|
|
163
|
+
| `binary` | It is not text. |
|
|
164
|
+
| `symlink` | It is a link, and links are not followed. A directory link names its target in `detail`. |
|
|
165
|
+
| `outside-root` | It is a link whose target resolves outside the scanned root. |
|
|
166
|
+
| `gitignore` | A `.gitignore` excluded it, in a scan that is not reading git's index. |
|
|
167
|
+
| `read-error` | It could not be read. |
|
|
168
|
+
| `not-file` | It is a directory, socket or FIFO. |
|
|
169
|
+
|
|
170
|
+
Every path the scanner discovers is recorded — nothing is dropped silently, in either discovery
|
|
171
|
+
mode. Inside a git repository the candidate list comes from git, which applies `.gitignore`
|
|
172
|
+
before the scanner runs; the ignored paths are then listed separately and recorded here too, so
|
|
173
|
+
a `.gitignore`d file or directory appears in `skipped[]` with reason `gitignore` whether or not
|
|
174
|
+
git is present.
|
|
175
|
+
|
|
176
|
+
`explain` reports two further reasons that never appear here, because they describe a path
|
|
177
|
+
rather than a decision about a candidate file: `missing` (nothing exists at that path) and
|
|
178
|
+
`not-discovered` (the path exists but was never a candidate — inside `node_modules`, for
|
|
179
|
+
example). See [Troubleshooting](troubleshooting.md#a-file-is-missing-from-the-snapshot).
|
|
180
|
+
|
|
181
|
+
## Safety
|
|
182
|
+
|
|
183
|
+
- **Sensitive files are excluded by default.** `.env`, `*.pem`, `*.key`, `.npmrc`, `.netrc`,
|
|
184
|
+
`.git-credentials`, `id_rsa`, `.kube/config`, `*.tfstate` and similar are skipped and
|
|
185
|
+
listed in `skipped[]`. `--include-sensitive-files` opts in.
|
|
186
|
+
- **Secret-shaped strings are redacted** inside ordinary source files. Only the matched span
|
|
187
|
+
is masked, never the whole line, and no excerpt of a matched line is written to any
|
|
188
|
+
artifact.
|
|
189
|
+
- **The output directory is owned by the tool.** Each run writes `.codebase-snapshot` into it
|
|
190
|
+
first, then swaps the new snapshot in atomically. A previous snapshot survives until the
|
|
191
|
+
replacement is complete.
|
|
192
|
+
- **The tool refuses to replace a directory it did not write.** Pointing `--out-dir` at
|
|
193
|
+
`src/` is refused with exit `1` and the directory is left untouched — a mistyped output
|
|
194
|
+
path cannot delete source code. Empty directories, directories containing only
|
|
195
|
+
`.gitkeep`/`.gitignore`/`.DS_Store`, and directories holding a complete snapshot this build
|
|
196
|
+
wrote are accepted.
|
|
197
|
+
- **Ownership is the marker, not a filename.** `.codebase-snapshot` is a JSON record carrying
|
|
198
|
+
this generator's name and `version`, the marker's `schemaVersion`, a `createdAt` timestamp,
|
|
199
|
+
a per-snapshot `id`, the run `state` (`staging` while it is being written, `complete` once it
|
|
200
|
+
is whole), the `manifestSha256` digest of the manifest it shipped, and `entries` — the
|
|
201
|
+
inventory of everything it wrote. A zero-byte marker, a marker that is not JSON, a marker
|
|
202
|
+
written by 3.0.0 (no `id`), a bare `manifest.json`, and a directory with files you added
|
|
203
|
+
after the run are all refused.
|
|
204
|
+
See [Troubleshooting](troubleshooting.md#refusing-to-replace-outdir-).
|
|
205
|
+
|
|
206
|
+
The marker also makes hook-written artifacts safe: the entries a `hooks.afterGenerate` adds to
|
|
207
|
+
the output are recorded in the marker, so they do not make the next run refuse the directory.
|
|
208
|
+
|
|
209
|
+
## Committing the snapshot
|
|
210
|
+
|
|
211
|
+
Usually you do not. Add the output directory to `.gitignore` — `codebase-snapshot init` does
|
|
212
|
+
it for you. If you do commit snapshots (for example, to diff context across releases),
|
|
213
|
+
`manifest.json` is the file that matters: it is compact and `diff` can compare two of them.
|