codebase-snapshot 0.0.0-stage → 4.5.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/CHANGELOG.md +1242 -0
- package/LICENSE +21 -0
- package/README.md +276 -2
- package/SECURITY.md +110 -0
- package/dist/bin.js +9840 -0
- package/dist/chunk-SSCFLILW.js +7885 -0
- package/dist/cli.cjs +9822 -0
- package/dist/cli.d.cts +124 -0
- package/dist/cli.d.ts +124 -0
- package/dist/cli.js +1931 -0
- package/dist/index.cjs +7973 -0
- package/dist/index.d.cts +710 -0
- package/dist/index.d.ts +710 -0
- package/dist/index.js +3 -0
- package/dist/types-Ca4PmkiQ.d.cts +491 -0
- package/dist/types-Ca4PmkiQ.d.ts +491 -0
- package/docs/agent.md +135 -0
- package/docs/cli.md +437 -0
- package/docs/configuration.md +424 -0
- package/docs/how-to-context-budget.md +90 -0
- package/docs/how-to-feed-a-repo-to-claude.md +122 -0
- package/docs/install.md +196 -0
- package/docs/output.md +264 -0
- package/docs/troubleshooting.md +384 -0
- package/docs/vs-code2prompt.md +66 -0
- package/docs/vs-gitingest.md +69 -0
- package/docs/vs-repomix.md +83 -0
- package/package.json +112 -4
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,710 @@
|
|
|
1
|
+
import { S as SecretRule, D as DiscoveryMode, a as SkipRecord, J as JsonSummary, M as MarkdownSummary, b as SourceSummary, R as ResolvedSnapshotConfig, c as SnapshotConfig, B as ByteSize, C as CliOptions, F as FileEntry, P as PartMetadata, L as LayerRule, K as KindRule, T as TextEncoding, d as SecretFinding, V as ValidationResult, e as Manifest, A as ArtifactSelection } from './types-Ca4PmkiQ.cjs';
|
|
2
|
+
export { f as BudgetConfig, H as HooksConfig, I as InclusionMode, g as ManifestArtifact, h as ManifestConfigSnapshot, i as ManifestIndexes, j as ManifestTotals, k as Mode, O as OutputFormat, l as PartFileEntry, m as ResolvedArtifacts, n as ResolvedBudgets, o as SecretRuleInput, p as SkipReason, q as SummaryQuality, r as ValidationDrift } from './types-Ca4PmkiQ.cjs';
|
|
3
|
+
|
|
4
|
+
interface CollectOptions {
|
|
5
|
+
/**
|
|
6
|
+
* When non-empty, only files matching at least one pattern are candidates.
|
|
7
|
+
* Applied before `excludePatterns`, which then narrows the result further.
|
|
8
|
+
*/
|
|
9
|
+
includePatterns?: string[];
|
|
10
|
+
excludePatterns?: string[];
|
|
11
|
+
/**
|
|
12
|
+
* Emit credential-bearing files (`.env`, `*.pem`, `.npmrc`, `id_rsa`, …).
|
|
13
|
+
* Default `false`: they are skipped with `reason: "sensitive-file"`.
|
|
14
|
+
*/
|
|
15
|
+
includeSensitiveFiles?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Secret rules used to scan *paths* (defaults are always applied). Pass
|
|
18
|
+
* `config.secretRules` so `customPatterns` also protect file names.
|
|
19
|
+
*/
|
|
20
|
+
secretRules?: readonly SecretRule[];
|
|
21
|
+
/**
|
|
22
|
+
* Top-level directory names this run is about to write — `outDir` and `tmpDir`.
|
|
23
|
+
*
|
|
24
|
+
* They are excluded from the scan without a `skipped[]` record because that is this tool's own
|
|
25
|
+
* bookkeeping and a first run cannot have written a marker into them yet. A directory with one
|
|
26
|
+
* of these *names* that carries no marker is still excluded when it is one of these paths, but
|
|
27
|
+
* is an ordinary directory when it is not: dropping it silently was the bug.
|
|
28
|
+
*/
|
|
29
|
+
toolOwnedPaths?: readonly string[];
|
|
30
|
+
/**
|
|
31
|
+
* How candidates are discovered: `auto` (git when usable, refusal when the
|
|
32
|
+
* directory claims to be a repository but git cannot list it), `git` (require
|
|
33
|
+
* git), or `walk` (walk the tree, reading the root `.gitignore` directly).
|
|
34
|
+
* Defaults to the configured mode, then `auto`.
|
|
35
|
+
*/
|
|
36
|
+
discovery?: DiscoveryMode;
|
|
37
|
+
}
|
|
38
|
+
interface CollectResult {
|
|
39
|
+
files: string[];
|
|
40
|
+
skipped: SkipRecord[];
|
|
41
|
+
}
|
|
42
|
+
declare function isSensitivePath(relativePath: string): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Translates a glob (`**`, `*`, `?`, `{a,b}`, `[abc]`) into a RegExp. A pattern
|
|
45
|
+
* without a leading `/` matches at any depth, mirroring `.gitignore` semantics.
|
|
46
|
+
*
|
|
47
|
+
* Brace groups and character classes are supported because they are what people
|
|
48
|
+
* actually write: `*.{ts,tsx}` previously matched nothing at all, since `{` and
|
|
49
|
+
* `}` were escaped as literals.
|
|
50
|
+
*/
|
|
51
|
+
declare function globToRegExp(glob: string): RegExp;
|
|
52
|
+
/**
|
|
53
|
+
* Path exclusion with proper segment semantics.
|
|
54
|
+
*
|
|
55
|
+
* A plain (non-glob) pattern matches a whole path segment sequence, so
|
|
56
|
+
* `"test"` matches `test/a.ts` and `src/test/b.ts` but **not** `contest/entry.ts`
|
|
57
|
+
* (the previous implementation was a raw `String.includes`).
|
|
58
|
+
*/
|
|
59
|
+
declare function matchesExclude(relativePath: string, pattern: string): boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Collects candidate file paths.
|
|
62
|
+
*
|
|
63
|
+
* Uses `git ls-files -z` when git is available: `-z` is required because the
|
|
64
|
+
* default output C-quotes paths containing non-ASCII characters, which silently
|
|
65
|
+
* mangled `src/café.ts` into a path that never existed and dropped the file.
|
|
66
|
+
*/
|
|
67
|
+
declare function collectFiles(cwd: string, options?: CollectOptions): CollectResult;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Strips `//` and block comments the way JSONC does, without touching anything
|
|
71
|
+
* inside a string literal. `JSON.parse` alone made every `.jsonc` file report
|
|
72
|
+
* `invalid-json`, losing its key shape entirely.
|
|
73
|
+
*/
|
|
74
|
+
declare function stripJsonComments(content: string): string;
|
|
75
|
+
declare function summarizeJson(content: string): JsonSummary;
|
|
76
|
+
declare function summarizeMarkdown(content: string): MarkdownSummary;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Extracts a structural summary from a JS/TS source file.
|
|
80
|
+
*
|
|
81
|
+
* The traversal is an explicit stack, not recursion: a generated file with a
|
|
82
|
+
* long `a && b && …` chain used to overflow the call stack inside our own
|
|
83
|
+
* visitor and abort the entire snapshot. Callers should still treat a throw as
|
|
84
|
+
* possible (TypeScript's own parser recurses), so wrap the call.
|
|
85
|
+
*/
|
|
86
|
+
declare function summarizeTypeScriptSource(relativePath: string, content: string): SourceSummary;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Brands a resolved config so consumers can distinguish it from a user
|
|
90
|
+
* `SnapshotConfig` structurally, instead of guessing from the presence of a
|
|
91
|
+
* single field (`"secretRules" in input`), which misfired for hand-built configs
|
|
92
|
+
* and for user configs that happened to carry an extra key.
|
|
93
|
+
*/
|
|
94
|
+
declare function markResolvedConfig<T extends ResolvedSnapshotConfig>(config: T): T;
|
|
95
|
+
/**
|
|
96
|
+
* Detects a resolved config.
|
|
97
|
+
*
|
|
98
|
+
* The brand is a non-enumerable symbol, so it survives `structuredClone` but NOT
|
|
99
|
+
* `{ ...resolvedConfig, override }`. Relying on the brand alone caused a real
|
|
100
|
+
* regression: a spread config was re-resolved as a user config, which silently
|
|
101
|
+
* dropped custom `secretRules` and let matching secrets through in clear text.
|
|
102
|
+
* The structural fallback therefore accepts any object carrying the complete
|
|
103
|
+
* resolved-only shape.
|
|
104
|
+
*/
|
|
105
|
+
declare function isResolvedConfig(value: unknown): value is ResolvedSnapshotConfig;
|
|
106
|
+
/**
|
|
107
|
+
* Re-validates a resolved config taken from an untrusted path (e.g. a spread
|
|
108
|
+
* copy), recompiling secret rules so string patterns and stray `g`/`y` flags are
|
|
109
|
+
* normalised even when the object did not come from `resolveConfig`.
|
|
110
|
+
*/
|
|
111
|
+
declare function normalizeResolvedConfig(config: ResolvedSnapshotConfig): ResolvedSnapshotConfig;
|
|
112
|
+
/**
|
|
113
|
+
* Guards against a silently lossy config round-trip.
|
|
114
|
+
*
|
|
115
|
+
* A `RegExp` cannot survive `JSON.stringify`/`JSON.parse`: the pattern comes
|
|
116
|
+
* back as `{}`. Such an object no longer satisfies `isResolvedConfig`, so it
|
|
117
|
+
* would be re-resolved as a user config and its custom `secretRules` would be
|
|
118
|
+
* dropped without a word — a silent security regression. Fail loudly instead.
|
|
119
|
+
*/
|
|
120
|
+
declare function assertUsableConfigInput(value: unknown): void;
|
|
121
|
+
interface ConfigLoadResult {
|
|
122
|
+
config: SnapshotConfig | null;
|
|
123
|
+
/** Absolute path of the file the config came from, if any. */
|
|
124
|
+
file: string | null;
|
|
125
|
+
/** Why a candidate config file was ignored. Never silently discarded. */
|
|
126
|
+
warnings: string[];
|
|
127
|
+
/**
|
|
128
|
+
* Config files that exist but could not be read.
|
|
129
|
+
*
|
|
130
|
+
* These are fatal: a config that fails to parse used to be skipped in favour of
|
|
131
|
+
* the defaults, which meant a typo silently produced a snapshot with none of the
|
|
132
|
+
* developer's settings applied. A config that is merely unsupported on this Node
|
|
133
|
+
* version (a `.ts` file below Node 22.6) stays a warning — nothing is wrong with
|
|
134
|
+
* the file, and the message already says what to do.
|
|
135
|
+
*/
|
|
136
|
+
errors: string[];
|
|
137
|
+
}
|
|
138
|
+
/** True when this Node can `import()` a `.ts` file natively (type stripping). */
|
|
139
|
+
declare function supportsTypeScriptImports(): boolean;
|
|
140
|
+
/**
|
|
141
|
+
* Loads the first usable config file from `cwd`.
|
|
142
|
+
*
|
|
143
|
+
* Every failure is reported through `warnings` instead of being swallowed by an
|
|
144
|
+
* empty `catch`: a syntax error in a user's config used to be indistinguishable
|
|
145
|
+
* from having no config at all.
|
|
146
|
+
*/
|
|
147
|
+
declare function loadConfigFileDetailed(cwd: string): Promise<ConfigLoadResult>;
|
|
148
|
+
declare function loadConfigFile(cwd: string): Promise<SnapshotConfig | null>;
|
|
149
|
+
/** Smallest usable part budget: a part header plus one section must fit. */
|
|
150
|
+
declare const MIN_MAX_PART_BYTES = 4096;
|
|
151
|
+
/** Every artifact this package can write, in the order it is written. */
|
|
152
|
+
declare const ARTIFACT_NAMES: readonly ["readme", "manifest", "parts", "navigation", "projectTree", "audit", "monolith"];
|
|
153
|
+
/**
|
|
154
|
+
* Parses a byte budget.
|
|
155
|
+
*
|
|
156
|
+
* Accepts a plain number (`122880`), a numeric string (`"122880"`), or a
|
|
157
|
+
* human-readable size (`"120k"`, `"1.5mb"`, `"2 GB"`). Returns `undefined` for
|
|
158
|
+
* anything it cannot read, so callers can decide whether that is an error or a
|
|
159
|
+
* fallback — a typo'd budget must not silently become the default.
|
|
160
|
+
*/
|
|
161
|
+
declare function parseByteSize(value: ByteSize | null | undefined): number | undefined;
|
|
162
|
+
/**
|
|
163
|
+
* Merges user config, preset defaults and CLI overrides.
|
|
164
|
+
*
|
|
165
|
+
* `baseDir` is the directory the config was loaded from. Without it the natural
|
|
166
|
+
* library flow (`loadConfigFile(dir)` then `resolveConfig(config)`) silently
|
|
167
|
+
* fell back to `process.cwd()` and generated into the caller's directory.
|
|
168
|
+
*/
|
|
169
|
+
declare function resolveConfig(userConfig: SnapshotConfig | null, cliOptions?: Partial<CliOptions>, baseDir?: string): ResolvedSnapshotConfig;
|
|
170
|
+
/**
|
|
171
|
+
* Matches a path against a layer/kind rule.
|
|
172
|
+
*
|
|
173
|
+
* `"src/app/**"` now means "exactly `src/app` or below": the previous
|
|
174
|
+
* implementation stripped `**` **and** the trailing slash, so the pattern also
|
|
175
|
+
* matched `src/apple/` and `src/application/`.
|
|
176
|
+
*/
|
|
177
|
+
declare function matchRule(filePath: string, matcher: string | RegExp | ((filePath: string) => boolean)): boolean;
|
|
178
|
+
|
|
179
|
+
declare function defineConfig(config: SnapshotConfig): SnapshotConfig;
|
|
180
|
+
|
|
181
|
+
declare function generateProjectTree(filePaths: string[]): string;
|
|
182
|
+
|
|
183
|
+
interface PartitionedPart {
|
|
184
|
+
filename: string;
|
|
185
|
+
markdown: string;
|
|
186
|
+
metadata: PartMetadata;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Partitions rendered file sections into byte-bounded parts.
|
|
190
|
+
*
|
|
191
|
+
* The budget is a hard bound: a section that cannot fit into a part on its own
|
|
192
|
+
* is byte-truncated (and marked `isPreviewTruncated`) rather than emitted whole.
|
|
193
|
+
* The previous implementation skipped the flush check for an empty chunk, so one
|
|
194
|
+
* large file produced a part 13.5x over the configured budget.
|
|
195
|
+
*/
|
|
196
|
+
declare function partitionFiles(files: FileEntry[], maxPartBytes: number, contentFingerprint?: string): PartitionedPart[];
|
|
197
|
+
/**
|
|
198
|
+
* Re-numbers a set of parts as one sequence and rewrites their headers.
|
|
199
|
+
*
|
|
200
|
+
* Needed when parts are produced by more than one partition pass (per-layer
|
|
201
|
+
* budgets): each pass numbers from `part-001`, so the merged set would otherwise
|
|
202
|
+
* contain duplicate filenames and every `Previous:`/`Next:` link would lie.
|
|
203
|
+
*/
|
|
204
|
+
declare function renumberParts(parts: PartitionedPart[], contentFingerprint?: string): PartitionedPart[];
|
|
205
|
+
/**
|
|
206
|
+
* Chooses a fence longer than any backtick run in `content`, so a file that
|
|
207
|
+
* contains a fenced block cannot break out of its own code block.
|
|
208
|
+
*/
|
|
209
|
+
declare function fenceForContent(content: string): string;
|
|
210
|
+
/**
|
|
211
|
+
* A fence longer than any backtick run in `content`, never shorter than
|
|
212
|
+
* `minimum`.
|
|
213
|
+
*
|
|
214
|
+
* Every fenced block the generator writes goes through this, not just file
|
|
215
|
+
* bodies: a *filename* may contain a backtick run (it is a legal name), and a
|
|
216
|
+
* fixed three-backtick fence around the `## Files` list let such a name close
|
|
217
|
+
* the block and turn the following filenames into live document text.
|
|
218
|
+
*/
|
|
219
|
+
declare function fenceForText(content: string, minimum: number): string;
|
|
220
|
+
/**
|
|
221
|
+
* Stable, injective anchor for a file path.
|
|
222
|
+
*
|
|
223
|
+
* The previous slug was not injective (`src/a-b.ts` and `src/a/b.ts` produced the
|
|
224
|
+
* same anchor) and, more importantly, no part file ever emitted the anchors that
|
|
225
|
+
* `reports/navigation.md` linked to — every file link was dead.
|
|
226
|
+
*/
|
|
227
|
+
declare function anchorForPath(filePath: string): string;
|
|
228
|
+
|
|
229
|
+
interface PreviewResult {
|
|
230
|
+
content: string;
|
|
231
|
+
isTruncated: boolean;
|
|
232
|
+
originalLines: number;
|
|
233
|
+
includedLines: number;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Line count with one consistent convention: a trailing newline does not start
|
|
237
|
+
* a new line, and an empty string has zero lines.
|
|
238
|
+
*
|
|
239
|
+
* Previously `index.ts` recorded 0 for an empty file while `preview.ts` recorded
|
|
240
|
+
* 1, and every newline-terminated file was over-counted by one.
|
|
241
|
+
*/
|
|
242
|
+
declare function countLines(content: string): number;
|
|
243
|
+
/**
|
|
244
|
+
* Truncates to a UTF-8 byte budget without splitting a multi-byte character.
|
|
245
|
+
*/
|
|
246
|
+
declare function truncateToBytes(content: string, maxBytes: number): {
|
|
247
|
+
content: string;
|
|
248
|
+
isTruncated: boolean;
|
|
249
|
+
};
|
|
250
|
+
declare function truncatePreview(content: string, headLines: number, tailLines: number): PreviewResult;
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Everything preset detection is allowed to look at.
|
|
254
|
+
*
|
|
255
|
+
* Built once per run: the previous implementation re-read `package.json` inside
|
|
256
|
+
* every detector, which was both slow and incapable of seeing anything outside
|
|
257
|
+
* the JavaScript ecosystem.
|
|
258
|
+
*
|
|
259
|
+
* Detection stays bounded: the root is read once, and a workspace root (and only
|
|
260
|
+
* a workspace root) additionally looks one level into its packages so a Next.js
|
|
261
|
+
* app in `apps/web` is still a Next.js repository — see {@link isWorkspace}.
|
|
262
|
+
*/
|
|
263
|
+
interface PresetContext {
|
|
264
|
+
cwd: string;
|
|
265
|
+
/** Top-level file names, e.g. `next.config.ts`. */
|
|
266
|
+
files: ReadonlySet<string>;
|
|
267
|
+
/** Top-level directory names. */
|
|
268
|
+
dirs: ReadonlySet<string>;
|
|
269
|
+
/** `dependencies` + `devDependencies` + `peerDependencies` names, root and workspace. */
|
|
270
|
+
dependencies: ReadonlySet<string>;
|
|
271
|
+
/** Small snapshots of well-known manifests, for keyword detection. */
|
|
272
|
+
manifests: Readonly<Record<string, string>>;
|
|
273
|
+
/** True when the root declares a workspace (`pnpm-workspace.yaml`, `workspaces`, nx/turbo/lerna). */
|
|
274
|
+
isWorkspace: boolean;
|
|
275
|
+
/**
|
|
276
|
+
* Top-level entry names of the workspace's packages, deduplicated.
|
|
277
|
+
*
|
|
278
|
+
* Flattened on purpose: a detector only needs "does some package carry a
|
|
279
|
+
* `next.config.*`/`vite.config.*` marker", not which package it was.
|
|
280
|
+
*/
|
|
281
|
+
workspaceFiles: ReadonlySet<string>;
|
|
282
|
+
/**
|
|
283
|
+
* `.md`/`.mdx` files at the root and up to two directories down.
|
|
284
|
+
*
|
|
285
|
+
* Only counted when the root has no `package.json` (the `docs` preset's other
|
|
286
|
+
* condition), so ordinary repositories pay nothing for it.
|
|
287
|
+
*/
|
|
288
|
+
markdownCount: number;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
interface PresetDefinition {
|
|
292
|
+
readonly name: string;
|
|
293
|
+
/** Higher wins. Framework presets outrank repository shapes. */
|
|
294
|
+
readonly priority: number;
|
|
295
|
+
detect(context: PresetContext): boolean;
|
|
296
|
+
readonly layers: LayerRule[];
|
|
297
|
+
readonly kinds: KindRule[];
|
|
298
|
+
/** Ecosystem-specific ignores, merged with the user's `excludePatterns`. */
|
|
299
|
+
readonly exclude?: string[];
|
|
300
|
+
/** Advice for the setup wizard. Never applied automatically. */
|
|
301
|
+
readonly recommendedBudget?: ByteSize;
|
|
302
|
+
}
|
|
303
|
+
interface DetectedPreset {
|
|
304
|
+
presetName: string;
|
|
305
|
+
layers: LayerRule[];
|
|
306
|
+
kinds: KindRule[];
|
|
307
|
+
/** Ecosystem ignores the scanner should apply in addition to the user's. */
|
|
308
|
+
exclude: string[];
|
|
309
|
+
recommendedBudget: ByteSize | null;
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Every preset, ordered by priority.
|
|
313
|
+
*
|
|
314
|
+
* Ordering is the whole detection model: the first definition whose `detect`
|
|
315
|
+
* returns true wins, so the list is sorted once at module load rather than being
|
|
316
|
+
* hand-maintained in the right order.
|
|
317
|
+
*/
|
|
318
|
+
declare const PRESETS: readonly PresetDefinition[];
|
|
319
|
+
/** Every preset name, in priority order — used by the CLI and the wizard. */
|
|
320
|
+
declare function listPresetNames(): string[];
|
|
321
|
+
declare function getPreset(name: string): PresetDefinition | undefined;
|
|
322
|
+
/** Detects the preset for `cwd`, reading the repository once. */
|
|
323
|
+
declare function detectPreset(cwd: string, forced?: string): DetectedPreset;
|
|
324
|
+
|
|
325
|
+
declare function isBinaryExtension(filePath: string): boolean;
|
|
326
|
+
declare function isBinaryBuffer(buffer: Buffer): boolean;
|
|
327
|
+
interface DecodedText {
|
|
328
|
+
/** Decoded body. Empty when `binary` is true. */
|
|
329
|
+
text: string;
|
|
330
|
+
encoding: TextEncoding;
|
|
331
|
+
/** Byte count that could not be decoded as UTF-8. Zero for well-formed input. */
|
|
332
|
+
invalidBytes: number;
|
|
333
|
+
/** True when the bytes are not text: a NUL, or a payload full of control characters. */
|
|
334
|
+
binary: boolean;
|
|
335
|
+
/** Human-readable reason the bytes were rejected, when `binary` is true. */
|
|
336
|
+
binaryReason?: string;
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Counts the bytes that are not part of a well-formed UTF-8 sequence.
|
|
340
|
+
*
|
|
341
|
+
* `buffer.toString("utf8")` silently substitutes U+FFFD for every bad sequence, which is how a
|
|
342
|
+
* Latin-1 source file used to reach the snapshot as `caf\uFFFD` with no warning anywhere. This
|
|
343
|
+
* gives the caller the number it needs to say so.
|
|
344
|
+
*/
|
|
345
|
+
declare function countInvalidUtf8Bytes(buffer: Buffer): number;
|
|
346
|
+
/**
|
|
347
|
+
* Decodes a file buffer to text without guessing silently.
|
|
348
|
+
*
|
|
349
|
+
* Order matters. A UTF-16 file is mostly NUL bytes, and NUL is perfectly valid UTF-8, so asking
|
|
350
|
+
* "is this valid UTF-8?" first would answer yes and hand the caller a string full of NULs. The
|
|
351
|
+
* BOM check and the UTF-16 shape test therefore run before the UTF-8 validity test.
|
|
352
|
+
*/
|
|
353
|
+
declare function decodeText(buffer: Buffer): DecodedText;
|
|
354
|
+
declare function isBinaryFile(absolutePath: string): boolean;
|
|
355
|
+
|
|
356
|
+
declare function detectLanguage(filePath: string): string;
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Redacts secrets from `content`.
|
|
360
|
+
*
|
|
361
|
+
* - Replaces only the matched span, so unrelated code sharing the line survives
|
|
362
|
+
* (the previous implementation discarded the whole line).
|
|
363
|
+
* - When a rule declares `valueGroup`, only that capture group is masked, so
|
|
364
|
+
* `dbPassword: "…"` keeps its key and stays parseable.
|
|
365
|
+
* - Masks PEM private-key blocks as a unit; per-line rules can only see the
|
|
366
|
+
* `BEGIN` header, which left every body line of a real key in the output.
|
|
367
|
+
* - Records `{ rule, line, path }` only. No excerpt of the source is retained,
|
|
368
|
+
* because that excerpt was being written verbatim into `reports/audit.md`.
|
|
369
|
+
*/
|
|
370
|
+
declare function scanAndRedactSecrets(content: string, rules: SecretRule[], filePath?: string): {
|
|
371
|
+
redacted: string;
|
|
372
|
+
findings: SecretFinding[];
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Validates a generated snapshot against the working tree and against the
|
|
377
|
+
* recorded configuration.
|
|
378
|
+
*
|
|
379
|
+
* Beyond source-file hashes this now also:
|
|
380
|
+
* - refuses a manifest that does not have the expected shape (previously threw
|
|
381
|
+
* an uncaught `TypeError`),
|
|
382
|
+
* - verifies every emitted `parts/*.md` against its recorded `byteSize`/`sha256`
|
|
383
|
+
* (previously a deleted or corrupted part still reported `valid: true`),
|
|
384
|
+
* - re-checks the effective configuration and the part/file bookkeeping,
|
|
385
|
+
* - verifies monolith artefact digests when present.
|
|
386
|
+
*/
|
|
387
|
+
declare function validateSnapshot(configInput: ResolvedSnapshotConfig | SnapshotConfig): ValidationResult;
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* The published identity of this package: its name and its version.
|
|
391
|
+
*
|
|
392
|
+
* Both live here rather than in `index.ts` because the wizard needs them and
|
|
393
|
+
* `index.ts` re-exports the wizard. Importing the barrel from the wizard closes a
|
|
394
|
+
* cycle, and a cycle over a `const` is read at call time — fine under ESM, a trap
|
|
395
|
+
* under CJS, where the first access can land before the assignment runs. `index.ts`
|
|
396
|
+
* re-exports these two names, so the public API keeps them at the same place.
|
|
397
|
+
*
|
|
398
|
+
* `package.json` carries the same version; `npm run verify` compares the two.
|
|
399
|
+
*/
|
|
400
|
+
declare const GENERATOR_NAME = "codebase-snapshot";
|
|
401
|
+
declare const GENERATOR_VERSION = "4.5.0";
|
|
402
|
+
|
|
403
|
+
declare const DEFAULT_SECRET_PATTERNS: SecretRule[];
|
|
404
|
+
|
|
405
|
+
interface ExplainResult {
|
|
406
|
+
/** Path relative to the configured cwd, POSIX separators. */
|
|
407
|
+
path: string;
|
|
408
|
+
/** Whether the path exists on disk. */
|
|
409
|
+
exists: boolean;
|
|
410
|
+
/** Whether the scanner would include it in the next snapshot. */
|
|
411
|
+
included: boolean;
|
|
412
|
+
/** Why it is excluded, when it is. Mirrors `SkipRecord.reason`. */
|
|
413
|
+
reason?: string;
|
|
414
|
+
detail?: string;
|
|
415
|
+
layer?: string;
|
|
416
|
+
layerMatch?: string;
|
|
417
|
+
kind?: string;
|
|
418
|
+
kindMatch?: string;
|
|
419
|
+
language?: string;
|
|
420
|
+
/** Part filename from the last written manifest, when one is available. */
|
|
421
|
+
part?: string | null;
|
|
422
|
+
inclusionMode?: string;
|
|
423
|
+
lines?: number;
|
|
424
|
+
bytes?: number;
|
|
425
|
+
/** Present when the file is not plain UTF-8, so an explanation cannot quietly lie about it. */
|
|
426
|
+
encoding?: TextEncoding;
|
|
427
|
+
secretCount?: number;
|
|
428
|
+
secrets?: Array<{
|
|
429
|
+
rules: string[];
|
|
430
|
+
line: number;
|
|
431
|
+
}>;
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Explains what the scanner would do with one path.
|
|
435
|
+
*
|
|
436
|
+
* The whole point is that every decision is visible: which layer and kind rule
|
|
437
|
+
* matched, why a file was skipped, which part it landed in, and whether any
|
|
438
|
+
* secret rule fires. Re-running the real scanner is what keeps it honest.
|
|
439
|
+
*/
|
|
440
|
+
declare function explainPath(config: ResolvedSnapshotConfig, target: string): ExplainResult;
|
|
441
|
+
/** Renders an explanation for a terminal. */
|
|
442
|
+
declare function renderExplainText(result: ExplainResult): string;
|
|
443
|
+
|
|
444
|
+
interface ManifestDiff {
|
|
445
|
+
before: DiffSide;
|
|
446
|
+
after: DiffSide;
|
|
447
|
+
added: string[];
|
|
448
|
+
removed: string[];
|
|
449
|
+
changed: string[];
|
|
450
|
+
unchanged: number;
|
|
451
|
+
/** What differs beyond the per-file list: the configuration and the emitted payload. */
|
|
452
|
+
payload: PayloadDiff;
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* A difference the per-file list cannot express.
|
|
456
|
+
*
|
|
457
|
+
* Per-file comparison is `relativePath + hashSha256`, so a run that changes only
|
|
458
|
+
* the *emitted* payload — `--large-file-bytes` turning a large preview into its
|
|
459
|
+
* full body, different preview head/tail counts, different redaction rules,
|
|
460
|
+
* different artifact selection — used to report "0 changed, N unchanged" and
|
|
461
|
+
* nothing else. The manifests already record the effective-settings digest, the
|
|
462
|
+
* content fingerprint and the part count, so this block says plainly when the
|
|
463
|
+
* source files are identical but the snapshot handed to the model is not.
|
|
464
|
+
*/
|
|
465
|
+
interface PayloadDiff {
|
|
466
|
+
/** No source file was added, removed or changed. */
|
|
467
|
+
sourceFilesIdentical: boolean;
|
|
468
|
+
/** The emitted payload, or the configuration that produced it, differs. */
|
|
469
|
+
differs: boolean;
|
|
470
|
+
/** The generator or the effective settings differ, independent of any source edit. */
|
|
471
|
+
configurationDiffers: boolean;
|
|
472
|
+
generator: {
|
|
473
|
+
before: string;
|
|
474
|
+
after: string;
|
|
475
|
+
};
|
|
476
|
+
/** `null` when the manifest predates the digest (written before 4.0.5). */
|
|
477
|
+
configDigest: {
|
|
478
|
+
before: string | null;
|
|
479
|
+
after: string | null;
|
|
480
|
+
};
|
|
481
|
+
fingerprint: {
|
|
482
|
+
before: string;
|
|
483
|
+
after: string;
|
|
484
|
+
};
|
|
485
|
+
partsCount: {
|
|
486
|
+
before: number;
|
|
487
|
+
after: number;
|
|
488
|
+
};
|
|
489
|
+
/**
|
|
490
|
+
* The `schemaVersion` of both manifests.
|
|
491
|
+
*
|
|
492
|
+
* Two snapshots written under different schema versions can carry the same file
|
|
493
|
+
* hashes and still mean different things, so a mismatch is reported as a
|
|
494
|
+
* difference (never as a crash) and turns the payload block on.
|
|
495
|
+
*/
|
|
496
|
+
schemaVersion: {
|
|
497
|
+
before: string;
|
|
498
|
+
after: string;
|
|
499
|
+
};
|
|
500
|
+
/**
|
|
501
|
+
* The `parts` array compared entry by entry: a part added, removed, or
|
|
502
|
+
* re-rendered (different `byteSize`, `sha256`, `lines` or `fileCount`).
|
|
503
|
+
*
|
|
504
|
+
* The per-file list compares `relativePath + hashSha256` only, so a hand-edited
|
|
505
|
+
* or older manifest whose parts were repacked, renamed or deleted used to produce
|
|
506
|
+
* "+0 -0 ~0 =N unchanged" and exit 0. This block is rendered independently of
|
|
507
|
+
* `differs`, because a source edit already legitimately turns the file lists on.
|
|
508
|
+
*/
|
|
509
|
+
partsManifest: {
|
|
510
|
+
differs: boolean;
|
|
511
|
+
changes: string[];
|
|
512
|
+
};
|
|
513
|
+
layers: {
|
|
514
|
+
before: Record<string, number>;
|
|
515
|
+
after: Record<string, number>;
|
|
516
|
+
};
|
|
517
|
+
/** Human-readable statements, rendered by the terminal form. */
|
|
518
|
+
reasons: string[];
|
|
519
|
+
}
|
|
520
|
+
interface DiffSide {
|
|
521
|
+
generatedAt: string;
|
|
522
|
+
fingerprint: string;
|
|
523
|
+
fileCount: number;
|
|
524
|
+
partsCount: number;
|
|
525
|
+
}
|
|
526
|
+
/** Compares two manifests file by file. */
|
|
527
|
+
declare function compareManifests(before: Manifest, after: Manifest): ManifestDiff;
|
|
528
|
+
/** Loads a manifest from a snapshot directory or a `manifest.json` path. */
|
|
529
|
+
declare function loadManifest(target: string): Manifest;
|
|
530
|
+
/** Renders a diff for a terminal. */
|
|
531
|
+
declare function renderDiffText(diff: ManifestDiff): string;
|
|
532
|
+
|
|
533
|
+
interface PromptIO {
|
|
534
|
+
input: NodeJS.ReadStream;
|
|
535
|
+
output: NodeJS.WriteStream;
|
|
536
|
+
}
|
|
537
|
+
/** Thrown when the developer presses Ctrl+C or Ctrl+D mid-prompt. */
|
|
538
|
+
declare class PromptCancelled extends Error {
|
|
539
|
+
constructor();
|
|
540
|
+
}
|
|
541
|
+
/** Both ends must be a TTY before anything interactive is attempted. */
|
|
542
|
+
declare function isInteractive(io: PromptIO): boolean;
|
|
543
|
+
|
|
544
|
+
interface InitOptions {
|
|
545
|
+
cwd: string;
|
|
546
|
+
/** Force a preset instead of detecting one. */
|
|
547
|
+
preset?: string;
|
|
548
|
+
maxPartBytes?: ByteSize;
|
|
549
|
+
include?: string[];
|
|
550
|
+
exclude?: string[];
|
|
551
|
+
skipArtifacts?: string[];
|
|
552
|
+
onlyArtifacts?: string[];
|
|
553
|
+
includeSensitiveFiles?: boolean;
|
|
554
|
+
/** Accept every default without prompting. */
|
|
555
|
+
yes?: boolean;
|
|
556
|
+
/** Overwrite an existing config. */
|
|
557
|
+
force?: boolean;
|
|
558
|
+
/**
|
|
559
|
+
* Keep an existing config instead of refusing.
|
|
560
|
+
*
|
|
561
|
+
* `setup` writes a config only when the repository has none; when it has one, the rest of the
|
|
562
|
+
* setup work (scripts, devDependency, `.gitignore`) still happens and the file is left exactly as
|
|
563
|
+
* it is. `init` on its own keeps refusing — a user who typed `init` expects to be told the file
|
|
564
|
+
* already exists.
|
|
565
|
+
*/
|
|
566
|
+
reuseConfig?: boolean;
|
|
567
|
+
/** Render the config without writing anything. */
|
|
568
|
+
dryRun?: boolean;
|
|
569
|
+
outDir?: string;
|
|
570
|
+
io?: PromptIO;
|
|
571
|
+
}
|
|
572
|
+
/** What happened to the `devDependencies` entry that makes the npm scripts self-contained. */
|
|
573
|
+
interface InitDependency {
|
|
574
|
+
name: string;
|
|
575
|
+
/** The range now present in package.json, or a pre-existing declaration. */
|
|
576
|
+
range: string | null;
|
|
577
|
+
/** True when this run wrote the entry. */
|
|
578
|
+
added: boolean;
|
|
579
|
+
}
|
|
580
|
+
interface InitResult {
|
|
581
|
+
configPath: string;
|
|
582
|
+
configText: string;
|
|
583
|
+
written: boolean;
|
|
584
|
+
/** True when an existing config was kept instead of being written. */
|
|
585
|
+
reused: boolean;
|
|
586
|
+
preset: string;
|
|
587
|
+
scripts: string[];
|
|
588
|
+
gitignoreEntries: string[];
|
|
589
|
+
dependency: InitDependency;
|
|
590
|
+
/** Human-readable notes about anything that was skipped or could not be done. */
|
|
591
|
+
notes: string[];
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* Runs the interactive setup, or writes a config from flags.
|
|
595
|
+
*
|
|
596
|
+
* Interactive only when both ends are a TTY and `--yes` was not passed; every
|
|
597
|
+
* other path uses the flag values and the detected preset, and prints what it
|
|
598
|
+
* decided rather than prompting.
|
|
599
|
+
*/
|
|
600
|
+
declare function runInit(options: InitOptions): Promise<InitResult>;
|
|
601
|
+
|
|
602
|
+
/** What the wizard decided, after prompts or flags. */
|
|
603
|
+
interface WizardAnswers {
|
|
604
|
+
preset: string;
|
|
605
|
+
branch: string;
|
|
606
|
+
maxPartBytes: ByteSize;
|
|
607
|
+
largeFileBytes: ByteSize;
|
|
608
|
+
include: string[];
|
|
609
|
+
excludePatterns: string[];
|
|
610
|
+
artifacts: Required<ArtifactSelection>;
|
|
611
|
+
includeSensitiveFiles: boolean;
|
|
612
|
+
outDir: string;
|
|
613
|
+
}
|
|
614
|
+
declare const DEFAULT_ANSWERS: WizardAnswers;
|
|
615
|
+
/**
|
|
616
|
+
* Renders the full, self-documenting config — the only shape `init` writes.
|
|
617
|
+
*
|
|
618
|
+
* Every setting appears at its current value, commented and grouped, because
|
|
619
|
+
* most developers never open the documentation — a file that lists only their
|
|
620
|
+
* changes cannot tell them what else exists. Deleting any line is safe: the
|
|
621
|
+
* defaults are real.
|
|
622
|
+
*
|
|
623
|
+
* There used to be a `--minimal` variant that wrote only the settings that
|
|
624
|
+
* differed from the defaults. It produced a seven-line file for a Next.js
|
|
625
|
+
* project — `export default { preset: "nextjs" }` — which is the one file the
|
|
626
|
+
* user is supposed to open to change things, holding none of the things. The
|
|
627
|
+
* flag is gone: one canonical config, always complete.
|
|
628
|
+
*
|
|
629
|
+
* The object is assigned to `config` and exported by name. An anonymous
|
|
630
|
+
* `export default { … }` is what every lint preset derived from
|
|
631
|
+
* `eslint-config-next` reports as `import/no-anonymous-default-export`, so the
|
|
632
|
+
* generated file was a lint error on the first save in a Next.js project — the
|
|
633
|
+
* preset this tool detects most often.
|
|
634
|
+
*/
|
|
635
|
+
declare function renderConfig(answers: WizardAnswers): string;
|
|
636
|
+
|
|
637
|
+
interface HeuristicResult {
|
|
638
|
+
summary: SourceSummary;
|
|
639
|
+
/**
|
|
640
|
+
* `ast` only when a real parser produced this (TypeScript, and the script
|
|
641
|
+
* block of a Vue/Svelte/Astro component). Everything else is `heuristic`, and
|
|
642
|
+
* the manifest says so — a line-scan is not a parse.
|
|
643
|
+
*/
|
|
644
|
+
quality: "ast" | "heuristic";
|
|
645
|
+
}
|
|
646
|
+
interface LanguageProfile {
|
|
647
|
+
readonly id: string;
|
|
648
|
+
readonly extensions: readonly string[];
|
|
649
|
+
summarize(path: string, content: string): HeuristicResult;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* Every heuristic profile, in no particular order — lookup is by extension.
|
|
654
|
+
*
|
|
655
|
+
* Adding a language is one file exporting a `LanguageProfile` plus one line
|
|
656
|
+
* here; nothing else in the pipeline changes.
|
|
657
|
+
*/
|
|
658
|
+
declare const LANGUAGE_PROFILES: readonly LanguageProfile[];
|
|
659
|
+
/** The profile that owns `path`, or `null` when no profile claims it. */
|
|
660
|
+
declare function profileForPath(path: string): LanguageProfile | null;
|
|
661
|
+
/**
|
|
662
|
+
* Summarizes `path` with whichever profile claims it.
|
|
663
|
+
*
|
|
664
|
+
* Returns `null` for files no profile handles — those still enter the snapshot,
|
|
665
|
+
* they just carry no structural summary.
|
|
666
|
+
*/
|
|
667
|
+
declare function summarizeWithProfile(path: string, content: string): HeuristicResult | null;
|
|
668
|
+
|
|
669
|
+
declare const SCHEMA_VERSION = "3.0.0";
|
|
670
|
+
interface GenerateOptions {
|
|
671
|
+
/** Compute the full manifest but write nothing to disk. */
|
|
672
|
+
dryRun?: boolean;
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* Resolves and validates the output directories.
|
|
676
|
+
*
|
|
677
|
+
* `outDir` was previously deleted with `rmSync` before the replacement was
|
|
678
|
+
* renamed into place, so `outDir: "."` erased the whole repository and a
|
|
679
|
+
* nested `tmpDir` erased its own parent. These guards make that impossible.
|
|
680
|
+
*/
|
|
681
|
+
declare function resolveSafeOutputDirs(config: {
|
|
682
|
+
cwd: string;
|
|
683
|
+
outDir: string;
|
|
684
|
+
tmpDir: string;
|
|
685
|
+
}): {
|
|
686
|
+
outDir: string;
|
|
687
|
+
tmpDir: string;
|
|
688
|
+
};
|
|
689
|
+
/**
|
|
690
|
+
* Refuses to replace a directory that this generator did not write.
|
|
691
|
+
*
|
|
692
|
+
* Path checks alone cannot make the swap safe: `outDir: "src"` is a plausible
|
|
693
|
+
* typo, and a user directory that happens to contain a `.codebase-snapshot` file
|
|
694
|
+
* or a manifest-shaped JSON document is not a snapshot. The staging root is
|
|
695
|
+
* checked separately by {@link assertStagingRootUsable}, because it holds one
|
|
696
|
+
* directory per run rather than a single snapshot.
|
|
697
|
+
*/
|
|
698
|
+
declare function assertReplaceable(target: string, label: "outDir" | "tmpDir", secretRules?: readonly SecretRule[]): void;
|
|
699
|
+
/**
|
|
700
|
+
* Generates the snapshot.
|
|
701
|
+
*
|
|
702
|
+
* Synchronous by design: it previously carried an `async` signature while doing
|
|
703
|
+
* only blocking I/O, which promised concurrency that did not exist. Existing
|
|
704
|
+
* `await generateSnapshot(...)` call sites continue to work.
|
|
705
|
+
*/
|
|
706
|
+
declare function generateSnapshot(configInput?: SnapshotConfig | ResolvedSnapshotConfig | null, options?: GenerateOptions): Manifest;
|
|
707
|
+
/** Promise-returning wrapper for consumers that prefer an async call site. */
|
|
708
|
+
declare function generateSnapshotAsync(configInput?: SnapshotConfig | ResolvedSnapshotConfig | null, options?: GenerateOptions): Promise<Manifest>;
|
|
709
|
+
|
|
710
|
+
export { ARTIFACT_NAMES, ArtifactSelection, ByteSize, CliOptions, DEFAULT_ANSWERS, DEFAULT_SECRET_PATTERNS, DiscoveryMode, type ExplainResult, FileEntry, GENERATOR_NAME, GENERATOR_VERSION, type GenerateOptions, type HeuristicResult, type InitDependency, type InitOptions, type InitResult, JsonSummary, KindRule, LANGUAGE_PROFILES, type LanguageProfile, LayerRule, MIN_MAX_PART_BYTES, Manifest, type ManifestDiff, MarkdownSummary, PRESETS, PartMetadata, PromptCancelled, ResolvedSnapshotConfig, SCHEMA_VERSION, SecretFinding, SecretRule, SkipRecord, SnapshotConfig, SourceSummary, TextEncoding, ValidationResult, type WizardAnswers, anchorForPath, assertReplaceable, assertUsableConfigInput, collectFiles, compareManifests, countInvalidUtf8Bytes, countLines, decodeText, defineConfig, detectLanguage, detectPreset, explainPath, fenceForContent, fenceForText, generateProjectTree, generateSnapshot, generateSnapshotAsync, getPreset, globToRegExp, isBinaryBuffer, isBinaryExtension, isBinaryFile, isInteractive, isResolvedConfig, isSensitivePath, listPresetNames, loadConfigFile, loadConfigFileDetailed, loadManifest, markResolvedConfig, matchRule, matchesExclude, normalizeResolvedConfig, parseByteSize, partitionFiles, profileForPath, renderConfig, renderDiffText, renderExplainText, renumberParts, resolveConfig, resolveSafeOutputDirs, runInit, scanAndRedactSecrets, stripJsonComments, summarizeJson, summarizeMarkdown, summarizeTypeScriptSource, summarizeWithProfile, supportsTypeScriptImports, truncatePreview, truncateToBytes, validateSnapshot };
|