@ankurhalder/codebase-snapshot 0.0.0-stage → 2.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/LICENSE +21 -0
- package/README.md +289 -2
- package/dist/bin.js +3725 -0
- package/dist/chunk-RGDVLQTT.js +3217 -0
- package/dist/cli.cjs +3718 -0
- package/dist/cli.d.cts +40 -0
- package/dist/cli.d.ts +40 -0
- package/dist/cli.js +487 -0
- package/dist/index.cjs +3292 -0
- package/dist/index.d.cts +364 -0
- package/dist/index.d.ts +364 -0
- package/dist/index.js +3 -0
- package/dist/types-S-5eRuRc.d.cts +372 -0
- package/dist/types-S-5eRuRc.d.ts +372 -0
- package/docs/configuration.md +244 -0
- package/package.json +93 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ankur Halder
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,290 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @ankurhalder/codebase-snapshot
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> LLM codebase snapshot generator, auditor, and AST partitioner for modern full-stack repositories.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@ankurhalder/codebase-snapshot)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
`@ankurhalder/codebase-snapshot` inspects your codebase, redacts sensitive credentials, parses TypeScript
|
|
9
|
+
AST structures, and partitions your repository into **hard byte-bounded**, LLM-ready markdown chunks
|
|
10
|
+
(`parts/part-XXX.md`) accompanied by navigation indexes, a directory tree, and a health audit.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## ✨ Features
|
|
15
|
+
|
|
16
|
+
- **Hard byte budgeting** — every `part-XXX.md` is guaranteed to stay under `maxPartBytes`. A single
|
|
17
|
+
oversized file is byte-truncated and marked rather than emitted whole.
|
|
18
|
+
- **Bounded redaction** — masks only the matched span (not the whole line), and masks multi-line PEM
|
|
19
|
+
private keys as a block. **No excerpt of a matched line is ever written to any artifact.**
|
|
20
|
+
- **Credential files are skipped by default** — `.env`, `*.pem`, `*.key`, `.npmrc`, `.netrc`,
|
|
21
|
+
`.git-credentials`, `id_rsa`, `.kube/config`, `*.tfstate`, … Opt back in with `includeSensitiveFiles`.
|
|
22
|
+
- **TypeScript AST extraction** — directives, imports, exports (including `export *` and anonymous
|
|
23
|
+
defaults), functions, classes, interfaces, types, enums, components, hooks, and route handlers.
|
|
24
|
+
- **Structured summaries** — JSON key shapes and Markdown headings, computed from redacted text.
|
|
25
|
+
- **Zero-config presets** — auto-detects Next.js, Vite, Node/Express/Nest, and plain TypeScript.
|
|
26
|
+
- **Real integrity validation** — verifies source hashes, every emitted `parts/*.md` against its recorded
|
|
27
|
+
`byteSize`/`sha256`, monolith artifacts, and the recorded configuration.
|
|
28
|
+
- **Safe output swapping** — the previous snapshot survives until the new one is fully in place, and
|
|
29
|
+
destructive `outDir`/`tmpDir` combinations are refused.
|
|
30
|
+
- **CLI & programmatic API** — `npx`, ESM, and CJS.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 🚀 Quick Start
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npx @ankurhalder/codebase-snapshot generate
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm install -D @ankurhalder/codebase-snapshot
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"scripts": {
|
|
47
|
+
"snapshot": "codebase-snapshot generate",
|
|
48
|
+
"snapshot:validate": "codebase-snapshot validate"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 🛠️ CLI
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
codebase-snapshot [command] [options]
|
|
59
|
+
snap [command] [options]
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Every option accepts both `--opt value` and `--opt=value`. Human-readable progress goes to **stderr**;
|
|
63
|
+
payloads (`--format json`) go to **stdout**, so the output is safe to pipe into `jq`.
|
|
64
|
+
|
|
65
|
+
### Commands
|
|
66
|
+
|
|
67
|
+
| Command | Description |
|
|
68
|
+
| :--- | :--- |
|
|
69
|
+
| `generate` *(default)* | Generates snapshot parts, reports, tree, and manifest |
|
|
70
|
+
| `validate`, `check` | Verifies snapshot integrity against disk and against the recorded config |
|
|
71
|
+
| `init` | Creates a starter `snapshot.config.ts` (or `.mjs` on Node < 22.6) |
|
|
72
|
+
| `report` | Prints metrics and health audits |
|
|
73
|
+
| `explain <path>` | Explains how one path is treated: layer, kind, part, or why it was skipped |
|
|
74
|
+
| `diff <a> <b>` | Compares two snapshots file by file |
|
|
75
|
+
|
|
76
|
+
See [docs/configuration.md](docs/configuration.md) for the full option reference.
|
|
77
|
+
|
|
78
|
+
### Options
|
|
79
|
+
|
|
80
|
+
| Option | Description | Default |
|
|
81
|
+
| :--- | :--- | :--- |
|
|
82
|
+
| `--cwd <path>` | Target repository directory | `process.cwd()` |
|
|
83
|
+
| `--out-dir <path>` | Snapshot output directory | `snapshot` |
|
|
84
|
+
| `--include-monolith` | Also write `full-codebase.json` & `full-codebase.md` | `false` |
|
|
85
|
+
| `--max-monolith-bytes <n>` | Skip monolith artifacts above this size | unlimited |
|
|
86
|
+
| `--max-part-bytes <n>` | Hard byte budget per markdown part (min `4096`). Accepts units: `120k`, `1.5mb` | `307200` (300 KB) |
|
|
87
|
+
| `--large-file-bytes <n>` | Threshold for head/tail preview truncation. Accepts units | `204800` (200 KB) |
|
|
88
|
+
| `--include-sensitive-files` | Emit credential-bearing files (skipped by default) | `false` |
|
|
89
|
+
| `--skip-artifacts <names>` | Omit artifacts, comma-separated: `readme`, `manifest`, `parts`, `navigation`, `projectTree`, `audit`, `monolith` | everything on |
|
|
90
|
+
| `--only-artifacts <names>` | Write *only* the named artifacts. Cannot be combined with `--skip-artifacts` | — |
|
|
91
|
+
| `--format <text\|json>` | Output format | `text` |
|
|
92
|
+
| `--dry-run` | Compute the snapshot and print the plan without writing | `false` |
|
|
93
|
+
| `--watch` | Re-generate whenever the repository changes | `false` |
|
|
94
|
+
|
|
95
|
+
### Exit codes
|
|
96
|
+
|
|
97
|
+
`0` success · `1` drift detected or generation failed · `2` usage error (unknown flag, bad number, …).
|
|
98
|
+
|
|
99
|
+
Unknown flags and unknown commands are **rejected**; they do not fall through to `generate`.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## ⚙️ Configuration
|
|
104
|
+
|
|
105
|
+
Run `npx @ankurhalder/codebase-snapshot init`. Supported filenames, in order:
|
|
106
|
+
`snapshot.config.ts`, `.js`, `.mjs`, `.cjs`, `.json`, `.snapshotrc.json`, `.snapshotrc`, and a `"snapshot"`
|
|
107
|
+
key in `package.json`.
|
|
108
|
+
|
|
109
|
+
> **Node note:** a `.ts` config is only importable natively from Node **22.6+**. `init` writes `.mjs`
|
|
110
|
+
> instead on older Node versions, and a config that cannot be loaded is reported on stderr rather than
|
|
111
|
+
> being silently ignored.
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
import { defineConfig } from "@ankurhalder/codebase-snapshot";
|
|
115
|
+
|
|
116
|
+
export default defineConfig({
|
|
117
|
+
branch: "main",
|
|
118
|
+
outDir: "snapshot",
|
|
119
|
+
maxPartBytes: 300 * 1024,
|
|
120
|
+
largeFileBytes: 200 * 1024,
|
|
121
|
+
previewHeadLines: 80,
|
|
122
|
+
previewTailLines: 40,
|
|
123
|
+
|
|
124
|
+
// Credential files are excluded unless you opt in:
|
|
125
|
+
includeSensitiveFiles: false,
|
|
126
|
+
|
|
127
|
+
// Every artifact is generated by default; switch individual ones off here.
|
|
128
|
+
artifacts: {
|
|
129
|
+
audit: false, // skip reports/audit.md
|
|
130
|
+
projectTree: false, // skip reports/project-tree.md
|
|
131
|
+
},
|
|
132
|
+
|
|
133
|
+
// Allow-list: when non-empty, only matching files are candidates.
|
|
134
|
+
include: ["src/**", "packages/*/src/**"],
|
|
135
|
+
|
|
136
|
+
// Plain strings match a whole path segment sequence; globs are supported.
|
|
137
|
+
excludePatterns: ["tests/fixtures", "**/*.lock", "**/__snapshots__/**"],
|
|
138
|
+
|
|
139
|
+
// "src/app/**" matches src/app and below — not src/apple or src/application.
|
|
140
|
+
layers: [
|
|
141
|
+
{ name: "app", match: "src/app/**" },
|
|
142
|
+
{ name: "features", match: "src/features/**" },
|
|
143
|
+
{ name: "shared", match: "src/shared/**" },
|
|
144
|
+
{ name: "scripts", match: "scripts/**" },
|
|
145
|
+
],
|
|
146
|
+
kinds: [
|
|
147
|
+
{ kind: "route", match: (p) => p.includes("/api/") || p.endsWith("route.ts") },
|
|
148
|
+
{ kind: "component", match: (p) => p.endsWith(".tsx") },
|
|
149
|
+
{ kind: "hook", match: (p) => p.includes("/hooks/") },
|
|
150
|
+
],
|
|
151
|
+
secrets: {
|
|
152
|
+
// Strings are accepted here too (JSON configs can only express strings).
|
|
153
|
+
customPatterns: [{ rule: "internal-corp-token", pattern: /CORP-[A-Z0-9]{32}/ }],
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Scoping with `include` / `excludePatterns`
|
|
159
|
+
|
|
160
|
+
`include` is an allow-list: when it is non-empty, a file must match at least one
|
|
161
|
+
pattern to enter the snapshot. `excludePatterns` then narrows what survived.
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
export default defineConfig({
|
|
165
|
+
include: ["src/**"], // only src/, everything else is out
|
|
166
|
+
excludePatterns: ["**/*.test.ts"], // …minus tests
|
|
167
|
+
});
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Both accept globs (`**`, `*`, `?`) or plain strings, where a plain string matches
|
|
171
|
+
a whole path segment sequence — `"docs"` matches `docs/a.md` and `src/docs/b.md`
|
|
172
|
+
but not `docsite/c.md`.
|
|
173
|
+
|
|
174
|
+
Files dropped by either list are recorded in `manifest.skipped` with
|
|
175
|
+
`reason: "excluded"` and the pattern that removed them, so an omission is always
|
|
176
|
+
explicable. The effective `include` list is written into
|
|
177
|
+
`manifest.config.include`, so changing it later is reported as configuration
|
|
178
|
+
drift by `validate`.
|
|
179
|
+
|
|
180
|
+
### Choosing which artifacts to emit
|
|
181
|
+
|
|
182
|
+
Everything is written by default. Each artifact can be switched off without
|
|
183
|
+
affecting the others, from a config file (`artifacts`) or the CLI
|
|
184
|
+
(`--skip-artifacts audit,navigation`):
|
|
185
|
+
|
|
186
|
+
| Artifact | Default | Notes |
|
|
187
|
+
| :--- | :--- | :--- |
|
|
188
|
+
| `readme` | on | `README.md` orientation page |
|
|
189
|
+
| `manifest` | on | `manifest.json` — **off breaks `validate` and `report`** |
|
|
190
|
+
| `parts` | on | `parts/part-XXX.md`, the code payload itself |
|
|
191
|
+
| `navigation` | on | `reports/navigation.md` — dangles if `parts` is off |
|
|
192
|
+
| `projectTree` | on | `reports/project-tree.md` |
|
|
193
|
+
| `audit` | on | `reports/audit.md` |
|
|
194
|
+
| `monolith` | off | `full-codebase.{json,md}`; also set by `includeMonolith` |
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
export default defineConfig({
|
|
198
|
+
// Reports only — a security/health pass with no code payload:
|
|
199
|
+
artifacts: { parts: false, navigation: false },
|
|
200
|
+
});
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Unknown artifact names are **rejected**, not ignored, so a typo cannot quietly
|
|
204
|
+
leave something enabled. Combinations that produce an unusable snapshot (a
|
|
205
|
+
disabled manifest, or navigation without parts) are recorded in
|
|
206
|
+
`manifest.warnings`. The selection is written into `manifest.config.artifacts`,
|
|
207
|
+
so changing it later is reported as configuration drift by `validate`.
|
|
208
|
+
|
|
209
|
+
### Redaction behaviour
|
|
210
|
+
|
|
211
|
+
- Only the matched substring is replaced; the rest of the line survives.
|
|
212
|
+
- Multi-line `-----BEGIN … PRIVATE KEY-----` blocks are masked as a whole, including keys that were
|
|
213
|
+
cut off before their `END` marker.
|
|
214
|
+
- A rule supplied with the `/g` flag is compiled without it, so it cannot skip every other line.
|
|
215
|
+
- `reports/audit.md` lists *where* secrets were found (file, line, rule) and never reproduces the value.
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## 📦 Programmatic API
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
import { generateSnapshot, resolveConfig, validateSnapshot } from "@ankurhalder/codebase-snapshot";
|
|
223
|
+
|
|
224
|
+
// `generateSnapshot` is synchronous — it does blocking file I/O by design.
|
|
225
|
+
// (`generateSnapshotAsync` is provided if you prefer an async call site.)
|
|
226
|
+
const config = resolveConfig({ cwd: process.cwd() }, { maxPartBytes: 300 * 1024 });
|
|
227
|
+
const manifest = generateSnapshot(config);
|
|
228
|
+
|
|
229
|
+
console.log(`Generated ${manifest.counts.partsCount} parts for ${manifest.counts.totalFiles} files.`);
|
|
230
|
+
console.log(`Integrity: ${manifest.validation.status}`);
|
|
231
|
+
|
|
232
|
+
// Dry run: compute everything, write nothing.
|
|
233
|
+
const plan = generateSnapshot(config, { dryRun: true });
|
|
234
|
+
|
|
235
|
+
// Validate against disk
|
|
236
|
+
const validation = validateSnapshot(config);
|
|
237
|
+
if (!validation.valid) {
|
|
238
|
+
console.error("Drift detected:", validation.errors);
|
|
239
|
+
console.error(validation.drift); // hashMismatches, partIssues, configMismatches, …
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
`resolveConfig(userConfig, cliOptions?, baseDir?)` brands its result; `isResolvedConfig(value)` lets you
|
|
244
|
+
check for it, and `generateSnapshot`/`validateSnapshot` accept either a resolved config or a plain
|
|
245
|
+
`SnapshotConfig`.
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## 📁 Snapshot Output
|
|
250
|
+
|
|
251
|
+
```text
|
|
252
|
+
snapshot/
|
|
253
|
+
├── README.md # Overview for LLMs and developers
|
|
254
|
+
├── manifest.json # Machine-readable metadata and AST registry
|
|
255
|
+
├── parts/
|
|
256
|
+
│ ├── part-001.md # Byte-bounded chunks with anchored code blocks
|
|
257
|
+
│ └── ...
|
|
258
|
+
└── reports/
|
|
259
|
+
├── navigation.md # File-to-part lookup index (anchors resolve)
|
|
260
|
+
├── project-tree.md # ASCII repository hierarchy
|
|
261
|
+
└── audit.md # Security findings, layer distributions, large files
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
`manifest.json` records the effective configuration (budgets, preview sizes, excludes,
|
|
265
|
+
`includeSensitiveFiles`), so `validate` can detect configuration drift as well as code drift.
|
|
266
|
+
`manifest.validation.status` is computed from a real pre-write integrity pass (part digests, byte
|
|
267
|
+
budgets, file-to-part assignment) — it is not a constant.
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## 🔄 Upgrading from 1.x
|
|
272
|
+
|
|
273
|
+
Version 2.0.0 contains breaking changes, all in service of correctness:
|
|
274
|
+
|
|
275
|
+
| Change | Reason |
|
|
276
|
+
| :--- | :--- |
|
|
277
|
+
| `generateSnapshot()` is synchronous | It never awaited anything; the `async` signature only promised concurrency it did not have. `await generateSnapshot(...)` still works; `generateSnapshotAsync` is available. |
|
|
278
|
+
| Manifest `schemaVersion` is `3.0.0` | The shape changed materially (below). |
|
|
279
|
+
| Removed duplicate fields: `path`, `bytes`, `sha256` (on entries), `PartMetadata.bytes`, `PartMetadata.files`, `contentFingerprint`, `generator.script` | Each was a permanent alias of a canonical field that could only ever agree. Use `relativePath`, `sizeBytes`, `hashSha256`, `byteSize`, `includedFiles[].relativePath`. |
|
|
280
|
+
| `totals.summaryOnlyFiles` → `totals.previewFiles` | The old name claimed more than the value meant. |
|
|
281
|
+
| `SecretFinding.snippet` removed | It carried the raw matched source line — the vector that leaked secrets into `reports/audit.md`. |
|
|
282
|
+
| Credential files skipped by default | A private key or registry token cannot be reliably masked by a regex; opt in with `includeSensitiveFiles`. |
|
|
283
|
+
| `monolithContent` option removed, `--force` removed | Never implemented / never read. |
|
|
284
|
+
| `dist/cli.js` is no longer the executable | The bin is now `dist/bin.js`; `./cli` is a side-effect-free module usable from both ESM and CJS. |
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## 📄 License
|
|
289
|
+
|
|
290
|
+
MIT © [Ankur Halder](https://github.com/ankurhalder)
|