codebase-snapshot 0.9.9 → 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-HQBEQ67K.js';
2
- 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, PromptCancelled, planFirstRun, isEffectivelyEmpty, isInteractive, defaultIO, hasPriorSnapshot, DEFAULT_ANSWERS, findConfigFile, firstRunMessage, parseByteSize, gitignorePath, appendGitignoreEntries, escapeControls, MIN_MAX_PART_BYTES, renderExplainText } from './chunk-KUVFTJHK.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) {
@@ -402,8 +419,10 @@ function parseCliArgs(argv) {
402
419
  throw new UsageError("--skip-artifacts and --only-artifacts cannot be combined.");
403
420
  }
404
421
  let skipArtifacts;
422
+ let onlyArtifacts;
405
423
  if (rawOnly !== void 0) {
406
424
  const keep = parseArtifactList(String(rawOnly), "--only-artifacts");
425
+ onlyArtifacts = keep;
407
426
  onlyNamesMonolith = keep.includes("monolith");
408
427
  skipArtifacts = ARTIFACT_NAMES.filter(
409
428
  (name) => !keep.includes(name)
@@ -432,7 +451,7 @@ function parseCliArgs(argv) {
432
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}`
433
452
  );
434
453
  }
435
- if (command === "init" && values.preset !== void 0) {
454
+ if ((command === "init" || command === "setup") && values.preset !== void 0) {
436
455
  const requested = String(values.preset);
437
456
  if (!getPreset(requested)) throw new UsageError(unknownPresetMessage(requested));
438
457
  }
@@ -476,6 +495,15 @@ function parseCliArgs(argv) {
476
495
  "max-part-bytes",
477
496
  MIN_MAX_PART_BYTES
478
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
+ ),
479
507
  largeFileBytes: parseByteOption(
480
508
  values["large-file-bytes"],
481
509
  "large-file-bytes",
@@ -487,7 +515,22 @@ function parseCliArgs(argv) {
487
515
  1
488
516
  ),
489
517
  skipArtifacts,
490
- 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
491
534
  };
492
535
  }
493
536
  var HelpRequested = class extends Error {
@@ -557,18 +600,24 @@ async function runInitCommand(invocation) {
557
600
  include: splitList(invocation.include),
558
601
  exclude: splitList(invocation.exclude),
559
602
  skipArtifacts: invocation.skipArtifacts,
603
+ onlyArtifacts: invocation.onlyArtifacts,
604
+ includeMonolith: invocation.includeMonolith,
560
605
  includeSensitiveFiles: invocation.includeSensitiveFiles,
561
606
  yes: invocation.yes,
562
607
  force: invocation.force,
563
608
  dryRun: invocation.dryRun
564
609
  });
610
+ const hook = invocation.installHook === true ? installPreCommitHook(invocation.cwd, result.outDir) : null;
565
611
  if (invocation.format === "json") {
566
- process.stdout.write(`${JSON.stringify(result, null, 2)}
567
- `);
612
+ process.stdout.write(
613
+ `${JSON.stringify(hook === null ? result : { ...result, hook }, null, 2)}
614
+ `
615
+ );
568
616
  return 0;
569
617
  }
570
618
  if (!result.written) {
571
619
  info(result.configText);
620
+ if (hook) for (const note of hook.notes) info(` ${note}`);
572
621
  return 0;
573
622
  }
574
623
  info(`\u2705 Created ${path2.basename(result.configPath)} (${presetSummary(result)})`);
@@ -580,6 +629,7 @@ async function runInitCommand(invocation) {
580
629
  info(` Added to .gitignore: ${result.gitignoreEntries.join(", ")}`);
581
630
  }
582
631
  for (const note of result.notes) info(` \u26A0 ${note}`);
632
+ if (hook) for (const note of hook.notes) info(` ${note}`);
583
633
  info("");
584
634
  info(` Next: ${nextStepHint(invocation.cwd, result.scripts.length > 0)}`);
585
635
  return 0;
@@ -649,15 +699,28 @@ async function runSetupCommand(invocation, options = {}) {
649
699
  preset: invocation.preset,
650
700
  outDir: invocation.outDir,
651
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,
652
712
  include: splitList(invocation.include),
653
713
  exclude: splitList(invocation.exclude),
654
714
  skipArtifacts: invocation.skipArtifacts,
715
+ onlyArtifacts: invocation.onlyArtifacts,
716
+ includeMonolith: invocation.includeMonolith,
655
717
  includeSensitiveFiles: invocation.includeSensitiveFiles,
656
718
  yes: !interactive,
657
719
  force: invocation.force,
658
720
  reuseConfig: true,
659
721
  dryRun: invocation.dryRun
660
722
  });
723
+ const hook = invocation.installHook === true ? installPreCommitHook(invocation.cwd, init.outDir) : null;
661
724
  if (invocation.dryRun) {
662
725
  const manager = invocation.noInstall ? null : detectPackageManager(invocation.cwd).name;
663
726
  if (invocation.format === "json") {
@@ -703,6 +766,7 @@ async function runSetupCommand(invocation, options = {}) {
703
766
  if (init.gitignoreEntries.length > 0) {
704
767
  info(` Added to .gitignore: ${init.gitignoreEntries.join(", ")}`);
705
768
  }
769
+ if (hook) for (const note of hook.notes) info(` ${note}`);
706
770
  }
707
771
  let install = null;
708
772
  if (invocation.noInstall) {
@@ -723,6 +787,20 @@ async function runSetupCommand(invocation, options = {}) {
723
787
  if ((invocation.skipArtifacts ?? []).length > 0) {
724
788
  generateArgs.push("--skip-artifacts", (invocation.skipArtifacts ?? []).join(","));
725
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");
726
804
  if (invocation.watch) generateArgs.push("--watch");
727
805
  if (invocation.format === "json") {
728
806
  const generated = await captureStdout(() => main(generateArgs));
@@ -743,7 +821,8 @@ async function runSetupCommand(invocation, options = {}) {
743
821
  dependency: init.dependency,
744
822
  gitignoreEntries: init.gitignoreEntries,
745
823
  install,
746
- notes: init.notes
824
+ notes: init.notes,
825
+ hook
747
826
  },
748
827
  manifest
749
828
  },
@@ -938,10 +1017,16 @@ ${USAGE}
938
1017
  includeMonolith: invocation.includeMonolith,
939
1018
  includeSensitiveFiles: invocation.includeSensitiveFiles,
940
1019
  maxPartBytes: invocation.maxPartBytes,
1020
+ maxPartTokens: invocation.maxPartTokens,
941
1021
  largeFileBytes: invocation.largeFileBytes,
942
1022
  maxMonolithBytes: invocation.maxMonolithBytes,
943
1023
  skipArtifacts: invocation.skipArtifacts,
944
- 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
945
1030
  },
946
1031
  invocation.cwd
947
1032
  );
@@ -1017,6 +1102,10 @@ ${USAGE}
1017
1102
  return fail(invocation.format, err.message);
1018
1103
  }
1019
1104
  }
1105
+ if (invocation.watch) {
1106
+ const problem = watchSupportProblem();
1107
+ if (problem !== null) return fail(invocation.format, problem);
1108
+ }
1020
1109
  const started = performance.now();
1021
1110
  info(`\u{1F4F8} ${invocation.dryRun ? "Planning snapshot (dry run)" : "Generating snapshot"} for: ${resolved.cwd}`);
1022
1111
  info(
@@ -1057,7 +1146,11 @@ ${USAGE}
1057
1146
  const reports = [
1058
1147
  artifacts.projectTree && "project-tree",
1059
1148
  artifacts.navigation && "navigation",
1060
- 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"
1061
1154
  ].filter(Boolean);
1062
1155
  if (reports.length > 0) {
1063
1156
  info(
@@ -1104,7 +1197,7 @@ ${USAGE}
1104
1197
  `);
1105
1198
  }
1106
1199
  if (invocation.watch) {
1107
- return await runWatchLoop(invocation, resolved);
1200
+ return await runWatchLoop(invocation, resolved, { generatedAlready: true });
1108
1201
  }
1109
1202
  return manifest.validation.status === "passed" ? 0 : 1;
1110
1203
  } catch (err) {
@@ -1163,10 +1256,22 @@ function decideWatchEvent(state, event, paths) {
1163
1256
  }
1164
1257
  return { state, action: "schedule" };
1165
1258
  }
1166
- async function runWatchLoop(invocation, resolved) {
1167
- const outDir = path2.resolve(resolved.cwd, resolved.outDir);
1168
- const tmpDir = path2.resolve(resolved.cwd, resolved.tmpDir);
1169
- 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);
1170
1275
  let state = initialWatchState();
1171
1276
  let timer = null;
1172
1277
  let stopped = false;
@@ -1208,14 +1313,21 @@ async function runWatchLoop(invocation, resolved) {
1208
1313
  includeMonolith: invocation.includeMonolith,
1209
1314
  includeSensitiveFiles: invocation.includeSensitiveFiles,
1210
1315
  maxPartBytes: invocation.maxPartBytes,
1316
+ maxPartTokens: invocation.maxPartTokens,
1211
1317
  largeFileBytes: invocation.largeFileBytes,
1212
1318
  maxMonolithBytes: invocation.maxMonolithBytes,
1213
1319
  skipArtifacts: invocation.skipArtifacts,
1214
- 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
1215
1326
  },
1216
1327
  invocation.cwd
1217
1328
  );
1218
1329
  const manifest = generateSnapshot(next, { dryRun: invocation.dryRun });
1330
+ paths = watchPathsFor(next);
1219
1331
  const elapsed = ((performance.now() - started) / 1e3).toFixed(2);
1220
1332
  info(
1221
1333
  `\u267B\uFE0F ${(/* @__PURE__ */ new Date()).toISOString()} ${manifest.counts.totalFiles} file(s), ${manifest.counts.partsCount} part(s), ${manifest.validation.status} (${elapsed}s)`
@@ -1229,25 +1341,37 @@ async function runWatchLoop(invocation, resolved) {
1229
1341
  }
1230
1342
  };
1231
1343
  info("\u{1F440} Watching for changes. Press Ctrl+C to stop.");
1232
- void regenerate();
1233
- const watcher = fs.watch(resolved.cwd, { recursive: true }, (_event, filename) => {
1234
- const decision = decideWatchEvent(
1235
- state,
1236
- {
1237
- path: filename ? path2.resolve(resolved.cwd, filename.toString()) : null,
1238
- now: performance.now()
1239
- },
1240
- paths
1241
- );
1242
- state = decision.state;
1243
- if (decision.action === "pending") {
1244
- if (timer) {
1245
- clearTimeout(timer);
1246
- 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;
1247
1362
  }
1248
- return;
1249
- }
1250
- 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.");
1251
1375
  });
1252
1376
  await new Promise((resolve2) => {
1253
1377
  const stop = () => {
@@ -1277,6 +1401,4 @@ if (invokedDirectly) {
1277
1401
  );
1278
1402
  }
1279
1403
 
1280
- export { UsageError, WATCH_DEBOUNCE_MS, WATCH_SETTLE_MS, beginWatchRun, cleanInstallEnv, decideWatchEvent, finishWatchRun, initialWatchState, isGeneratedWatchPath, main, parseByteOption, parseCliArgs, parsePositiveInteger };
1281
- //# sourceMappingURL=cli.js.map
1282
- //# 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 };