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/dist/cli.js CHANGED
@@ -1,5 +1,6 @@
1
- import { buildAgentContract, runStdioServer, COMMAND_OPTION_SCOPE, commandsForOption, SKILL_NAME, renderSkill, renderContractText } from './chunk-RQKXZOD7.js';
2
- import { shouldCheck, checkForUpdate, GENERATOR_VERSION, cacheFilePath, formatUpdateNotice, loadConfigFileDetailed, resolveConfig, validateSnapshot, describeManifestShapeProblem, explainPath, compareManifests, loadManifest, renderDiffText, generateSnapshot, formatNumber, isDiscoveryMode, ARTIFACT_NAMES, getPreset, unknownPresetMessage, GENERATOR_NAME, runInit, PromptCancelled, planFirstRun, isEffectivelyEmpty, isInteractive, defaultIO, hasPriorSnapshot, DEFAULT_ANSWERS, findConfigFile, firstRunMessage, parseByteSize, gitignorePath, appendGitignoreEntries, escapeControls, MIN_MAX_PART_BYTES, renderExplainText } from './chunk-C24OAT6T.js';
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 byte count such as 120000 or "120k", received "${raw}".`
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
- discovery: isDiscoveryMode(rawDiscovery) ? rawDiscovery : void 0
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(`${JSON.stringify(result, null, 2)}
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
- async function runWatchLoop(invocation, resolved) {
1147
- const outDir = path2.resolve(resolved.cwd, resolved.outDir);
1148
- const tmpDir = path2.resolve(resolved.cwd, resolved.tmpDir);
1149
- const paths = { cwd: resolved.cwd, outDir, tmpDir };
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
- void regenerate();
1213
- const watcher = fs.watch(resolved.cwd, { recursive: true }, (_event, filename) => {
1214
- const decision = decideWatchEvent(
1215
- state,
1216
- {
1217
- path: filename ? path2.resolve(resolved.cwd, filename.toString()) : null,
1218
- now: performance.now()
1219
- },
1220
- paths
1221
- );
1222
- state = decision.state;
1223
- if (decision.action === "pending") {
1224
- if (timer) {
1225
- clearTimeout(timer);
1226
- timer = null;
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
- return;
1229
- }
1230
- if (decision.action === "schedule") schedule();
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 };