codebase-snapshot 0.9.12 → 0.9.14
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 +379 -0
- package/README.md +1 -1
- package/dist/bin.js +1313 -633
- package/dist/{chunk-3H45YMBG.js → chunk-DUZK5VL2.js} +1413 -767
- package/dist/{chunk-5OR3WVPW.js → chunk-J4W74N4Y.js} +144 -44
- package/dist/cli.cjs +1305 -628
- package/dist/cli.d.cts +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +43 -54
- package/dist/{generate-worker-D5Xgh27c.d.cts → generate-worker-D_mUjgXw.d.cts} +21 -5
- package/dist/{generate-worker-D5Xgh27c.d.ts → generate-worker-D_mUjgXw.d.ts} +21 -5
- package/dist/index.cjs +1389 -760
- package/dist/index.d.cts +58 -35
- package/dist/index.d.ts +58 -35
- package/dist/index.js +1 -1
- package/dist/mcp.cjs +973 -405
- package/dist/mcp.d.cts +67 -67
- package/dist/mcp.d.ts +67 -67
- package/dist/mcp.js +2 -2
- package/dist/{types-CTewen3J.d.cts → types-DIX6zWSt.d.cts} +15 -0
- package/dist/{types-CTewen3J.d.ts → types-DIX6zWSt.d.ts} +15 -0
- package/docs/cli.md +8 -6
- package/docs/configuration.md +44 -10
- package/docs/mcp.md +16 -3
- package/docs/output.md +13 -0
- package/package.json +1 -1
package/dist/mcp.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { G as GenerateSummary, W as WorkerResults, g as generateInWorker } from './generate-worker-
|
|
1
|
+
export { G as GenerateSummary, W as WorkerResults, g as generateInWorker } from './generate-worker-D_mUjgXw.cjs';
|
|
2
2
|
import { Readable, Writable } from 'node:stream';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -101,72 +101,6 @@ interface InputSchema extends JsonSchema {
|
|
|
101
101
|
properties: Record<string, JsonSchema>;
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
type ProtocolEra = "legacy" | "modern";
|
|
105
|
-
/** Per-connection state. The modern era has no session, so this stays empty until legacy init. */
|
|
106
|
-
interface SessionState {
|
|
107
|
-
/** Which era the connection committed to, or null before the first decisive request. */
|
|
108
|
-
era: ProtocolEra | null;
|
|
109
|
-
/** The revision in force. Null in legacy until `initialize` has been negotiated. */
|
|
110
|
-
protocolVersion: string | null;
|
|
111
|
-
/** True once `initialize` has been answered, which is the legacy lifecycle's gate. */
|
|
112
|
-
ready: boolean;
|
|
113
|
-
}
|
|
114
|
-
declare function createSession(): SessionState;
|
|
115
|
-
/** The modern request metadata, when a request carries it at all. */
|
|
116
|
-
interface ModernMeta {
|
|
117
|
-
version: string;
|
|
118
|
-
clientCapabilities: JsonObject;
|
|
119
|
-
clientInfo: JsonObject | null;
|
|
120
|
-
}
|
|
121
|
-
type MetaInspection = {
|
|
122
|
-
kind: "absent";
|
|
123
|
-
} | {
|
|
124
|
-
kind: "incomplete";
|
|
125
|
-
missing: string;
|
|
126
|
-
} | {
|
|
127
|
-
kind: "present";
|
|
128
|
-
meta: ModernMeta;
|
|
129
|
-
};
|
|
130
|
-
/**
|
|
131
|
-
* Reads `_meta` from a request.
|
|
132
|
-
*
|
|
133
|
-
* `absent` and `incomplete` are different answers: a request with no `_meta` at all is a legacy
|
|
134
|
-
* client, while a request with `_meta` that lacks a required key is a modern client making a
|
|
135
|
-
* mistake — and telling those two apart is what keeps the legacy era from being rejected for a
|
|
136
|
-
* field it never had.
|
|
137
|
-
*/
|
|
138
|
-
declare function inspectModernMeta(params: JsonObject | undefined): MetaInspection;
|
|
139
|
-
type EraDecision = {
|
|
140
|
-
kind: "era";
|
|
141
|
-
era: ProtocolEra;
|
|
142
|
-
version: string | null;
|
|
143
|
-
} | {
|
|
144
|
-
kind: "error";
|
|
145
|
-
code: number;
|
|
146
|
-
message: string;
|
|
147
|
-
data?: unknown;
|
|
148
|
-
};
|
|
149
|
-
/**
|
|
150
|
-
* Decides which era a request belongs to, and whether its version is acceptable.
|
|
151
|
-
*
|
|
152
|
-
* Once a connection commits to an era it stays committed. A legacy client that suddenly sends
|
|
153
|
-
* modern `_meta` is confused rather than clever, and silently switching would answer half a
|
|
154
|
-
* session in each shape.
|
|
155
|
-
*/
|
|
156
|
-
declare function classifyRequest(method: string, params: JsonObject | undefined, session: SessionState): EraDecision;
|
|
157
|
-
interface LegacyNegotiation {
|
|
158
|
-
version: string;
|
|
159
|
-
/** False when the client asked for something we cannot speak and was negotiated down. */
|
|
160
|
-
matched: boolean;
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* Legacy version negotiation: echo a supported request, otherwise answer with our best.
|
|
164
|
-
*
|
|
165
|
-
* A client that asked for a modern revision here gets the newest *legacy* revision, not an error:
|
|
166
|
-
* under this lifecycle the server's answer is the offer, and the client decides.
|
|
167
|
-
*/
|
|
168
|
-
declare function negotiateLegacyVersion(requested: unknown): LegacyNegotiation;
|
|
169
|
-
|
|
170
104
|
/**
|
|
171
105
|
* The capability records the MCP server serves.
|
|
172
106
|
*
|
|
@@ -279,6 +213,72 @@ interface McpServerInfo {
|
|
|
279
213
|
websiteUrl: string;
|
|
280
214
|
}
|
|
281
215
|
|
|
216
|
+
type ProtocolEra = "legacy" | "modern";
|
|
217
|
+
/** Per-connection state. The modern era has no session, so this stays empty until legacy init. */
|
|
218
|
+
interface SessionState {
|
|
219
|
+
/** Which era the connection committed to, or null before the first decisive request. */
|
|
220
|
+
era: ProtocolEra | null;
|
|
221
|
+
/** The revision in force. Null in legacy until `initialize` has been negotiated. */
|
|
222
|
+
protocolVersion: string | null;
|
|
223
|
+
/** True once `initialize` has been answered, which is the legacy lifecycle's gate. */
|
|
224
|
+
ready: boolean;
|
|
225
|
+
}
|
|
226
|
+
declare function createSession(): SessionState;
|
|
227
|
+
/** The modern request metadata, when a request carries it at all. */
|
|
228
|
+
interface ModernMeta {
|
|
229
|
+
version: string;
|
|
230
|
+
clientCapabilities: JsonObject;
|
|
231
|
+
clientInfo: JsonObject | null;
|
|
232
|
+
}
|
|
233
|
+
type MetaInspection = {
|
|
234
|
+
kind: "absent";
|
|
235
|
+
} | {
|
|
236
|
+
kind: "incomplete";
|
|
237
|
+
missing: string;
|
|
238
|
+
} | {
|
|
239
|
+
kind: "present";
|
|
240
|
+
meta: ModernMeta;
|
|
241
|
+
};
|
|
242
|
+
/**
|
|
243
|
+
* Reads `_meta` from a request.
|
|
244
|
+
*
|
|
245
|
+
* `absent` and `incomplete` are different answers: a request with no `_meta` at all is a legacy
|
|
246
|
+
* client, while a request with `_meta` that lacks a required key is a modern client making a
|
|
247
|
+
* mistake — and telling those two apart is what keeps the legacy era from being rejected for a
|
|
248
|
+
* field it never had.
|
|
249
|
+
*/
|
|
250
|
+
declare function inspectModernMeta(params: JsonObject | undefined): MetaInspection;
|
|
251
|
+
type EraDecision = {
|
|
252
|
+
kind: "era";
|
|
253
|
+
era: ProtocolEra;
|
|
254
|
+
version: string | null;
|
|
255
|
+
} | {
|
|
256
|
+
kind: "error";
|
|
257
|
+
code: number;
|
|
258
|
+
message: string;
|
|
259
|
+
data?: unknown;
|
|
260
|
+
};
|
|
261
|
+
/**
|
|
262
|
+
* Decides which era a request belongs to, and whether its version is acceptable.
|
|
263
|
+
*
|
|
264
|
+
* Once a connection commits to an era it stays committed. A legacy client that suddenly sends
|
|
265
|
+
* modern `_meta` is confused rather than clever, and silently switching would answer half a
|
|
266
|
+
* session in each shape.
|
|
267
|
+
*/
|
|
268
|
+
declare function classifyRequest(method: string, params: JsonObject | undefined, session: SessionState): EraDecision;
|
|
269
|
+
interface LegacyNegotiation {
|
|
270
|
+
version: string;
|
|
271
|
+
/** False when the client asked for something we cannot speak and was negotiated down. */
|
|
272
|
+
matched: boolean;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Legacy version negotiation: echo a supported request, otherwise answer with our best.
|
|
276
|
+
*
|
|
277
|
+
* A client that asked for a modern revision here gets the newest *legacy* revision, not an error:
|
|
278
|
+
* under this lifecycle the server's answer is the offer, and the client decides.
|
|
279
|
+
*/
|
|
280
|
+
declare function negotiateLegacyVersion(requested: unknown): LegacyNegotiation;
|
|
281
|
+
|
|
282
282
|
interface McpServerOptions {
|
|
283
283
|
info: McpServerInfo;
|
|
284
284
|
instructions: string;
|
package/dist/mcp.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { G as GenerateSummary, W as WorkerResults, g as generateInWorker } from './generate-worker-
|
|
1
|
+
export { G as GenerateSummary, W as WorkerResults, g as generateInWorker } from './generate-worker-D_mUjgXw.js';
|
|
2
2
|
import { Readable, Writable } from 'node:stream';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -101,72 +101,6 @@ interface InputSchema extends JsonSchema {
|
|
|
101
101
|
properties: Record<string, JsonSchema>;
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
type ProtocolEra = "legacy" | "modern";
|
|
105
|
-
/** Per-connection state. The modern era has no session, so this stays empty until legacy init. */
|
|
106
|
-
interface SessionState {
|
|
107
|
-
/** Which era the connection committed to, or null before the first decisive request. */
|
|
108
|
-
era: ProtocolEra | null;
|
|
109
|
-
/** The revision in force. Null in legacy until `initialize` has been negotiated. */
|
|
110
|
-
protocolVersion: string | null;
|
|
111
|
-
/** True once `initialize` has been answered, which is the legacy lifecycle's gate. */
|
|
112
|
-
ready: boolean;
|
|
113
|
-
}
|
|
114
|
-
declare function createSession(): SessionState;
|
|
115
|
-
/** The modern request metadata, when a request carries it at all. */
|
|
116
|
-
interface ModernMeta {
|
|
117
|
-
version: string;
|
|
118
|
-
clientCapabilities: JsonObject;
|
|
119
|
-
clientInfo: JsonObject | null;
|
|
120
|
-
}
|
|
121
|
-
type MetaInspection = {
|
|
122
|
-
kind: "absent";
|
|
123
|
-
} | {
|
|
124
|
-
kind: "incomplete";
|
|
125
|
-
missing: string;
|
|
126
|
-
} | {
|
|
127
|
-
kind: "present";
|
|
128
|
-
meta: ModernMeta;
|
|
129
|
-
};
|
|
130
|
-
/**
|
|
131
|
-
* Reads `_meta` from a request.
|
|
132
|
-
*
|
|
133
|
-
* `absent` and `incomplete` are different answers: a request with no `_meta` at all is a legacy
|
|
134
|
-
* client, while a request with `_meta` that lacks a required key is a modern client making a
|
|
135
|
-
* mistake — and telling those two apart is what keeps the legacy era from being rejected for a
|
|
136
|
-
* field it never had.
|
|
137
|
-
*/
|
|
138
|
-
declare function inspectModernMeta(params: JsonObject | undefined): MetaInspection;
|
|
139
|
-
type EraDecision = {
|
|
140
|
-
kind: "era";
|
|
141
|
-
era: ProtocolEra;
|
|
142
|
-
version: string | null;
|
|
143
|
-
} | {
|
|
144
|
-
kind: "error";
|
|
145
|
-
code: number;
|
|
146
|
-
message: string;
|
|
147
|
-
data?: unknown;
|
|
148
|
-
};
|
|
149
|
-
/**
|
|
150
|
-
* Decides which era a request belongs to, and whether its version is acceptable.
|
|
151
|
-
*
|
|
152
|
-
* Once a connection commits to an era it stays committed. A legacy client that suddenly sends
|
|
153
|
-
* modern `_meta` is confused rather than clever, and silently switching would answer half a
|
|
154
|
-
* session in each shape.
|
|
155
|
-
*/
|
|
156
|
-
declare function classifyRequest(method: string, params: JsonObject | undefined, session: SessionState): EraDecision;
|
|
157
|
-
interface LegacyNegotiation {
|
|
158
|
-
version: string;
|
|
159
|
-
/** False when the client asked for something we cannot speak and was negotiated down. */
|
|
160
|
-
matched: boolean;
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* Legacy version negotiation: echo a supported request, otherwise answer with our best.
|
|
164
|
-
*
|
|
165
|
-
* A client that asked for a modern revision here gets the newest *legacy* revision, not an error:
|
|
166
|
-
* under this lifecycle the server's answer is the offer, and the client decides.
|
|
167
|
-
*/
|
|
168
|
-
declare function negotiateLegacyVersion(requested: unknown): LegacyNegotiation;
|
|
169
|
-
|
|
170
104
|
/**
|
|
171
105
|
* The capability records the MCP server serves.
|
|
172
106
|
*
|
|
@@ -279,6 +213,72 @@ interface McpServerInfo {
|
|
|
279
213
|
websiteUrl: string;
|
|
280
214
|
}
|
|
281
215
|
|
|
216
|
+
type ProtocolEra = "legacy" | "modern";
|
|
217
|
+
/** Per-connection state. The modern era has no session, so this stays empty until legacy init. */
|
|
218
|
+
interface SessionState {
|
|
219
|
+
/** Which era the connection committed to, or null before the first decisive request. */
|
|
220
|
+
era: ProtocolEra | null;
|
|
221
|
+
/** The revision in force. Null in legacy until `initialize` has been negotiated. */
|
|
222
|
+
protocolVersion: string | null;
|
|
223
|
+
/** True once `initialize` has been answered, which is the legacy lifecycle's gate. */
|
|
224
|
+
ready: boolean;
|
|
225
|
+
}
|
|
226
|
+
declare function createSession(): SessionState;
|
|
227
|
+
/** The modern request metadata, when a request carries it at all. */
|
|
228
|
+
interface ModernMeta {
|
|
229
|
+
version: string;
|
|
230
|
+
clientCapabilities: JsonObject;
|
|
231
|
+
clientInfo: JsonObject | null;
|
|
232
|
+
}
|
|
233
|
+
type MetaInspection = {
|
|
234
|
+
kind: "absent";
|
|
235
|
+
} | {
|
|
236
|
+
kind: "incomplete";
|
|
237
|
+
missing: string;
|
|
238
|
+
} | {
|
|
239
|
+
kind: "present";
|
|
240
|
+
meta: ModernMeta;
|
|
241
|
+
};
|
|
242
|
+
/**
|
|
243
|
+
* Reads `_meta` from a request.
|
|
244
|
+
*
|
|
245
|
+
* `absent` and `incomplete` are different answers: a request with no `_meta` at all is a legacy
|
|
246
|
+
* client, while a request with `_meta` that lacks a required key is a modern client making a
|
|
247
|
+
* mistake — and telling those two apart is what keeps the legacy era from being rejected for a
|
|
248
|
+
* field it never had.
|
|
249
|
+
*/
|
|
250
|
+
declare function inspectModernMeta(params: JsonObject | undefined): MetaInspection;
|
|
251
|
+
type EraDecision = {
|
|
252
|
+
kind: "era";
|
|
253
|
+
era: ProtocolEra;
|
|
254
|
+
version: string | null;
|
|
255
|
+
} | {
|
|
256
|
+
kind: "error";
|
|
257
|
+
code: number;
|
|
258
|
+
message: string;
|
|
259
|
+
data?: unknown;
|
|
260
|
+
};
|
|
261
|
+
/**
|
|
262
|
+
* Decides which era a request belongs to, and whether its version is acceptable.
|
|
263
|
+
*
|
|
264
|
+
* Once a connection commits to an era it stays committed. A legacy client that suddenly sends
|
|
265
|
+
* modern `_meta` is confused rather than clever, and silently switching would answer half a
|
|
266
|
+
* session in each shape.
|
|
267
|
+
*/
|
|
268
|
+
declare function classifyRequest(method: string, params: JsonObject | undefined, session: SessionState): EraDecision;
|
|
269
|
+
interface LegacyNegotiation {
|
|
270
|
+
version: string;
|
|
271
|
+
/** False when the client asked for something we cannot speak and was negotiated down. */
|
|
272
|
+
matched: boolean;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Legacy version negotiation: echo a supported request, otherwise answer with our best.
|
|
276
|
+
*
|
|
277
|
+
* A client that asked for a modern revision here gets the newest *legacy* revision, not an error:
|
|
278
|
+
* under this lifecycle the server's answer is the offer, and the client decides.
|
|
279
|
+
*/
|
|
280
|
+
declare function negotiateLegacyVersion(requested: unknown): LegacyNegotiation;
|
|
281
|
+
|
|
282
282
|
interface McpServerOptions {
|
|
283
283
|
info: McpServerInfo;
|
|
284
284
|
instructions: string;
|
package/dist/mcp.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { ERROR_CODES, LEGACY_PROTOCOL_VERSIONS, MAX_LINE_BYTES, METHOD, MODERN_PROTOCOL_VERSIONS, SERVER_INSTRUCTIONS, SUPPORTED_PROTOCOL_VERSIONS, buildPrompts, buildResourceProvider, buildTools, classifyRequest, coerceNumericStrings, createLineReader, createMcpServer, createServer, createSession, createWriter, findSection, inspectModernMeta, negotiateLegacyVersion, parsePartSections, runStdioServer, validateArguments, windowText } from './chunk-
|
|
2
|
-
export { generateInWorker } from './chunk-
|
|
1
|
+
export { ERROR_CODES, LEGACY_PROTOCOL_VERSIONS, MAX_LINE_BYTES, METHOD, MODERN_PROTOCOL_VERSIONS, SERVER_INSTRUCTIONS, SUPPORTED_PROTOCOL_VERSIONS, buildPrompts, buildResourceProvider, buildTools, classifyRequest, coerceNumericStrings, createLineReader, createMcpServer, createServer, createSession, createWriter, findSection, inspectModernMeta, negotiateLegacyVersion, parsePartSections, runStdioServer, validateArguments, windowText } from './chunk-J4W74N4Y.js';
|
|
2
|
+
export { generateInWorker } from './chunk-DUZK5VL2.js';
|
|
@@ -568,9 +568,24 @@ interface CliOptions {
|
|
|
568
568
|
includeSensitiveFiles?: boolean;
|
|
569
569
|
maxMonolithBytes?: number | null;
|
|
570
570
|
maxPartBytes?: number;
|
|
571
|
+
maxPartTokens?: ByteSize | null;
|
|
571
572
|
largeFileBytes?: number;
|
|
572
573
|
/** Artifact names to suppress for this invocation (`--skip-artifacts`). */
|
|
573
574
|
skipArtifacts?: string[];
|
|
575
|
+
/**
|
|
576
|
+
* Artifact names to keep, with everything else off (`--only-artifacts`).
|
|
577
|
+
*
|
|
578
|
+
* Separate from the derived `skipArtifacts` because the two are not interchangeable: "only
|
|
579
|
+
* these" is a statement about the whole selection, and the suppression list cannot express it
|
|
580
|
+
* when the configuration has already turned one of the named artifacts off.
|
|
581
|
+
*/
|
|
582
|
+
onlyArtifacts?: string[];
|
|
583
|
+
discovery?: DiscoveryMode;
|
|
584
|
+
changedSince?: string;
|
|
585
|
+
staged?: boolean;
|
|
586
|
+
withDependents?: boolean;
|
|
587
|
+
skeleton?: boolean;
|
|
588
|
+
sarif?: boolean;
|
|
574
589
|
}
|
|
575
590
|
interface ValidationDrift {
|
|
576
591
|
missingInSnapshot: string[];
|
|
@@ -568,9 +568,24 @@ interface CliOptions {
|
|
|
568
568
|
includeSensitiveFiles?: boolean;
|
|
569
569
|
maxMonolithBytes?: number | null;
|
|
570
570
|
maxPartBytes?: number;
|
|
571
|
+
maxPartTokens?: ByteSize | null;
|
|
571
572
|
largeFileBytes?: number;
|
|
572
573
|
/** Artifact names to suppress for this invocation (`--skip-artifacts`). */
|
|
573
574
|
skipArtifacts?: string[];
|
|
575
|
+
/**
|
|
576
|
+
* Artifact names to keep, with everything else off (`--only-artifacts`).
|
|
577
|
+
*
|
|
578
|
+
* Separate from the derived `skipArtifacts` because the two are not interchangeable: "only
|
|
579
|
+
* these" is a statement about the whole selection, and the suppression list cannot express it
|
|
580
|
+
* when the configuration has already turned one of the named artifacts off.
|
|
581
|
+
*/
|
|
582
|
+
onlyArtifacts?: string[];
|
|
583
|
+
discovery?: DiscoveryMode;
|
|
584
|
+
changedSince?: string;
|
|
585
|
+
staged?: boolean;
|
|
586
|
+
withDependents?: boolean;
|
|
587
|
+
skeleton?: boolean;
|
|
588
|
+
sarif?: boolean;
|
|
574
589
|
}
|
|
575
590
|
interface ValidationDrift {
|
|
576
591
|
missingInSnapshot: string[];
|
package/docs/cli.md
CHANGED
|
@@ -174,7 +174,7 @@ The whole first run in one command, with defaults and no prompts. This is the sa
|
|
|
174
174
|
🚀 Setup for: /path/to/repo
|
|
175
175
|
✅ Created snapshot.config.ts (preset: nextjs)
|
|
176
176
|
Added npm scripts: snapshot, snapshot:validate
|
|
177
|
-
Added devDependency: codebase-snapshot ^0.9.
|
|
177
|
+
Added devDependency: codebase-snapshot ^0.9.14
|
|
178
178
|
Added to .gitignore: snapshot/, snapshot.tmp/
|
|
179
179
|
⏳ Installing dependencies…
|
|
180
180
|
✅ Installed dependencies (npm install, 2.1s)
|
|
@@ -192,7 +192,9 @@ The whole first run in one command, with defaults and no prompts. This is the sa
|
|
|
192
192
|
`generate` and `init`.
|
|
193
193
|
- `--format json` emits **one** document:
|
|
194
194
|
`{ "setup": { configPath, configReused, preset, scripts, dependency, gitignoreEntries, install, notes }, "manifest": { … } }`,
|
|
195
|
-
where `manifest` is the same object `generate --format json` writes.
|
|
195
|
+
where `manifest` is the same object `generate --format json` writes. If that generate fails, the
|
|
196
|
+
document is `{ "error": { … }, "setup": { … }, "manifest": null }` and the exit code is the
|
|
197
|
+
generate's: one document per invocation, a payload or an error, never a payload holding an error.
|
|
196
198
|
- If `outDir` holds files this tool did not write, `setup` still refuses to replace it (exit 1,
|
|
197
199
|
naming the entries) after writing the config — the guard is not weakened by the convenience.
|
|
198
200
|
### `init`
|
|
@@ -211,7 +213,7 @@ It also has three side effects on files you own, and it says so when it does the
|
|
|
211
213
|
```
|
|
212
214
|
✅ Created snapshot.config.ts (preset: node-lib)
|
|
213
215
|
Added npm scripts: snapshot, snapshot:validate
|
|
214
|
-
Added devDependency: codebase-snapshot ^0.9.
|
|
216
|
+
Added devDependency: codebase-snapshot ^0.9.14
|
|
215
217
|
Added to .gitignore: snapshot/, snapshot.tmp/
|
|
216
218
|
```
|
|
217
219
|
|
|
@@ -407,16 +409,16 @@ narrowest cases of the same rule.
|
|
|
407
409
|
| `--include-monolith` | flag | off | generate, validate, report, explain | Also write `full-codebase.json` and `full-codebase.md`. |
|
|
408
410
|
| `--include-sensitive-files` | flag | off | generate, validate, report, explain, init | Emit `.env`, `*.pem`, `.npmrc` and similar. Off by default; they are reported as skipped. |
|
|
409
411
|
| `--skip-artifacts <names>` | list | unset | generate, validate, report, explain, init | Comma-separated artifacts to omit. |
|
|
410
|
-
| `--only-artifacts <names>` | list | unset | generate, validate, report, explain, init | Write only these. Cannot be combined with `--skip-artifacts`. |
|
|
412
|
+
| `--only-artifacts <names>` | list | unset | generate, validate, report, explain, init | Write *only* these, and **turn on** any this names: a config with `artifacts: { audit: false }` still gets `reports/audit.md` from `--only-artifacts audit`, because "only these" is a statement about the whole selection rather than a suppression list. Cannot be combined with `--skip-artifacts`. |
|
|
411
413
|
| `--format <text\|json>` | enum | `text` | generate, validate, report, explain, init, setup, diff, describe, skill | `json` writes a pure JSON payload to stdout. Every command honours it, and `describe` reports the same nine-command list. |
|
|
412
414
|
| `--sarif` | flag | off | generate, validate, setup | Also write `reports/audit.sarif`: the redaction findings as SARIF 2.1.0, for upload to GitHub Code Scanning. The artefact carries the path, the line and the rule — never the value. Works with `--skip-artifacts audit`, because a CI gate does not read markdown. Rule ids are fixed per rule name, so adding a custom rule never re-points an existing alert. |
|
|
413
|
-
| `--skeleton` | flag | off | generate, validate, setup | Emit
|
|
415
|
+
| `--skeleton` | flag | off | generate, validate, setup | Emit signatures instead of bodies: each function or method body becomes `{ /* ... */ }` in place, so formatting and comments around it are untouched. Covers `.ts`, `.tsx`, `.mts`, `.cts`, `.js`, `.jsx`, `.mjs` and `.cjs`; `.d.ts` files are left alone because they are already declarations. The summary still describes the whole file, and affected entries carry `inclusionMode: "signatures"`. `--include-monolith` still writes the whole body, so the monolith's `inclusionMode: "full"` stays true. |
|
|
414
416
|
| `--changed-since <ref>` | git ref | unset | generate, validate, explain | Include only files changed since this ref, plus untracked ones. Every other file is recorded in `manifest.skipped[]` with reason `unchanged` and is never opened. `validate` re-resolves the scope: a skipped file that has changed since is reported as drift, and one that has not is left alone — and with `--with-dependents`, so is a file that now imports one that changed. `explain` answers with the same rule. A ref beginning with `-` is refused. |
|
|
415
417
|
| `--staged` | flag | off | generate, validate, explain | Include only files staged for the next commit. |
|
|
416
418
|
| `--with-dependents` | flag | off | generate, validate, explain | With either of the two above, also include the files that import a changed one — a one-hop closure, not a transitive walk. Needs a scope; alone it is a usage error. |
|
|
417
419
|
| `--install-hook` | flag | off | init, setup | Write `.git/hooks/pre-commit` so a commit is refused while the snapshot is out of date. The hook checks the `outDir` the config names, so it works for a project that does not use `snapshot/`. An existing hook is never replaced; the note says what to add to it instead. |
|
|
418
420
|
| `--dry-run` | flag | off | generate, init, setup | Compute everything, print it, write nothing. |
|
|
419
|
-
| `--watch` | flag | off | generate | Regenerate on every change until interrupted. |
|
|
421
|
+
| `--watch` | flag | off | generate | Regenerate on every change until interrupted. Redaction and parsing are cached per file across runs in the same process, keyed on the content hash, so a one-file edit regenerates from that one file instead of from the whole repository. The cache is memory only and is dropped when the process exits. |
|
|
420
422
|
| `--autopilot` | flag | off | generate | Set the project up without asking — config, `.gitignore`, npm scripts, devDependency, install — then generate. Never overwrites an existing config; refuses an empty directory (exit `1`); contradicts `--no-config` (exit `2`). |
|
|
421
423
|
| `--no-install` | flag | off | setup, generate `--autopilot` | Skip the dependency install. Anywhere else it is a usage error, because it would change nothing. |
|
|
422
424
|
| `--no-config` | flag | off | generate, validate, report, explain, diff | Ignore every `snapshot.config.*` file and run on the built-in defaults. `diff` never loads a config anyway and accepts the flag for consistency. |
|
package/docs/configuration.md
CHANGED
|
@@ -192,7 +192,7 @@ export default {
|
|
|
192
192
|
// changedSince: "main", // only files changed since this git ref
|
|
193
193
|
// staged: true, // only files staged for the next commit
|
|
194
194
|
// withDependents: true, // ...and the files that import them
|
|
195
|
-
// skeleton: true, //
|
|
195
|
+
// skeleton: true, // signatures instead of bodies (--skeleton); TS, JS, TSX and JSX
|
|
196
196
|
largeFileBytes: "200k", // above this a file becomes a head/tail preview
|
|
197
197
|
previewHeadLines: 80,
|
|
198
198
|
previewTailLines: 40,
|
|
@@ -229,8 +229,9 @@ export default {
|
|
|
229
229
|
```
|
|
230
230
|
|
|
231
231
|
Everything except `monolith` is on by default; `monolith` follows `includeMonolith`. Turn
|
|
232
|
-
artifacts off with the config
|
|
233
|
-
|
|
232
|
+
artifacts off with the config or `--skip-artifacts a,b`. `--only-artifacts a,b` is different: it
|
|
233
|
+
*writes only* those and turns each named artifact **on**, so `--only-artifacts audit` still produces
|
|
234
|
+
`reports/audit.md` in a project whose config says `audit: false`. The two flags cannot be combined.
|
|
234
235
|
|
|
235
236
|
Two combinations warn because they produce a snapshot that contradicts itself: `manifest` off
|
|
236
237
|
means `validate` and `report` cannot inspect the snapshot, and `parts` off with `navigation`
|
|
@@ -266,19 +267,52 @@ makes an audit entry mean something.
|
|
|
266
267
|
matched span is masked, never the whole line, and no excerpt of a matched line reaches any
|
|
267
268
|
artifact. Multi-line PEM keys are masked as a block.
|
|
268
269
|
- `customPatterns` add rules; `disableDefaultPatterns` removes the built-in set — it does not
|
|
269
|
-
disable redaction itself
|
|
270
|
+
disable redaction itself, and it applies to **paths** as well as to content, so a
|
|
271
|
+
credential-shaped file name is no longer skipped by a rule you switched off.
|
|
270
272
|
|
|
271
273
|
Redaction is **fail-closed**, so a placeholder is indistinguishable from a secret: a
|
|
272
274
|
`.env.example` whose line reads `JWT_SECRET=replace-with-base64url-48-bytes` is masked like a
|
|
273
275
|
live key would be, and a test fixture literal such as `process.env.X_SECRET = "test-value"` is
|
|
274
|
-
masked too.
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
secret by shape alone. That is the intended trade-off — the alternative is guessing which values
|
|
278
|
-
are throwaway — and it is worth knowing before you hand the snapshot to a model and find the
|
|
279
|
-
template less readable than the original. To narrow it, remove a rule by name with
|
|
276
|
+
masked too. That is the intended trade-off — the alternative is guessing which values are
|
|
277
|
+
throwaway — and it is worth knowing before you hand the snapshot to a model and find the template
|
|
278
|
+
less readable than the original. To narrow it, remove a rule by name with
|
|
280
279
|
`disableDefaultPatterns: true` plus your own `customPatterns`.
|
|
281
280
|
|
|
281
|
+
The one shape that is *not* masked is a value that is a single `${…}` reference and nothing else —
|
|
282
|
+
`password: "${DB_PASSWORD}"`, `` `${process.env.TOKEN}` `` — because that is an expression and
|
|
283
|
+
masking it rewrites a program (or an assertion message) rather than hiding a credential. Anything
|
|
284
|
+
inside the braces that is not a plain reference settles it the other way:
|
|
285
|
+
`password: "${DB_PASSWORD:-hunter2}"` is masked, because the shell and Compose idiom for a default
|
|
286
|
+
puts a **hardcoded credential** in the fallback position. So does a second interpolation, or any
|
|
287
|
+
quote inside the braces.
|
|
288
|
+
|
|
289
|
+
The same default is masked when it is not quoted, which is how it is usually written:
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
# .env, docker-compose.yml, a workflow, a shell script — all of these are masked
|
|
293
|
+
POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-MyProdPassword123!}
|
|
294
|
+
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-MyProdPassword123!}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
The fallback is what gets replaced; the variable name stays, exactly as `POSTGRES_PASSWORD:` does.
|
|
298
|
+
`${PORT:-3000}` is untouched: the name is not a credential name, and a port number is not one
|
|
299
|
+
either.
|
|
300
|
+
|
|
301
|
+
A credential assigned to a name inside a string is also masked — a connection string is a string:
|
|
302
|
+
|
|
303
|
+
```js
|
|
304
|
+
const dsn = "password=MySuperSecret123;host=db.internal";
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
What is left alone is a credential *name* in a literal, with nothing assigned to it:
|
|
308
|
+
`["awsSecret:", "apiKey:"]` is a list of keys, not two credentials, and a documentation line showing
|
|
309
|
+
`password: "…"` inside a code span is showing an example. The difference is whether the literal
|
|
310
|
+
closes between the name and the value.
|
|
311
|
+
|
|
312
|
+
`readonly` and `declare` are shell assignments in a shell file and declarations in a source file, and
|
|
313
|
+
the file decides: `readonly api_key=hunter2` in `.envrc`, `.sh`, `.zsh`, a YAML or an INI file is
|
|
314
|
+
masked, while `private readonly apiKey = someValue` in TypeScript is left alone.
|
|
315
|
+
|
|
282
316
|
Findings are summarised in `reports/audit.md` (one row per line, naming every rule that matched
|
|
283
317
|
it), counted in `manifest.totals.redactedLines`, and shown in the `generate` summary as a
|
|
284
318
|
`Redacted:` line separate from `Warnings:`.
|
package/docs/mcp.md
CHANGED
|
@@ -10,7 +10,7 @@ walk — that the client sees.
|
|
|
10
10
|
## Start it
|
|
11
11
|
|
|
12
12
|
```bash
|
|
13
|
-
npx -y codebase-snapshot@0.9.
|
|
13
|
+
npx -y codebase-snapshot@0.9.14 mcp
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
The server serves the directory it was started in. `--cwd <path>` points it elsewhere, and that
|
|
@@ -25,7 +25,7 @@ Claude Desktop and Cursor:
|
|
|
25
25
|
"mcpServers": {
|
|
26
26
|
"codebase-snapshot": {
|
|
27
27
|
"command": "npx",
|
|
28
|
-
"args": ["-y", "codebase-snapshot@0.9.
|
|
28
|
+
"args": ["-y", "codebase-snapshot@0.9.14", "mcp", "--cwd", "/absolute/path/to/repo"]
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
}
|
|
@@ -34,7 +34,7 @@ Claude Desktop and Cursor:
|
|
|
34
34
|
Claude Code registers the same thing from a shell:
|
|
35
35
|
|
|
36
36
|
```bash
|
|
37
|
-
claude mcp add codebase-snapshot -- npx -y codebase-snapshot@0.9.
|
|
37
|
+
claude mcp add codebase-snapshot -- npx -y codebase-snapshot@0.9.14 mcp --cwd /absolute/path/to/repo
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
On Windows, wrap the command as `cmd /c npx` with the same arguments — `npx` is a `.cmd` shim
|
|
@@ -61,6 +61,19 @@ still reachable: pass the same value to the read side that you passed to `snapsh
|
|
|
61
61
|
that, only `snapshot_generate` honoured it and every other tool looked in the configured directory
|
|
62
62
|
and reported "there is no snapshot manifest here".
|
|
63
63
|
|
|
64
|
+
`snapshot_plan`, `snapshot_generate`, `snapshot_validate` and `snapshot_explain` also take the
|
|
65
|
+
**scope and compression** switches `generate` accepts: `maxPartTokens`, `skeleton`, `sarif`,
|
|
66
|
+
`changedSince`, `staged` and `withDependents`. They have to: `manifest.config.digest` covers those
|
|
67
|
+
settings, so a snapshot generated with `--skeleton` was not merely un-inspectable over MCP, it was
|
|
68
|
+
reported as configuration drift on every `snapshot_validate`. Pass the same switches you generated
|
|
69
|
+
with, exactly as you would on the command line. The four remaining tools do not take them, because
|
|
70
|
+
none of them re-derives the configuration and a flag that changes nothing is the command-line form
|
|
71
|
+
of a silent fallback.
|
|
72
|
+
|
|
73
|
+
`includeSensitiveFiles` is deliberately absent from every one of them. A prompt can ask for
|
|
74
|
+
anything, and a tool that emits `.env` on request is a secret-exfiltration primitive with a
|
|
75
|
+
friendly name.
|
|
76
|
+
|
|
64
77
|
`snapshot_locate` is the one that makes the others usable: a twelve-part snapshot is hundreds of
|
|
65
78
|
kilobytes, and asking "which part holds authentication?" is cheaper than reading all twelve. It
|
|
66
79
|
searches the manifest's index — file paths, exported symbols, component names, routes, layers and
|
package/docs/output.md
CHANGED
|
@@ -31,6 +31,12 @@ fields in `manifest.json` for the same file. `manifest.json` reports what the pa
|
|
|
31
31
|
(CRLF normalised, the trailing newline not counted); the monolith reports the content it
|
|
32
32
|
embedded. Compare a file's fields within one artifact, not across the two.
|
|
33
33
|
|
|
34
|
+
"CRLF normalised" covers all three line endings a file can have. A CR-only file — an old Mac file,
|
|
35
|
+
or anything written by a tool that predates LF on that platform — is written to a part with LF too,
|
|
36
|
+
and its `lines` count is the real one: the preview budget and the line counts used to split on `\n`
|
|
37
|
+
alone, so such a file was one line, `largeFileBytes` never applied, and the whole file went into a
|
|
38
|
+
part with its carriage returns intact.
|
|
39
|
+
|
|
34
40
|
The manifest embedded in `full-codebase.json` is otherwise identical to `manifest.json` with one
|
|
35
41
|
impossible field: a file cannot record its own sha256, so `artifacts.fullJson` is `null` in the
|
|
36
42
|
embedded copy and a `selfReference` sentence points at `manifest.json`, which carries the real
|
|
@@ -202,6 +208,13 @@ has several at once, and "within budget" cannot be answered without knowing whic
|
|
|
202
208
|
`variables`, `reactComponents`, `hooks`, `routeHandlers`), plus `parseErrors` when the parser
|
|
203
209
|
reported syntax errors. JSON gets `{ shape, itemCount, keys, sampleKeys }` and Markdown gets
|
|
204
210
|
`{ headings, frontmatterKeys }` — a shape description rather than a code summary.
|
|
211
|
+
`imports` covers `import` declarations, named and star re-exports, `require("…")`, `import("…")`
|
|
212
|
+
and `import x = require("…")`; a computed specifier is an expression and is not recorded, because
|
|
213
|
+
a wrong edge in the import graph is worse than a missing one. The JSON reader follows JSONC —
|
|
214
|
+
comments, trailing commas and a BOM — and reports `invalid-json` only for a file that still does
|
|
215
|
+
not parse. `headings` skips headings inside fenced code and inside YAML front matter, and a
|
|
216
|
+
document that *opens* with a `---` horizontal rule keeps its headings: the block has to look like
|
|
217
|
+
YAML before it counts as front matter.
|
|
205
218
|
- `summaryOmitted: true` means the file's summary was dropped because the summary alone would
|
|
206
219
|
not fit the part budget. The entry then has `summaryQuality` but no `summary`, and the
|
|
207
220
|
file's body is still emitted in full. It is only ever present when `true`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codebase-snapshot",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.14",
|
|
4
4
|
"description": "Pack a codebase for an LLM into budgeted Markdown parts, with a manifest of every file and skip, and validate to prove the snapshot is current.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"codebase",
|