codebase-snapshot 0.9.8 → 0.9.12
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 +536 -0
- package/README.md +3 -3
- package/dist/bin.js +5262 -3459
- package/dist/{chunk-C24OAT6T.js → chunk-3H45YMBG.js} +1867 -509
- package/dist/{chunk-RQKXZOD7.js → chunk-5OR3WVPW.js} +366 -84
- package/dist/cli.cjs +5264 -3481
- package/dist/cli.d.cts +282 -3
- package/dist/cli.d.ts +282 -3
- package/dist/cli.js +181 -39
- package/dist/generate-worker-D5Xgh27c.d.cts +57 -0
- package/dist/generate-worker-D5Xgh27c.d.ts +57 -0
- package/dist/index.cjs +1820 -506
- package/dist/index.d.cts +310 -5
- package/dist/index.d.ts +310 -5
- package/dist/index.js +1 -3
- package/dist/mcp.cjs +5670 -4877
- package/dist/mcp.d.cts +70 -2
- package/dist/mcp.d.ts +70 -2
- package/dist/mcp.js +2 -4
- package/dist/{types-Ca4PmkiQ.d.cts → types-CTewen3J.d.cts} +115 -2
- package/dist/{types-Ca4PmkiQ.d.ts → types-CTewen3J.d.ts} +115 -2
- package/docs/agent.md +1 -1
- package/docs/cli.md +27 -4
- package/docs/configuration.md +5 -0
- package/docs/how-to-feed-a-repo-to-claude.md +61 -0
- package/docs/mcp.md +23 -10
- package/docs/output.md +36 -4
- package/docs/stale-context-is-your-bug.md +13 -0
- package/docs/vs-repomix.md +1 -1
- package/package.json +5 -3
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { buildAgentContract, runStdioServer, COMMAND_OPTION_SCOPE, commandsForOption, SKILL_NAME, renderSkill, renderContractText } from './chunk-
|
|
2
|
-
|
|
1
|
+
import { buildAgentContract, runStdioServer, COMMAND_OPTION_SCOPE, commandsForOption, SKILL_NAME, renderSkill, renderContractText } from './chunk-5OR3WVPW.js';
|
|
2
|
+
export { buildAgentContract } from './chunk-5OR3WVPW.js';
|
|
3
|
+
import { shouldCheck, checkForUpdate, GENERATOR_VERSION, cacheFilePath, formatUpdateNotice, installKind, loadConfigFileDetailed, resolveConfig, validateSnapshot, describeManifestShapeProblem, explainPath, compareManifests, loadManifest, renderDiffText, generateSnapshot, formatNumber, isDiscoveryMode, ARTIFACT_NAMES, getPreset, unknownPresetMessage, GENERATOR_NAME, runInit, installPreCommitHook, PromptCancelled, planFirstRun, isEffectivelyEmpty, isInteractive, defaultIO, hasPriorSnapshot, DEFAULT_ANSWERS, findConfigFile, firstRunMessage, parseByteSize, gitignorePath, appendGitignoreEntries, escapeControls, MIN_MAX_PART_TOKENS, MIN_MAX_PART_BYTES, renderExplainText } from './chunk-3H45YMBG.js';
|
|
3
4
|
import { spawnSync } from 'child_process';
|
|
4
5
|
import * as fs from 'fs';
|
|
5
6
|
import * as path2 from 'path';
|
|
@@ -136,6 +137,7 @@ var CLI_OPTIONS = {
|
|
|
136
137
|
watch: { type: "boolean" },
|
|
137
138
|
"no-config": { type: "boolean" },
|
|
138
139
|
discovery: { type: "string" },
|
|
140
|
+
"max-part-tokens": { type: "string" },
|
|
139
141
|
preset: { type: "string" },
|
|
140
142
|
include: { type: "string" },
|
|
141
143
|
exclude: { type: "string" },
|
|
@@ -146,6 +148,12 @@ var CLI_OPTIONS = {
|
|
|
146
148
|
"no-install": { type: "boolean" },
|
|
147
149
|
target: { type: "string" },
|
|
148
150
|
"no-update-check": { type: "boolean" },
|
|
151
|
+
"install-hook": { type: "boolean" },
|
|
152
|
+
"changed-since": { type: "string" },
|
|
153
|
+
"with-dependents": { type: "boolean" },
|
|
154
|
+
staged: { type: "boolean" },
|
|
155
|
+
skeleton: { type: "boolean" },
|
|
156
|
+
sarif: { type: "boolean" },
|
|
149
157
|
help: { type: "boolean", short: "h" },
|
|
150
158
|
version: { type: "boolean", short: "v" }
|
|
151
159
|
};
|
|
@@ -185,7 +193,13 @@ Options:
|
|
|
185
193
|
--max-monolith-bytes <n> Skip monolith artefacts larger than n bytes
|
|
186
194
|
--max-part-bytes <n> Maximum byte budget per markdown part (default: 307200).
|
|
187
195
|
Accepts units: 120k, 1.5mb
|
|
196
|
+
--max-part-tokens <n> Per-part budget in tokens; narrows --max-part-bytes
|
|
188
197
|
--large-file-bytes <n> Threshold for head/tail truncation (default: 204800)
|
|
198
|
+
--changed-since <ref> Only files changed since this git ref, plus untracked ones
|
|
199
|
+
--staged Only files staged for the next commit
|
|
200
|
+
--with-dependents With the two above: also include their importers
|
|
201
|
+
--skeleton Emit TypeScript signatures instead of bodies
|
|
202
|
+
--sarif Also write reports/audit.sarif for GitHub Code Scanning
|
|
189
203
|
--include-sensitive-files Emit .env / *.pem / .npmrc / credential files
|
|
190
204
|
--skip-artifacts <names> Comma-separated artifacts to omit
|
|
191
205
|
(readme, manifest, parts, navigation,
|
|
@@ -211,9 +225,12 @@ Options:
|
|
|
211
225
|
--exclude <globs> Comma-separated exclude patterns
|
|
212
226
|
--yes Accept every default without prompting
|
|
213
227
|
--force Overwrite an existing config
|
|
228
|
+
--install-hook Write .git/hooks/pre-commit so a stale snapshot cannot be
|
|
229
|
+
committed. An existing hook is never replaced.
|
|
214
230
|
|
|
215
231
|
setup only:
|
|
216
232
|
--no-install Skip installing the dependency before generating
|
|
233
|
+
--install-hook As for init
|
|
217
234
|
--preset / --include / --exclude / --force As for init
|
|
218
235
|
|
|
219
236
|
skill only:
|
|
@@ -241,12 +258,12 @@ function parsePositiveInteger(raw, optionName, minimum) {
|
|
|
241
258
|
}
|
|
242
259
|
return value;
|
|
243
260
|
}
|
|
244
|
-
function parseByteOption(raw, optionName, minimum) {
|
|
261
|
+
function parseByteOption(raw, optionName, minimum, unit = "byte count") {
|
|
245
262
|
if (raw === void 0) return void 0;
|
|
246
263
|
const value = parseByteSize(raw);
|
|
247
264
|
if (value === void 0 || !Number.isSafeInteger(value)) {
|
|
248
265
|
throw new UsageError(
|
|
249
|
-
`--${optionName} expects a
|
|
266
|
+
`--${optionName} expects a ${unit} such as 120000 or "120k", received "${raw}".`
|
|
250
267
|
);
|
|
251
268
|
}
|
|
252
269
|
if (value < minimum) {
|
|
@@ -269,7 +286,27 @@ function parseArtifactList(raw, flag) {
|
|
|
269
286
|
}
|
|
270
287
|
return names;
|
|
271
288
|
}
|
|
289
|
+
var SHORT_FLAGS = new Set(
|
|
290
|
+
Object.values(CLI_OPTIONS).map((spec) => spec.short).filter((short) => typeof short === "string").map((short) => `-${short}`)
|
|
291
|
+
);
|
|
292
|
+
function misdashedLongOption(argv) {
|
|
293
|
+
for (const token of argv) {
|
|
294
|
+
if (token === "--") break;
|
|
295
|
+
if (!/^-[A-Za-z][A-Za-z0-9-]+$/.test(token)) continue;
|
|
296
|
+
if ([...token.slice(1)].every((character) => SHORT_FLAGS.has(`-${character}`))) continue;
|
|
297
|
+
return token;
|
|
298
|
+
}
|
|
299
|
+
return null;
|
|
300
|
+
}
|
|
272
301
|
function parseCliArgs(argv) {
|
|
302
|
+
const misdashed = misdashedLongOption(argv);
|
|
303
|
+
if (misdashed !== null) {
|
|
304
|
+
const long = `--${misdashed.slice(1)}`;
|
|
305
|
+
const known = Object.prototype.hasOwnProperty.call(CLI_OPTIONS, misdashed.slice(1));
|
|
306
|
+
throw new UsageError(
|
|
307
|
+
known ? `Unknown option '${misdashed}'. Options start with two dashes \u2014 did you mean '${long}'?` : `Unknown option '${misdashed}'. Options start with two dashes; if '${misdashed}' is a path or an argument, put it after '--'.`
|
|
308
|
+
);
|
|
309
|
+
}
|
|
273
310
|
let parsed;
|
|
274
311
|
try {
|
|
275
312
|
parsed = parseArgs({
|
|
@@ -382,8 +419,10 @@ function parseCliArgs(argv) {
|
|
|
382
419
|
throw new UsageError("--skip-artifacts and --only-artifacts cannot be combined.");
|
|
383
420
|
}
|
|
384
421
|
let skipArtifacts;
|
|
422
|
+
let onlyArtifacts;
|
|
385
423
|
if (rawOnly !== void 0) {
|
|
386
424
|
const keep = parseArtifactList(String(rawOnly), "--only-artifacts");
|
|
425
|
+
onlyArtifacts = keep;
|
|
387
426
|
onlyNamesMonolith = keep.includes("monolith");
|
|
388
427
|
skipArtifacts = ARTIFACT_NAMES.filter(
|
|
389
428
|
(name) => !keep.includes(name)
|
|
@@ -412,7 +451,7 @@ function parseCliArgs(argv) {
|
|
|
412
451
|
`${ignored.join(", ")} ${ignored.length === 1 ? "does" : "do"} not apply to \`${command}\` and ${ignored.length === 1 ? "was" : "were"} not applied. ${ignored.length === 1 ? "It is" : "They are"} accepted by: ${owners.join(", ")}.${hint}`
|
|
413
452
|
);
|
|
414
453
|
}
|
|
415
|
-
if (command === "init" && values.preset !== void 0) {
|
|
454
|
+
if ((command === "init" || command === "setup") && values.preset !== void 0) {
|
|
416
455
|
const requested = String(values.preset);
|
|
417
456
|
if (!getPreset(requested)) throw new UsageError(unknownPresetMessage(requested));
|
|
418
457
|
}
|
|
@@ -456,6 +495,15 @@ function parseCliArgs(argv) {
|
|
|
456
495
|
"max-part-bytes",
|
|
457
496
|
MIN_MAX_PART_BYTES
|
|
458
497
|
),
|
|
498
|
+
// Tokens are counted, so `32k` is a shorthand a context window is often named with; the
|
|
499
|
+
// same parser accepts a bare integer. The floor is the byte floor rewritten in tokens —
|
|
500
|
+
// clamping a smaller budget up to it accepted the flag and then applied a different number.
|
|
501
|
+
maxPartTokens: parseByteOption(
|
|
502
|
+
values["max-part-tokens"],
|
|
503
|
+
"max-part-tokens",
|
|
504
|
+
MIN_MAX_PART_TOKENS,
|
|
505
|
+
"token count"
|
|
506
|
+
),
|
|
459
507
|
largeFileBytes: parseByteOption(
|
|
460
508
|
values["large-file-bytes"],
|
|
461
509
|
"large-file-bytes",
|
|
@@ -467,7 +515,22 @@ function parseCliArgs(argv) {
|
|
|
467
515
|
1
|
|
468
516
|
),
|
|
469
517
|
skipArtifacts,
|
|
470
|
-
|
|
518
|
+
onlyArtifacts,
|
|
519
|
+
discovery: isDiscoveryMode(rawDiscovery) ? rawDiscovery : void 0,
|
|
520
|
+
noUpdateCheck: values["no-update-check"] === true,
|
|
521
|
+
installHook: values["install-hook"] === true,
|
|
522
|
+
changedSince: values["changed-since"] === void 0 ? void 0 : String(values["changed-since"]),
|
|
523
|
+
// `undefined` when the flag was not given, never `false`.
|
|
524
|
+
//
|
|
525
|
+
// `resolveConfig` reads these as `cliOptions.x ?? config.x`, so a `false` here is an
|
|
526
|
+
// *explicit override* — it beat `skeleton: true` in a `snapshot.config.ts` and turned the
|
|
527
|
+
// setting off without a word. Every run that did not pass the flag sent `false`, so a config
|
|
528
|
+
// file could not turn any of these four on at all, and a snapshot generated with `--skeleton`
|
|
529
|
+
// could never be validated against the config that describes it.
|
|
530
|
+
staged: values.staged === true ? true : void 0,
|
|
531
|
+
withDependents: values["with-dependents"] === true ? true : void 0,
|
|
532
|
+
skeleton: values.skeleton === true ? true : void 0,
|
|
533
|
+
sarif: values.sarif === true ? true : void 0
|
|
471
534
|
};
|
|
472
535
|
}
|
|
473
536
|
var HelpRequested = class extends Error {
|
|
@@ -537,18 +600,24 @@ async function runInitCommand(invocation) {
|
|
|
537
600
|
include: splitList(invocation.include),
|
|
538
601
|
exclude: splitList(invocation.exclude),
|
|
539
602
|
skipArtifacts: invocation.skipArtifacts,
|
|
603
|
+
onlyArtifacts: invocation.onlyArtifacts,
|
|
604
|
+
includeMonolith: invocation.includeMonolith,
|
|
540
605
|
includeSensitiveFiles: invocation.includeSensitiveFiles,
|
|
541
606
|
yes: invocation.yes,
|
|
542
607
|
force: invocation.force,
|
|
543
608
|
dryRun: invocation.dryRun
|
|
544
609
|
});
|
|
610
|
+
const hook = invocation.installHook === true ? installPreCommitHook(invocation.cwd, result.outDir) : null;
|
|
545
611
|
if (invocation.format === "json") {
|
|
546
|
-
process.stdout.write(
|
|
547
|
-
|
|
612
|
+
process.stdout.write(
|
|
613
|
+
`${JSON.stringify(hook === null ? result : { ...result, hook }, null, 2)}
|
|
614
|
+
`
|
|
615
|
+
);
|
|
548
616
|
return 0;
|
|
549
617
|
}
|
|
550
618
|
if (!result.written) {
|
|
551
619
|
info(result.configText);
|
|
620
|
+
if (hook) for (const note of hook.notes) info(` ${note}`);
|
|
552
621
|
return 0;
|
|
553
622
|
}
|
|
554
623
|
info(`\u2705 Created ${path2.basename(result.configPath)} (${presetSummary(result)})`);
|
|
@@ -560,6 +629,7 @@ async function runInitCommand(invocation) {
|
|
|
560
629
|
info(` Added to .gitignore: ${result.gitignoreEntries.join(", ")}`);
|
|
561
630
|
}
|
|
562
631
|
for (const note of result.notes) info(` \u26A0 ${note}`);
|
|
632
|
+
if (hook) for (const note of hook.notes) info(` ${note}`);
|
|
563
633
|
info("");
|
|
564
634
|
info(` Next: ${nextStepHint(invocation.cwd, result.scripts.length > 0)}`);
|
|
565
635
|
return 0;
|
|
@@ -629,15 +699,28 @@ async function runSetupCommand(invocation, options = {}) {
|
|
|
629
699
|
preset: invocation.preset,
|
|
630
700
|
outDir: invocation.outDir,
|
|
631
701
|
maxPartBytes: invocation.maxPartBytesRaw ?? invocation.maxPartBytes,
|
|
702
|
+
// Written into the config, not just forwarded to the generation below. A setting that
|
|
703
|
+
// reaches the snapshot but not the file beside it is drift the very next run reports:
|
|
704
|
+
// `setup --discovery walk --large-file-bytes 100000` used to exit 0 and then fail its own
|
|
705
|
+
// `validate` with three configuration mismatches.
|
|
706
|
+
maxPartTokens: invocation.maxPartTokens,
|
|
707
|
+
largeFileBytes: invocation.largeFileBytes,
|
|
708
|
+
maxMonolithBytes: invocation.maxMonolithBytes ?? void 0,
|
|
709
|
+
discovery: invocation.discovery,
|
|
710
|
+
skeleton: invocation.skeleton === true ? true : void 0,
|
|
711
|
+
sarif: invocation.sarif === true ? true : void 0,
|
|
632
712
|
include: splitList(invocation.include),
|
|
633
713
|
exclude: splitList(invocation.exclude),
|
|
634
714
|
skipArtifacts: invocation.skipArtifacts,
|
|
715
|
+
onlyArtifacts: invocation.onlyArtifacts,
|
|
716
|
+
includeMonolith: invocation.includeMonolith,
|
|
635
717
|
includeSensitiveFiles: invocation.includeSensitiveFiles,
|
|
636
718
|
yes: !interactive,
|
|
637
719
|
force: invocation.force,
|
|
638
720
|
reuseConfig: true,
|
|
639
721
|
dryRun: invocation.dryRun
|
|
640
722
|
});
|
|
723
|
+
const hook = invocation.installHook === true ? installPreCommitHook(invocation.cwd, init.outDir) : null;
|
|
641
724
|
if (invocation.dryRun) {
|
|
642
725
|
const manager = invocation.noInstall ? null : detectPackageManager(invocation.cwd).name;
|
|
643
726
|
if (invocation.format === "json") {
|
|
@@ -683,6 +766,7 @@ async function runSetupCommand(invocation, options = {}) {
|
|
|
683
766
|
if (init.gitignoreEntries.length > 0) {
|
|
684
767
|
info(` Added to .gitignore: ${init.gitignoreEntries.join(", ")}`);
|
|
685
768
|
}
|
|
769
|
+
if (hook) for (const note of hook.notes) info(` ${note}`);
|
|
686
770
|
}
|
|
687
771
|
let install = null;
|
|
688
772
|
if (invocation.noInstall) {
|
|
@@ -703,6 +787,20 @@ async function runSetupCommand(invocation, options = {}) {
|
|
|
703
787
|
if ((invocation.skipArtifacts ?? []).length > 0) {
|
|
704
788
|
generateArgs.push("--skip-artifacts", (invocation.skipArtifacts ?? []).join(","));
|
|
705
789
|
}
|
|
790
|
+
if (invocation.includeMonolith === true) generateArgs.push("--include-monolith");
|
|
791
|
+
if (invocation.discovery !== void 0) generateArgs.push("--discovery", invocation.discovery);
|
|
792
|
+
if (invocation.largeFileBytes !== void 0) {
|
|
793
|
+
generateArgs.push("--large-file-bytes", String(invocation.largeFileBytes));
|
|
794
|
+
}
|
|
795
|
+
if (invocation.maxMonolithBytes !== void 0 && invocation.maxMonolithBytes !== null) {
|
|
796
|
+
generateArgs.push("--max-monolith-bytes", String(invocation.maxMonolithBytes));
|
|
797
|
+
}
|
|
798
|
+
if (invocation.maxPartTokens !== void 0) {
|
|
799
|
+
generateArgs.push("--max-part-tokens", String(invocation.maxPartTokens));
|
|
800
|
+
}
|
|
801
|
+
if (invocation.skeleton === true) generateArgs.push("--skeleton");
|
|
802
|
+
if (invocation.sarif === true) generateArgs.push("--sarif");
|
|
803
|
+
if (invocation.noUpdateCheck) generateArgs.push("--no-update-check");
|
|
706
804
|
if (invocation.watch) generateArgs.push("--watch");
|
|
707
805
|
if (invocation.format === "json") {
|
|
708
806
|
const generated = await captureStdout(() => main(generateArgs));
|
|
@@ -723,7 +821,8 @@ async function runSetupCommand(invocation, options = {}) {
|
|
|
723
821
|
dependency: init.dependency,
|
|
724
822
|
gitignoreEntries: init.gitignoreEntries,
|
|
725
823
|
install,
|
|
726
|
-
notes: init.notes
|
|
824
|
+
notes: init.notes,
|
|
825
|
+
hook
|
|
727
826
|
},
|
|
728
827
|
manifest
|
|
729
828
|
},
|
|
@@ -851,7 +950,7 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
851
950
|
if (pending) {
|
|
852
951
|
const notice = await pending;
|
|
853
952
|
if (notice) process.stderr.write(`
|
|
854
|
-
${formatUpdateNotice(notice)}
|
|
953
|
+
${formatUpdateNotice(notice, installKind())}
|
|
855
954
|
`);
|
|
856
955
|
}
|
|
857
956
|
return code;
|
|
@@ -918,10 +1017,16 @@ ${USAGE}
|
|
|
918
1017
|
includeMonolith: invocation.includeMonolith,
|
|
919
1018
|
includeSensitiveFiles: invocation.includeSensitiveFiles,
|
|
920
1019
|
maxPartBytes: invocation.maxPartBytes,
|
|
1020
|
+
maxPartTokens: invocation.maxPartTokens,
|
|
921
1021
|
largeFileBytes: invocation.largeFileBytes,
|
|
922
1022
|
maxMonolithBytes: invocation.maxMonolithBytes,
|
|
923
1023
|
skipArtifacts: invocation.skipArtifacts,
|
|
924
|
-
discovery: invocation.discovery
|
|
1024
|
+
discovery: invocation.discovery,
|
|
1025
|
+
changedSince: invocation.changedSince,
|
|
1026
|
+
staged: invocation.staged,
|
|
1027
|
+
withDependents: invocation.withDependents,
|
|
1028
|
+
skeleton: invocation.skeleton,
|
|
1029
|
+
sarif: invocation.sarif
|
|
925
1030
|
},
|
|
926
1031
|
invocation.cwd
|
|
927
1032
|
);
|
|
@@ -997,6 +1102,10 @@ ${USAGE}
|
|
|
997
1102
|
return fail(invocation.format, err.message);
|
|
998
1103
|
}
|
|
999
1104
|
}
|
|
1105
|
+
if (invocation.watch) {
|
|
1106
|
+
const problem = watchSupportProblem();
|
|
1107
|
+
if (problem !== null) return fail(invocation.format, problem);
|
|
1108
|
+
}
|
|
1000
1109
|
const started = performance.now();
|
|
1001
1110
|
info(`\u{1F4F8} ${invocation.dryRun ? "Planning snapshot (dry run)" : "Generating snapshot"} for: ${resolved.cwd}`);
|
|
1002
1111
|
info(
|
|
@@ -1037,7 +1146,11 @@ ${USAGE}
|
|
|
1037
1146
|
const reports = [
|
|
1038
1147
|
artifacts.projectTree && "project-tree",
|
|
1039
1148
|
artifacts.navigation && "navigation",
|
|
1040
|
-
artifacts.audit && "audit"
|
|
1149
|
+
artifacts.audit && "audit",
|
|
1150
|
+
// `--sarif` writes beside the audit rather than inside `artifacts`, so it is listed
|
|
1151
|
+
// here by hand. Without it a run that produced `reports/audit.sarif` still reported
|
|
1152
|
+
// that every artifact was disabled and only the ownership marker had been written.
|
|
1153
|
+
resolved.sarif === true && "audit.sarif"
|
|
1041
1154
|
].filter(Boolean);
|
|
1042
1155
|
if (reports.length > 0) {
|
|
1043
1156
|
info(
|
|
@@ -1084,7 +1197,7 @@ ${USAGE}
|
|
|
1084
1197
|
`);
|
|
1085
1198
|
}
|
|
1086
1199
|
if (invocation.watch) {
|
|
1087
|
-
return await runWatchLoop(invocation, resolved);
|
|
1200
|
+
return await runWatchLoop(invocation, resolved, { generatedAlready: true });
|
|
1088
1201
|
}
|
|
1089
1202
|
return manifest.validation.status === "passed" ? 0 : 1;
|
|
1090
1203
|
} catch (err) {
|
|
@@ -1143,10 +1256,22 @@ function decideWatchEvent(state, event, paths) {
|
|
|
1143
1256
|
}
|
|
1144
1257
|
return { state, action: "schedule" };
|
|
1145
1258
|
}
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1259
|
+
function watchPathsFor(config) {
|
|
1260
|
+
return {
|
|
1261
|
+
cwd: config.cwd,
|
|
1262
|
+
outDir: path2.resolve(config.cwd, config.outDir),
|
|
1263
|
+
tmpDir: path2.resolve(config.cwd, config.tmpDir)
|
|
1264
|
+
};
|
|
1265
|
+
}
|
|
1266
|
+
function watchSupportProblem(platform = process.platform, version = process.versions.node) {
|
|
1267
|
+
if (platform !== "linux") return null;
|
|
1268
|
+
const [major, minor] = version.split(".").map((part) => Number.parseInt(part, 10));
|
|
1269
|
+
if (!Number.isFinite(major)) return null;
|
|
1270
|
+
if (major > 20 || major === 20 && (minor ?? 0) >= 13) return null;
|
|
1271
|
+
return `--watch needs recursive directory watching, which Node only provides on Linux from 20.13 (this is ${version}). Re-run \`generate\` after each change, or upgrade to Node 20.13 or later.`;
|
|
1272
|
+
}
|
|
1273
|
+
async function runWatchLoop(invocation, resolved, options) {
|
|
1274
|
+
let paths = watchPathsFor(resolved);
|
|
1150
1275
|
let state = initialWatchState();
|
|
1151
1276
|
let timer = null;
|
|
1152
1277
|
let stopped = false;
|
|
@@ -1188,14 +1313,21 @@ async function runWatchLoop(invocation, resolved) {
|
|
|
1188
1313
|
includeMonolith: invocation.includeMonolith,
|
|
1189
1314
|
includeSensitiveFiles: invocation.includeSensitiveFiles,
|
|
1190
1315
|
maxPartBytes: invocation.maxPartBytes,
|
|
1316
|
+
maxPartTokens: invocation.maxPartTokens,
|
|
1191
1317
|
largeFileBytes: invocation.largeFileBytes,
|
|
1192
1318
|
maxMonolithBytes: invocation.maxMonolithBytes,
|
|
1193
1319
|
skipArtifacts: invocation.skipArtifacts,
|
|
1194
|
-
discovery: invocation.discovery
|
|
1320
|
+
discovery: invocation.discovery,
|
|
1321
|
+
changedSince: invocation.changedSince,
|
|
1322
|
+
staged: invocation.staged,
|
|
1323
|
+
withDependents: invocation.withDependents,
|
|
1324
|
+
skeleton: invocation.skeleton,
|
|
1325
|
+
sarif: invocation.sarif
|
|
1195
1326
|
},
|
|
1196
1327
|
invocation.cwd
|
|
1197
1328
|
);
|
|
1198
1329
|
const manifest = generateSnapshot(next, { dryRun: invocation.dryRun });
|
|
1330
|
+
paths = watchPathsFor(next);
|
|
1199
1331
|
const elapsed = ((performance.now() - started) / 1e3).toFixed(2);
|
|
1200
1332
|
info(
|
|
1201
1333
|
`\u267B\uFE0F ${(/* @__PURE__ */ new Date()).toISOString()} ${manifest.counts.totalFiles} file(s), ${manifest.counts.partsCount} part(s), ${manifest.validation.status} (${elapsed}s)`
|
|
@@ -1209,25 +1341,37 @@ async function runWatchLoop(invocation, resolved) {
|
|
|
1209
1341
|
}
|
|
1210
1342
|
};
|
|
1211
1343
|
info("\u{1F440} Watching for changes. Press Ctrl+C to stop.");
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
if (
|
|
1225
|
-
|
|
1226
|
-
|
|
1344
|
+
let watcher;
|
|
1345
|
+
try {
|
|
1346
|
+
watcher = fs.watch(resolved.cwd, { recursive: true }, (_event, filename) => {
|
|
1347
|
+
const decision = decideWatchEvent(
|
|
1348
|
+
state,
|
|
1349
|
+
{
|
|
1350
|
+
path: filename ? path2.resolve(resolved.cwd, filename.toString()) : null,
|
|
1351
|
+
now: performance.now()
|
|
1352
|
+
},
|
|
1353
|
+
paths
|
|
1354
|
+
);
|
|
1355
|
+
state = decision.state;
|
|
1356
|
+
if (decision.action === "pending") {
|
|
1357
|
+
if (timer) {
|
|
1358
|
+
clearTimeout(timer);
|
|
1359
|
+
timer = null;
|
|
1360
|
+
}
|
|
1361
|
+
return;
|
|
1227
1362
|
}
|
|
1228
|
-
|
|
1229
|
-
}
|
|
1230
|
-
|
|
1363
|
+
if (decision.action === "schedule") schedule();
|
|
1364
|
+
});
|
|
1365
|
+
} catch (err) {
|
|
1366
|
+
const code = err.code;
|
|
1367
|
+
const detail = code === "ENOSPC" ? "the operating system's file-watch limit is exhausted; raise it (`fs.inotify.max_user_watches` on Linux) or run `generate` by hand" : code === "ERR_FEATURE_UNAVAILABLE_ON_PLATFORM" ? watchSupportProblem() ?? "this platform has no recursive directory watching" : err.message;
|
|
1368
|
+
warn(`\u274C Could not start watching ${resolved.cwd}: ${detail}`);
|
|
1369
|
+
warn(" The snapshot that was just written is complete and valid.");
|
|
1370
|
+
return 1;
|
|
1371
|
+
}
|
|
1372
|
+
watcher.on("error", (err) => {
|
|
1373
|
+
info(`\u26A0 Watch error: ${err.message}`);
|
|
1374
|
+
info(" The watcher may have missed changes. Restart with Ctrl+C if they stop arriving.");
|
|
1231
1375
|
});
|
|
1232
1376
|
await new Promise((resolve2) => {
|
|
1233
1377
|
const stop = () => {
|
|
@@ -1257,6 +1401,4 @@ if (invokedDirectly) {
|
|
|
1257
1401
|
);
|
|
1258
1402
|
}
|
|
1259
1403
|
|
|
1260
|
-
export { UsageError, WATCH_DEBOUNCE_MS, WATCH_SETTLE_MS, beginWatchRun, cleanInstallEnv, decideWatchEvent, finishWatchRun, initialWatchState, isGeneratedWatchPath, main, parseByteOption, parseCliArgs, parsePositiveInteger };
|
|
1261
|
-
//# sourceMappingURL=cli.js.map
|
|
1262
|
-
//# sourceMappingURL=cli.js.map
|
|
1404
|
+
export { CLI_OPTIONS, UsageError, WATCH_DEBOUNCE_MS, WATCH_SETTLE_MS, beginWatchRun, cleanInstallEnv, decideWatchEvent, finishWatchRun, initialWatchState, isGeneratedWatchPath, main, parseByteOption, parseCliArgs, parsePositiveInteger, watchPathsFor, watchSupportProblem };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/** What the caller needs from a finished run; the whole manifest can be megabytes. */
|
|
2
|
+
interface GenerateSummary {
|
|
3
|
+
fingerprint: string;
|
|
4
|
+
totalFiles: number;
|
|
5
|
+
skippedCount: number;
|
|
6
|
+
partsCount: number;
|
|
7
|
+
parts: Array<{
|
|
8
|
+
filename: string;
|
|
9
|
+
byteSize: number;
|
|
10
|
+
}>;
|
|
11
|
+
totalContentBytes: number;
|
|
12
|
+
warnings: string[];
|
|
13
|
+
}
|
|
14
|
+
type WorkerResults = {
|
|
15
|
+
ok: true;
|
|
16
|
+
summary: GenerateSummary;
|
|
17
|
+
} | {
|
|
18
|
+
ok: false;
|
|
19
|
+
message: string;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* The absolute path of the package entry this process is running from.
|
|
23
|
+
*
|
|
24
|
+
* `__dirname` exists in the CJS build and `import.meta.url` in the ESM one, and `typeof` on an
|
|
25
|
+
* undeclared identifier does not throw — so this resolves correctly in both without a build-time
|
|
26
|
+
* substitution.
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* Where the worker finds this package's entry: the path, and the URL the worker imports.
|
|
30
|
+
*
|
|
31
|
+
* Exported so the URL can be tested directly. The defect it fixes only shows up for an install
|
|
32
|
+
* path that contains a `#`, `?` or `%`, or on a Windows drive letter, and no test can move the
|
|
33
|
+
* package's own `dist/` to such a path.
|
|
34
|
+
*
|
|
35
|
+
* `pathToFileURL` rather than `` `file://${path}` ``: the interpolation form turns a Windows path
|
|
36
|
+
* into `file://C:/…`, whose **host** is the drive letter, reads a `#` as a fragment and a `?` as
|
|
37
|
+
* a query, and leaves a space unescaped. `config.ts` already carries this note for the same
|
|
38
|
+
* reason.
|
|
39
|
+
*/
|
|
40
|
+
declare function workerEntry(directory: string, isCjs: boolean): {
|
|
41
|
+
path: string;
|
|
42
|
+
url: string;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Generates in a worker and resolves with the summary.
|
|
46
|
+
*
|
|
47
|
+
* A worker that fails to start, exits without posting, or throws at the module level is reported
|
|
48
|
+
* as a tool failure naming the reason — never silently retried on the calling thread, because a
|
|
49
|
+
* quiet fallback would hide exactly the condition this exists to avoid.
|
|
50
|
+
*/
|
|
51
|
+
declare function generateInWorker(cwd: string, cliOptions: {
|
|
52
|
+
cwd: string;
|
|
53
|
+
maxPartBytes?: number;
|
|
54
|
+
outDir?: string;
|
|
55
|
+
}): Promise<WorkerResults>;
|
|
56
|
+
|
|
57
|
+
export { type GenerateSummary as G, type WorkerResults as W, generateInWorker as g, workerEntry as w };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/** What the caller needs from a finished run; the whole manifest can be megabytes. */
|
|
2
|
+
interface GenerateSummary {
|
|
3
|
+
fingerprint: string;
|
|
4
|
+
totalFiles: number;
|
|
5
|
+
skippedCount: number;
|
|
6
|
+
partsCount: number;
|
|
7
|
+
parts: Array<{
|
|
8
|
+
filename: string;
|
|
9
|
+
byteSize: number;
|
|
10
|
+
}>;
|
|
11
|
+
totalContentBytes: number;
|
|
12
|
+
warnings: string[];
|
|
13
|
+
}
|
|
14
|
+
type WorkerResults = {
|
|
15
|
+
ok: true;
|
|
16
|
+
summary: GenerateSummary;
|
|
17
|
+
} | {
|
|
18
|
+
ok: false;
|
|
19
|
+
message: string;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* The absolute path of the package entry this process is running from.
|
|
23
|
+
*
|
|
24
|
+
* `__dirname` exists in the CJS build and `import.meta.url` in the ESM one, and `typeof` on an
|
|
25
|
+
* undeclared identifier does not throw — so this resolves correctly in both without a build-time
|
|
26
|
+
* substitution.
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* Where the worker finds this package's entry: the path, and the URL the worker imports.
|
|
30
|
+
*
|
|
31
|
+
* Exported so the URL can be tested directly. The defect it fixes only shows up for an install
|
|
32
|
+
* path that contains a `#`, `?` or `%`, or on a Windows drive letter, and no test can move the
|
|
33
|
+
* package's own `dist/` to such a path.
|
|
34
|
+
*
|
|
35
|
+
* `pathToFileURL` rather than `` `file://${path}` ``: the interpolation form turns a Windows path
|
|
36
|
+
* into `file://C:/…`, whose **host** is the drive letter, reads a `#` as a fragment and a `?` as
|
|
37
|
+
* a query, and leaves a space unescaped. `config.ts` already carries this note for the same
|
|
38
|
+
* reason.
|
|
39
|
+
*/
|
|
40
|
+
declare function workerEntry(directory: string, isCjs: boolean): {
|
|
41
|
+
path: string;
|
|
42
|
+
url: string;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Generates in a worker and resolves with the summary.
|
|
46
|
+
*
|
|
47
|
+
* A worker that fails to start, exits without posting, or throws at the module level is reported
|
|
48
|
+
* as a tool failure naming the reason — never silently retried on the calling thread, because a
|
|
49
|
+
* quiet fallback would hide exactly the condition this exists to avoid.
|
|
50
|
+
*/
|
|
51
|
+
declare function generateInWorker(cwd: string, cliOptions: {
|
|
52
|
+
cwd: string;
|
|
53
|
+
maxPartBytes?: number;
|
|
54
|
+
outDir?: string;
|
|
55
|
+
}): Promise<WorkerResults>;
|
|
56
|
+
|
|
57
|
+
export { type GenerateSummary as G, type WorkerResults as W, generateInWorker as g, workerEntry as w };
|