@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 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
- # 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
+ ## ✨ 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)