@ankurhalder/codebase-snapshot 0.0.0-stage → 2.1.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 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,332 @@
1
- # Temporary Holding Version
1
+ # @ankurhalder/codebase-snapshot
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ > LLM codebase snapshot generator, auditor, and AST partitioner for modern full-stack repositories.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@ankurhalder/codebase-snapshot.svg)](https://www.npmjs.com/package/@ankurhalder/codebase-snapshot)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](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
+ ## ⚡ Set up a repository
15
+
16
+ ```bash
17
+ npm create codebase-snapshot
18
+ ```
19
+
20
+ Detects the project type, asks a handful of questions with real defaults, and
21
+ writes a `snapshot.config.mjs` that lists **every setting at its default** — so
22
+ the config doubles as the documentation. Works for 25 project shapes including
23
+ Next.js, Nuxt, SvelteKit, Astro, Vue, React, NestJS, Django, FastAPI, Go, Rust,
24
+ Java, .NET, PHP, Ruby, monorepos and infra repos.
25
+
26
+ Non-interactive, with every answer as a flag:
27
+
28
+ ```bash
29
+ npx @ankurhalder/codebase-snapshot init --preset react-vite --max-part-bytes 120k --yes
30
+ ```
31
+
32
+ ---
33
+
34
+ ## ✨ Features
35
+
36
+ - **25 project presets** — detection reads the repository once and picks the most
37
+ specific match: Next.js, Nuxt, SvelteKit, Remix, Astro, Angular, Solid, Vue,
38
+ React, NestJS, Express/ Fastify, Python (Django / FastAPI / library), Go, Rust,
39
+ Java/Spring, .NET, Laravel, Rails, monorepos, infra and docs.
40
+ - **Structural summaries beyond TypeScript** — imports, declarations and exports
41
+ for Python, Go, Rust, Java/Kotlin/Scala, C#, PHP, Ruby and C/C++, plus the
42
+ script block of Vue/Svelte/Astro components through the real TypeScript
43
+ compiler. Every summary is labelled `ast`, `structural` or `heuristic`.
44
+ - **Interactive setup** — `npm create codebase-snapshot` walks every tunable
45
+ parameter and writes a self-documenting config.
46
+ - **Per-ecosystem ignores** — `__pycache__`, `.venv`, `target`, `.gradle`,
47
+ `bin`/`obj`, `vendor`, `.svelte-kit` and friends are recognised per preset.
48
+ - **Hard byte budgeting** — every `part-XXX.md` is guaranteed to stay under `maxPartBytes`. A single
49
+ oversized file is byte-truncated and marked rather than emitted whole.
50
+ - **Bounded redaction** — masks only the matched span (not the whole line), and masks multi-line PEM
51
+ private keys as a block. **No excerpt of a matched line is ever written to any artifact.**
52
+ - **Credential files are skipped by default** — `.env`, `*.pem`, `*.key`, `.npmrc`, `.netrc`,
53
+ `.git-credentials`, `id_rsa`, `.kube/config`, `*.tfstate`, … Opt back in with `includeSensitiveFiles`.
54
+ - **TypeScript AST extraction** — directives, imports, exports (including `export *` and anonymous
55
+ defaults), functions, classes, interfaces, types, enums, components, hooks, and route handlers.
56
+ - **Structured summaries** — JSON key shapes and Markdown headings, computed from redacted text.
57
+ - **Zero-config presets** — auto-detects Next.js, Vite, Node/Express/Nest, and plain TypeScript.
58
+ - **Real integrity validation** — verifies source hashes, every emitted `parts/*.md` against its recorded
59
+ `byteSize`/`sha256`, monolith artifacts, and the recorded configuration.
60
+ - **Safe output swapping** — the previous snapshot survives until the new one is fully in place, and
61
+ destructive `outDir`/`tmpDir` combinations are refused.
62
+ - **CLI & programmatic API** — `npx`, ESM, and CJS.
63
+
64
+ ---
65
+
66
+ ## 🚀 Quick Start
67
+
68
+ ```bash
69
+ npx @ankurhalder/codebase-snapshot generate
70
+ ```
71
+
72
+ Or install it in your project. Any npm-compatible client works — they all resolve the
73
+ same published package:
74
+
75
+ ```bash
76
+ npm install -D @ankurhalder/codebase-snapshot
77
+ yarn add -D @ankurhalder/codebase-snapshot
78
+ pnpm add -D @ankurhalder/codebase-snapshot
79
+ bun add -d @ankurhalder/codebase-snapshot
80
+ ```
81
+
82
+ ```json
83
+ {
84
+ "scripts": {
85
+ "snapshot": "codebase-snapshot generate",
86
+ "snapshot:validate": "codebase-snapshot validate"
87
+ }
88
+ }
89
+ ```
90
+
91
+ Requires **Node.js 18+**. Every client above is verified in CI, including yarn Berry with
92
+ strict PnP. See [docs/install.md](docs/install.md) for global installs, version pinning,
93
+ and troubleshooting.
94
+
95
+ ---
96
+
97
+ ## 🛠️ CLI
98
+
99
+ ```bash
100
+ codebase-snapshot [command] [options]
101
+ snap [command] [options]
102
+ ```
103
+
104
+ Every option accepts both `--opt value` and `--opt=value`. Human-readable progress goes to **stderr**;
105
+ payloads (`--format json`) go to **stdout**, so the output is safe to pipe into `jq`.
106
+
107
+ ### Commands
108
+
109
+ | Command | Description |
110
+ | :--- | :--- |
111
+ | `generate` *(default)* | Generates snapshot parts, reports, tree, and manifest |
112
+ | `validate`, `check` | Verifies snapshot integrity against disk and against the recorded config |
113
+ | `init` | Creates a starter `snapshot.config.ts` (or `.mjs` on Node < 22.6) |
114
+ | `report` | Prints metrics and health audits |
115
+ | `explain <path>` | Explains how one path is treated: layer, kind, part, or why it was skipped |
116
+ | `diff <a> <b>` | Compares two snapshots file by file |
117
+
118
+ See [docs/configuration.md](docs/configuration.md) for the full option reference.
119
+
120
+ ### Options
121
+
122
+ | Option | Description | Default |
123
+ | :--- | :--- | :--- |
124
+ | `--cwd <path>` | Target repository directory | `process.cwd()` |
125
+ | `--out-dir <path>` | Snapshot output directory | `snapshot` |
126
+ | `--include-monolith` | Also write `full-codebase.json` & `full-codebase.md` | `false` |
127
+ | `--max-monolith-bytes <n>` | Skip monolith artifacts above this size | unlimited |
128
+ | `--max-part-bytes <n>` | Hard byte budget per markdown part (min `4096`). Accepts units: `120k`, `1.5mb` | `307200` (300 KB) |
129
+ | `--large-file-bytes <n>` | Threshold for head/tail preview truncation. Accepts units | `204800` (200 KB) |
130
+ | `--include-sensitive-files` | Emit credential-bearing files (skipped by default) | `false` |
131
+ | `--skip-artifacts <names>` | Omit artifacts, comma-separated: `readme`, `manifest`, `parts`, `navigation`, `projectTree`, `audit`, `monolith` | everything on |
132
+ | `--only-artifacts <names>` | Write *only* the named artifacts. Cannot be combined with `--skip-artifacts` | — |
133
+ | `--format <text\|json>` | Output format | `text` |
134
+ | `--dry-run` | Compute the snapshot and print the plan without writing | `false` |
135
+ | `--watch` | Re-generate whenever the repository changes | `false` |
136
+
137
+ ### Exit codes
138
+
139
+ `0` success · `1` drift detected or generation failed · `2` usage error (unknown flag, bad number, …).
140
+
141
+ Unknown flags and unknown commands are **rejected**; they do not fall through to `generate`.
142
+
143
+ ---
144
+
145
+ ## ⚙️ Configuration
146
+
147
+ Run `npx @ankurhalder/codebase-snapshot init`. Supported filenames, in order:
148
+ `snapshot.config.ts`, `.js`, `.mjs`, `.cjs`, `.json`, `.snapshotrc.json`, `.snapshotrc`, and a `"snapshot"`
149
+ key in `package.json`.
150
+
151
+ > **Node note:** a `.ts` config is only importable natively from Node **22.6+**. `init` writes `.mjs`
152
+ > instead on older Node versions, and a config that cannot be loaded is reported on stderr rather than
153
+ > being silently ignored.
154
+
155
+ ```typescript
156
+ import { defineConfig } from "@ankurhalder/codebase-snapshot";
157
+
158
+ export default defineConfig({
159
+ branch: "main",
160
+ outDir: "snapshot",
161
+ maxPartBytes: 300 * 1024,
162
+ largeFileBytes: 200 * 1024,
163
+ previewHeadLines: 80,
164
+ previewTailLines: 40,
165
+
166
+ // Credential files are excluded unless you opt in:
167
+ includeSensitiveFiles: false,
168
+
169
+ // Every artifact is generated by default; switch individual ones off here.
170
+ artifacts: {
171
+ audit: false, // skip reports/audit.md
172
+ projectTree: false, // skip reports/project-tree.md
173
+ },
174
+
175
+ // Allow-list: when non-empty, only matching files are candidates.
176
+ include: ["src/**", "packages/*/src/**"],
177
+
178
+ // Plain strings match a whole path segment sequence; globs are supported.
179
+ excludePatterns: ["tests/fixtures", "**/*.lock", "**/__snapshots__/**"],
180
+
181
+ // "src/app/**" matches src/app and below — not src/apple or src/application.
182
+ layers: [
183
+ { name: "app", match: "src/app/**" },
184
+ { name: "features", match: "src/features/**" },
185
+ { name: "shared", match: "src/shared/**" },
186
+ { name: "scripts", match: "scripts/**" },
187
+ ],
188
+ kinds: [
189
+ { kind: "route", match: (p) => p.includes("/api/") || p.endsWith("route.ts") },
190
+ { kind: "component", match: (p) => p.endsWith(".tsx") },
191
+ { kind: "hook", match: (p) => p.includes("/hooks/") },
192
+ ],
193
+ secrets: {
194
+ // Strings are accepted here too (JSON configs can only express strings).
195
+ customPatterns: [{ rule: "internal-corp-token", pattern: /CORP-[A-Z0-9]{32}/ }],
196
+ },
197
+ });
198
+ ```
199
+
200
+ ### Scoping with `include` / `excludePatterns`
201
+
202
+ `include` is an allow-list: when it is non-empty, a file must match at least one
203
+ pattern to enter the snapshot. `excludePatterns` then narrows what survived.
204
+
205
+ ```typescript
206
+ export default defineConfig({
207
+ include: ["src/**"], // only src/, everything else is out
208
+ excludePatterns: ["**/*.test.ts"], // …minus tests
209
+ });
210
+ ```
211
+
212
+ Both accept globs (`**`, `*`, `?`) or plain strings, where a plain string matches
213
+ a whole path segment sequence — `"docs"` matches `docs/a.md` and `src/docs/b.md`
214
+ but not `docsite/c.md`.
215
+
216
+ Files dropped by either list are recorded in `manifest.skipped` with
217
+ `reason: "excluded"` and the pattern that removed them, so an omission is always
218
+ explicable. The effective `include` list is written into
219
+ `manifest.config.include`, so changing it later is reported as configuration
220
+ drift by `validate`.
221
+
222
+ ### Choosing which artifacts to emit
223
+
224
+ Everything is written by default. Each artifact can be switched off without
225
+ affecting the others, from a config file (`artifacts`) or the CLI
226
+ (`--skip-artifacts audit,navigation`):
227
+
228
+ | Artifact | Default | Notes |
229
+ | :--- | :--- | :--- |
230
+ | `readme` | on | `README.md` orientation page |
231
+ | `manifest` | on | `manifest.json` — **off breaks `validate` and `report`** |
232
+ | `parts` | on | `parts/part-XXX.md`, the code payload itself |
233
+ | `navigation` | on | `reports/navigation.md` — dangles if `parts` is off |
234
+ | `projectTree` | on | `reports/project-tree.md` |
235
+ | `audit` | on | `reports/audit.md` |
236
+ | `monolith` | off | `full-codebase.{json,md}`; also set by `includeMonolith` |
237
+
238
+ ```typescript
239
+ export default defineConfig({
240
+ // Reports only — a security/health pass with no code payload:
241
+ artifacts: { parts: false, navigation: false },
242
+ });
243
+ ```
244
+
245
+ Unknown artifact names are **rejected**, not ignored, so a typo cannot quietly
246
+ leave something enabled. Combinations that produce an unusable snapshot (a
247
+ disabled manifest, or navigation without parts) are recorded in
248
+ `manifest.warnings`. The selection is written into `manifest.config.artifacts`,
249
+ so changing it later is reported as configuration drift by `validate`.
250
+
251
+ ### Redaction behaviour
252
+
253
+ - Only the matched substring is replaced; the rest of the line survives.
254
+ - Multi-line `-----BEGIN … PRIVATE KEY-----` blocks are masked as a whole, including keys that were
255
+ cut off before their `END` marker.
256
+ - A rule supplied with the `/g` flag is compiled without it, so it cannot skip every other line.
257
+ - `reports/audit.md` lists *where* secrets were found (file, line, rule) and never reproduces the value.
258
+
259
+ ---
260
+
261
+ ## 📦 Programmatic API
262
+
263
+ ```typescript
264
+ import { generateSnapshot, resolveConfig, validateSnapshot } from "@ankurhalder/codebase-snapshot";
265
+
266
+ // `generateSnapshot` is synchronous — it does blocking file I/O by design.
267
+ // (`generateSnapshotAsync` is provided if you prefer an async call site.)
268
+ const config = resolveConfig({ cwd: process.cwd() }, { maxPartBytes: 300 * 1024 });
269
+ const manifest = generateSnapshot(config);
270
+
271
+ console.log(`Generated ${manifest.counts.partsCount} parts for ${manifest.counts.totalFiles} files.`);
272
+ console.log(`Integrity: ${manifest.validation.status}`);
273
+
274
+ // Dry run: compute everything, write nothing.
275
+ const plan = generateSnapshot(config, { dryRun: true });
276
+
277
+ // Validate against disk
278
+ const validation = validateSnapshot(config);
279
+ if (!validation.valid) {
280
+ console.error("Drift detected:", validation.errors);
281
+ console.error(validation.drift); // hashMismatches, partIssues, configMismatches, …
282
+ }
283
+ ```
284
+
285
+ `resolveConfig(userConfig, cliOptions?, baseDir?)` brands its result; `isResolvedConfig(value)` lets you
286
+ check for it, and `generateSnapshot`/`validateSnapshot` accept either a resolved config or a plain
287
+ `SnapshotConfig`.
288
+
289
+ ---
290
+
291
+ ## 📁 Snapshot Output
292
+
293
+ ```text
294
+ snapshot/
295
+ ├── README.md # Overview for LLMs and developers
296
+ ├── manifest.json # Machine-readable metadata and AST registry
297
+ ├── parts/
298
+ │ ├── part-001.md # Byte-bounded chunks with anchored code blocks
299
+ │ └── ...
300
+ └── reports/
301
+ ├── navigation.md # File-to-part lookup index (anchors resolve)
302
+ ├── project-tree.md # ASCII repository hierarchy
303
+ └── audit.md # Security findings, layer distributions, large files
304
+ ```
305
+
306
+ `manifest.json` records the effective configuration (budgets, preview sizes, excludes,
307
+ `includeSensitiveFiles`), so `validate` can detect configuration drift as well as code drift.
308
+ `manifest.validation.status` is computed from a real pre-write integrity pass (part digests, byte
309
+ budgets, file-to-part assignment) — it is not a constant.
310
+
311
+ ---
312
+
313
+ ## 🔄 Upgrading from 1.x
314
+
315
+ Version 2.0.0 contains breaking changes, all in service of correctness:
316
+
317
+ | Change | Reason |
318
+ | :--- | :--- |
319
+ | `generateSnapshot()` is synchronous | It never awaited anything; the `async` signature only promised concurrency it did not have. `await generateSnapshot(...)` still works; `generateSnapshotAsync` is available. |
320
+ | Manifest `schemaVersion` is `3.0.0` | The shape changed materially (below). |
321
+ | 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`. |
322
+ | `totals.summaryOnlyFiles` → `totals.previewFiles` | The old name claimed more than the value meant. |
323
+ | `SecretFinding.snippet` removed | It carried the raw matched source line — the vector that leaked secrets into `reports/audit.md`. |
324
+ | Credential files skipped by default | A private key or registry token cannot be reliably masked by a regex; opt in with `includeSensitiveFiles`. |
325
+ | `monolithContent` option removed, `--force` removed | Never implemented / never read. |
326
+ | `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. |
327
+
328
+ ---
329
+
330
+ ## 📄 License
331
+
332
+ MIT © [Ankur Halder](https://github.com/ankurhalder)