token-goat 2.9.4 → 2.9.6

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.
@@ -86,7 +86,7 @@ import {
86
86
  runZipRead,
87
87
  symbolNamesInFile,
88
88
  upsertNote
89
- } from "./token-goat-chunk-US57I7GN.mjs";
89
+ } from "./token-goat-chunk-46VKOCUH.mjs";
90
90
  import {
91
91
  DEFAULT_RECONCILE_BUDGET_MS,
92
92
  GEMINI_TOOL_NAME_MAP,
@@ -108,7 +108,7 @@ import {
108
108
  runReconcile,
109
109
  storeWebOutput,
110
110
  summarizeResidentContext
111
- } from "./token-goat-chunk-FSC44QOH.mjs";
111
+ } from "./token-goat-chunk-7GJBID7S.mjs";
112
112
  import {
113
113
  AGENT_SALT_MARKER,
114
114
  BASH_OUTPUT_SUBDIR,
@@ -121,9 +121,6 @@ import {
121
121
  SESSIONS_SUBDIR,
122
122
  SKILLS_OUTPUT_SUBDIR,
123
123
  SKIP_DIRS,
124
- UNTRUSTED_FILE_TAG,
125
- UNTRUSTED_TOOL_TAG,
126
- UNTRUSTED_WEB_TAG,
127
124
  WORKER_HEARTBEAT_STALE_MS,
128
125
  WorkerAlreadyRunningError,
129
126
  applyIndexingPriority,
@@ -154,8 +151,6 @@ import {
154
151
  extractCompactFromMarker,
155
152
  extractNamedSection,
156
153
  fenceUntrusted,
157
- fenceUntrustedContent,
158
- fenceUntrustedOcrText,
159
154
  findClaudeMdFiles,
160
155
  findContentDuplicates,
161
156
  findLatestSessionId,
@@ -240,7 +235,7 @@ import {
240
235
  visionTokensSavedByText,
241
236
  walkProject,
242
237
  writeCompact
243
- } from "./token-goat-chunk-CBO45DYZ.mjs";
238
+ } from "./token-goat-chunk-3HJQR4OO.mjs";
244
239
  import {
245
240
  C,
246
241
  CONFIG_KEY_ENV_OVERRIDES,
@@ -249,11 +244,15 @@ import {
249
244
  FILTERS,
250
245
  LOCK_WAIT_MS_HARDENED,
251
246
  MATERIALIZE_SHRUNK_IMAGE_JS,
247
+ PACKAGE_NAME,
252
248
  PROJECT_LOCKED_KEYS,
253
249
  PROJECT_LOCKED_SECTIONS,
254
250
  RESET,
255
251
  TOOL_FILTERS,
256
252
  ToolFilter,
253
+ UNTRUSTED_FILE_TAG,
254
+ UNTRUSTED_TOOL_TAG,
255
+ UNTRUSTED_WEB_TAG,
257
256
  VERSION,
258
257
  _useRichStats,
259
258
  anchoredMarkerPattern,
@@ -283,6 +282,8 @@ import {
283
282
  envBool,
284
283
  escapeRegExp,
285
284
  extractErrorMessage,
285
+ fenceUntrustedContent,
286
+ fenceUntrustedOcrText,
286
287
  fg,
287
288
  fileIsAbsent,
288
289
  findProject,
@@ -337,6 +338,7 @@ import {
337
338
  requirePositiveStrictInt,
338
339
  resolveConfigKeyLayer,
339
340
  resolveIndexPath,
341
+ resolveOnPath,
340
342
  resolveProjectRoot,
341
343
  runGit,
342
344
  safeSlice,
@@ -366,7 +368,7 @@ import {
366
368
  withFileLock,
367
369
  withRetryOnLock,
368
370
  writeJsonSettings
369
- } from "./token-goat-chunk-3ZR4NLB3.mjs";
371
+ } from "./token-goat-chunk-T2OE7MYM.mjs";
370
372
  import {
371
373
  __export,
372
374
  init_define_import_meta_env
@@ -5952,6 +5954,7 @@ function statsJsonPayload(summary) {
5952
5954
  by_command: summary.by_command,
5953
5955
  by_source: summary.by_source,
5954
5956
  by_harness: summary.by_harness,
5957
+ by_pricing_version: summary.by_pricing_version,
5955
5958
  counts: summary.counts,
5956
5959
  window_days: summary.window_days
5957
5960
  };
@@ -5989,7 +5992,7 @@ init_define_import_meta_env();
5989
5992
  import * as fs12 from "fs";
5990
5993
  import * as os8 from "os";
5991
5994
  import * as path12 from "path";
5992
- import { execSync, spawnSync as spawnSync2 } from "child_process";
5995
+ import { spawnSync as spawnSync2 } from "child_process";
5993
5996
  function globalMcpConfigPath() {
5994
5997
  const copilotHome = process.env["COPILOT_HOME"];
5995
5998
  const root = copilotHome !== void 0 && copilotHome.trim() !== "" ? path12.resolve(copilotHome) : path12.join(os8.homedir(), ".copilot");
@@ -6076,12 +6079,16 @@ function checkMcpProcessHealth(processes) {
6076
6079
  }
6077
6080
  function runProcessListCommand() {
6078
6081
  const command = "Get-CimInstance Win32_Process | Select-Object ProcessId,ParentProcessId,Name,CommandLine | ConvertTo-Json -Compress";
6079
- return execSync(`powershell.exe -NoProfile -NonInteractive -Command "${command}"`, {
6082
+ const systemRoot = process.env["SystemRoot"] ?? process.env["windir"] ?? "C:\\Windows";
6083
+ const shell = path12.join(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
6084
+ const result = spawnSync2(fs12.existsSync(shell) ? shell : "powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", command], {
6080
6085
  encoding: "utf8",
6081
6086
  timeout: 2e4,
6082
6087
  maxBuffer: 10 * 1024 * 1024,
6083
6088
  windowsHide: true
6084
6089
  });
6090
+ if (result.error !== void 0) throw result.error;
6091
+ return result.stdout ?? "";
6085
6092
  }
6086
6093
  function readWindowsProcesses(runCommand = runProcessListCommand) {
6087
6094
  if (process.platform !== "win32") return [];
@@ -6286,20 +6293,14 @@ function checkDirtyQueueHealth(dataDir2) {
6286
6293
  return { name: "Dirty queue", status: "ok", message: `${pendingCount} file(s) pending, worker actively draining` };
6287
6294
  }
6288
6295
  function checkInstall() {
6289
- try {
6290
- const output = execSync("token-goat --version", { encoding: "utf-8" });
6291
- return {
6292
- name: "Installation",
6293
- status: "ok",
6294
- message: output.trim()
6295
- };
6296
- } catch {
6297
- return {
6298
- name: "Installation",
6299
- status: "fail",
6300
- message: "token-goat command not found; run: npm install -g token-goat-ts"
6301
- };
6296
+ const resolved = resolveOnPath("token-goat");
6297
+ if (resolved !== null) {
6298
+ const isBatch = /\.(?:cmd|bat)$/i.test(resolved);
6299
+ const comspec = path12.join(process.env["SystemRoot"] ?? process.env["windir"] ?? "C:\\Windows", "System32", "cmd.exe");
6300
+ const result = isBatch ? spawnSync2(fs12.existsSync(comspec) ? comspec : "cmd.exe", ["/d", "/s", "/c", resolved, "--version"], { encoding: "utf-8", timeout: 15e3, windowsHide: true }) : spawnSync2(resolved, ["--version"], { encoding: "utf-8", timeout: 15e3, windowsHide: true });
6301
+ if (result.status === 0) return { name: "Installation", status: "ok", message: (result.stdout ?? "").trim() };
6302
6302
  }
6303
+ return { name: "Installation", status: "fail", message: `token-goat command not found; run: npm install -g ${PACKAGE_NAME}` };
6303
6304
  }
6304
6305
  function checkTsCompiler() {
6305
6306
  if (isAvailable2()) {
@@ -6488,7 +6489,9 @@ var LOCKED_BOOLEAN_SAFE_VALUE = {
6488
6489
  "screenshot.block_private_targets": true,
6489
6490
  "network.offline": true,
6490
6491
  "indexing.cross_project_symbols": false,
6491
- "hints.fold_code_bodies": false,
6492
+ // `hints.fold_code_bodies` is deliberately absent for the same reason as `hints.fold_prose_paragraphs` below: it now ships on. Its cost was the thing keeping it off, and that cost is now measured rather than assumed -- 6.6 points of recovery reads beyond what the same symbols draw anyway, against a fold that withholds a whole function body. It remains project-locked, so the environment deciding it is still reported by the value-replacement arm.
6493
+ // `hints.fold_prose_paragraphs` is deliberately absent: it now ships on, so neither direction is a weakening. Setting it true restates the shipped default, and setting it false shows the reader MORE of a document, so warning on either would break this file's own rule that a default install stays quiet. It remains project-locked, so the environment deciding it is still worth reporting, which is what the value-replacement arm below does.
6494
+ // The three remaining hints keys, fold_comment_blocks and outline_large_documents and skeleton_large_sources, are absent for exactly the same reason and just as deliberately: each ships on, so neither direction of an override is a weakening, and a false entry here would make a default install warn about itself. All three stay project-locked, so the environment deciding one is still reported by the value-replacement arm below.
6492
6495
  "webfetch.compress_bodies": true
6493
6496
  };
6494
6497
  function lockedEnvOverridableKeys() {
@@ -6555,9 +6558,15 @@ function checkSecurityPosture(cfg, dataDirPath) {
6555
6558
  cfg.mcp.confine_reads_to_project_root ? {
6556
6559
  name: "Security mcp roots",
6557
6560
  status: "ok",
6558
- message: extraRoots === 0 ? "reads are confined to the project root" : `reads are confined to the project root plus ${extraRoots} configured root${extraRoots === 1 ? "" : "s"}`
6561
+ // Two separate controls, and reporting only the first read as a guarantee about the second. `confine_reads_to_project_root` keeps a read inside the root the caller named; `mcp.allowed_roots` decides which roots may be named at all, and empty means any of them (assertRootAllowed returns early). Saying "confined to the project root" while the caller picks that root overstates the confinement, so the empty case now names itself.
6562
+ message: extraRoots === 0 ? "reads are confined to the project root the caller names, but mcp.allowed_roots is empty, so an MCP caller may name any root on this machine" : `reads are confined to the project root, and callers may name only the ${extraRoots} root${extraRoots === 1 ? "" : "s"} in mcp.allowed_roots`
6559
6563
  } : { name: "Security mcp roots", status: "warn", message: "confinement is off (mcp.confine_reads_to_project_root): a read can leave the project" }
6560
6564
  );
6565
+ results.push({
6566
+ name: "Security symbol scope",
6567
+ status: "ok",
6568
+ message: cfg.indexing.cross_project_symbols ? "symbol lookups can resolve into other projects in the machine-wide index (indexing.cross_project_symbols = false confines them to this project)" : "symbol lookups are confined to this project"
6569
+ });
6561
6570
  const overridden = envOverriddenSecuritySettings();
6562
6571
  const weakened = overridden.filter((o) => o.kind === "weakened");
6563
6572
  const replaced = overridden.filter((o) => o.kind === "replaced");
@@ -14093,15 +14102,15 @@ var READ_DIVERT_MARKER_RE = /(?:was already read this session|Already read |You'
14093
14102
  var READ_DIVERT_MAX_BYTES = 2500;
14094
14103
  var READ_FULL_SERVE_MIN_BYTES = 10240;
14095
14104
  var DENY_TEMPLATES = [
14096
- { kind: "node_modules_deny", re: /node_modules is typically noise/ },
14097
- { kind: "lock_file_deny", re: /Lock files are rarely useful to read in full/ },
14098
- { kind: "tsbuildinfo_deny", re: /TypeScript incremental build cache file/ },
14099
- { kind: "generated_build_deny", re: /Generated\/build artifact — read the source file instead\./ },
14100
- { kind: "compact_sidecar_served", re: /Serving the extractive compact sidecar in place of the full file/ },
14101
- { kind: "notebook_sidecar_served", re: /Serving the output-stripped notebook in place of the full file/ },
14105
+ { kind: "node_modules_deny", re: /node_modules is typically noise/, tool: "Read" },
14106
+ { kind: "lock_file_deny", re: /Lock files are rarely useful to read in full/, tool: "Read" },
14107
+ { kind: "tsbuildinfo_deny", re: /TypeScript incremental build cache file/, tool: "Read" },
14108
+ { kind: "generated_build_deny", re: /Generated\/build artifact — read the source file instead\./, tool: "Read" },
14109
+ { kind: "compact_sidecar_served", re: /Serving the extractive compact sidecar in place of the full file/, tool: "Read" },
14110
+ { kind: "notebook_sidecar_served", re: /Serving the output-stripped notebook in place of the full file/, tool: "Read" },
14102
14111
  // Must stay ABOVE markdown_heading_tree_deny: this deny's message embeds the same "Large markdown file (N headings)" guidance block, so the generic alternative would swallow it and the new intervention would be uncountable. DENY_TEMPLATES is scanned with `.find`, so the more specific wording has to come first.
14103
- { kind: "subagent_markdown_first_read_deny", re: /Subagent first read of a large markdown file/ },
14104
- { kind: "markdown_heading_tree_deny", re: /Large markdown file \(\d+ headings\)/ },
14112
+ { kind: "subagent_markdown_first_read_deny", re: /Subagent first read of a large markdown file/, tool: "Read" },
14113
+ { kind: "markdown_heading_tree_deny", re: /Large markdown file \(\d+ headings\)/, tool: "Read" },
14105
14114
  // The first alternative is a wording hooks_read.ts no longer emits (it claimed a compact-manifest
14106
14115
  // section that never existed, reworded in 3d044feb). It stays because this table classifies a
14107
14116
  // historical corpus, not just today's output: transcripts written before the rewording still
@@ -14109,24 +14118,32 @@ var DENY_TEMPLATES = [
14109
14118
  // 30 -- 41% of its history -- with a green suite. A superseded alternative is only removable
14110
14119
  // once no transcript contains it, which source code cannot tell you. See the superseded-wording
14111
14120
  // test in tests/deny_outcomes.test.ts.
14112
- { kind: "memory_md_reread_deny", re: /MEMORY\.md was read this session\. Its content is in the compact manifest|already read this session\. Memory files rarely change mid-session/ },
14113
- { kind: "improve_state_reread_deny", re: /Orchestrator state already read this session/ },
14114
- { kind: "env_reread_deny", re: /Environment files rarely change mid-session/ },
14115
- { kind: "session_artifact_truncated_deny", re: /File was truncated on last read\. Use `token-goat bash-output/ },
14116
- { kind: "session_artifact_unchanged_deny", re: /is unchanged since last read\. Use `token-goat bash-output/ },
14117
- { kind: "session_artifact_diff_deny", re: /Content changed since last read of [\s\S]*?bash-output --file/ },
14118
- { kind: "session_artifact_large_deny", re: /(?:Session transcript|Tool-result file) is large \(/ },
14119
- { kind: "session_artifact_generic_reread_deny", re: /already read this session\. Use `token-goat bash-output --file/ },
14120
- { kind: "truncated_read_deny", re: /File was truncated on last read \(>33K tokens\)/ },
14121
- { kind: "doc_unchanged_deny", re: /is unchanged since last read\. Use `token-goat (?:section|read)/ },
14122
- { kind: "doc_diff_deny", re: /Content changed since last read of [\s\S]*?Use `token-goat (?:section|read)/ },
14123
- { kind: "read_served_deny", re: /was already served in this session, byte for byte/ },
14124
- { kind: "markdown_already_read_deny", re: /Markdown file already read this session\. Use `token-goat section/ },
14125
- { kind: "read_count_deny", re: /Read this file \d+ times already/ },
14126
- { kind: "generic_reread_deny", re: /was already read this session \(\d+ read/ },
14127
- { kind: "large_file_deny", re: /is very large \(\d+(?:\.\d+)?KB\)\./ },
14128
- { kind: "file_type_handler_deny", re: /too large to preview \(exceeds the in-hook scan cap\)|cannot be read as text\.|Read cannot return spreadsheet content|Read cannot return slide content|Read cannot return document content|Use Read with offset and limit parameters to read specific line ranges/ }
14121
+ { kind: "memory_md_reread_deny", re: /MEMORY\.md was read this session\. Its content is in the compact manifest|already read this session\. Memory files rarely change mid-session/, tool: "Read" },
14122
+ { kind: "improve_state_reread_deny", re: /Orchestrator state already read this session/, tool: "Read" },
14123
+ { kind: "env_reread_deny", re: /Environment files rarely change mid-session/, tool: "Read" },
14124
+ { kind: "session_artifact_truncated_deny", re: /File was truncated on last read\. Use `token-goat bash-output/, tool: "Read" },
14125
+ { kind: "session_artifact_unchanged_deny", re: /is unchanged since last read\. Use `token-goat bash-output/, tool: "Read" },
14126
+ { kind: "session_artifact_diff_deny", re: /Content changed since last read of [\s\S]*?bash-output --file/, tool: "Read" },
14127
+ { kind: "session_artifact_large_deny", re: /(?:Session transcript|Tool-result file) is large \(/, tool: "Read" },
14128
+ { kind: "session_artifact_generic_reread_deny", re: /already read this session\. Use `token-goat bash-output --file/, tool: "Read" },
14129
+ { kind: "truncated_read_deny", re: /File was truncated on last read \(>33K tokens\)/, tool: "Read" },
14130
+ { kind: "doc_unchanged_deny", re: /is unchanged since last read\. Use `token-goat (?:section|read)/, tool: "Read" },
14131
+ { kind: "doc_diff_deny", re: /Content changed since last read of [\s\S]*?Use `token-goat (?:section|read)/, tool: "Read" },
14132
+ { kind: "read_served_deny", re: /was already served in this session, byte for byte/, tool: "Read" },
14133
+ { kind: "markdown_already_read_deny", re: /Markdown file already read this session\. Use `token-goat section/, tool: "Read" },
14134
+ { kind: "read_count_deny", re: /Read this file \d+ times already/, tool: "Read" },
14135
+ { kind: "generic_reread_deny", re: /was already read this session \(\d+ read/, tool: "Read" },
14136
+ { kind: "large_file_deny", re: /is very large \(\d+(?:\.\d+)?KB\)\./, tool: "Read" },
14137
+ { kind: "file_type_handler_deny", re: /too large to preview \(exceeds the in-hook scan cap\)|cannot be read as text\.|Read cannot return spreadsheet content|Read cannot return slide content|Read cannot return document content|Use Read with offset and limit parameters to read specific line ranges/, tool: "Read" },
14138
+ // Skill-tool denies from src/hooks_skill.ts's preSkillHandler -- kind names prefixed skill_ so they read as a family, distinct from the Read-deny kinds above. The two heading-tree wordings share the prose around them ("...its heading tree...instead of the full body. Use `token-goat skill-section NAME '<heading>'`...") but each anchors on an infix the other never emits -- truncated always says "shows N of M headings below" (no "inlined"), complete always says "(N headings) is inlined below" (no "shows") -- so neither regex can match the other's text; skill_heading_tree_truncated_deny is still listed first on the same specific-before-generic principle as markdown_heading_tree_deny above, in case a future rewording narrows the gap.
14139
+ { kind: "skill_already_loaded_deny", re: /was already loaded this session and is cached/, tool: "Skill" },
14140
+ { kind: "skill_compact_slice_deny", re: /has a compact slice available/, tool: "Skill" },
14141
+ // The compact slice has two outcomes and they are separate kinds because they cost different things: the pointer above withholds the slice behind a command, this one inlines the slice and withholds only the rest of the body. Its "(N bytes) is inlined below" cannot collide with the heading-tree wording below, which requires "heading tree (N headings) is inlined below".
14142
+ { kind: "skill_compact_slice_inlined_deny", re: /compact slice \(\d+ bytes\) is inlined below instead of the full body/, tool: "Skill" },
14143
+ { kind: "skill_heading_tree_truncated_deny", re: /heading tree shows \d+ of \d+ headings below instead of the full body/, tool: "Skill" },
14144
+ { kind: "skill_heading_tree_complete_deny", re: /heading tree \(\d+ headings\) is inlined below instead of the full body/, tool: "Skill" }
14129
14145
  ];
14146
+ var DENY_TEMPLATE_KINDS = DENY_TEMPLATES.map((t) => t.kind);
14130
14147
  var DENY_KINDS_WITH_SIZE = /* @__PURE__ */ new Set(["large_file_deny", "session_artifact_large_deny", "file_type_handler_deny"]);
14131
14148
  var DENY_SIZE_RE = /(\d+(?:\.\d+)?)\s*(B|KB|MB|GB|TB)\b/;
14132
14149
  function parseWithheldBytes(kind, text) {
@@ -14396,9 +14413,8 @@ async function auditOneFile(filePath, s, toolMap, attachmentMap, hookMap, laneOb
14396
14413
  s.editErrorBaseline.totalErrors += 1;
14397
14414
  editErrorById.set(id, true);
14398
14415
  }
14399
- if (name === "Read") {
14400
- s.readInterception.readResults += 1;
14401
- const denyTemplate = DENY_TEMPLATES.find((t) => t.re.test(text));
14416
+ if (name === "Read" || name === "Skill") {
14417
+ const denyTemplate = DENY_TEMPLATES.find((t) => t.tool === name && t.re.test(text));
14402
14418
  if (denyTemplate !== void 0) {
14403
14419
  const path31 = readPathById.get(id) ?? "";
14404
14420
  const lastSlash = path31.lastIndexOf("/");
@@ -14413,6 +14429,9 @@ async function auditOneFile(filePath, s, toolMap, attachmentMap, hookMap, laneOb
14413
14429
  toolCalls: []
14414
14430
  });
14415
14431
  }
14432
+ }
14433
+ if (name === "Read") {
14434
+ s.readInterception.readResults += 1;
14416
14435
  if (bytes < READ_DIVERT_MAX_BYTES && READ_DIVERT_MARKER_RE.test(text)) {
14417
14436
  s.readInterception.divertedByMarker += 1;
14418
14437
  s.readInterception.divertedBytes += bytes;
@@ -14942,7 +14961,7 @@ function analyzeMcpCache() {
14942
14961
  const metrics = serverMetrics.get(toolName);
14943
14962
  metrics.callCount += 1;
14944
14963
  metrics.totalBytes += sizeBytes;
14945
- metrics.perCallEstimate = Math.floor(metrics.totalBytes / metrics.callCount / 3) + 1;
14964
+ metrics.perCallEstimate = estimateTokensFromLength(metrics.totalBytes / metrics.callCount);
14946
14965
  }
14947
14966
  return serverMetrics;
14948
14967
  }
@@ -15711,7 +15730,7 @@ async function cmdMcpServe() {
15711
15730
  let StdioServerTransport;
15712
15731
  try {
15713
15732
  ;
15714
- ({ createMcpServer } = await import("./token-goat-chunk-OZAEJQ7L.mjs"));
15733
+ ({ createMcpServer } = await import("./token-goat-chunk-C7LLUVUT.mjs"));
15715
15734
  ({ StdioServerTransport } = await import("./token-goat-chunk-EVC4TOLE.mjs"));
15716
15735
  } catch (err2) {
15717
15736
  process.stderr.write(
@@ -15736,7 +15755,7 @@ async function cmdHook(event, opts) {
15736
15755
  if (typeof opts.harness === "string" && opts.harness.length > 0) {
15737
15756
  process.env[ENV_KEYS.HARNESS_OVERRIDE] = opts.harness;
15738
15757
  }
15739
- const { relay } = await import("./token-goat-chunk-EQKNR7CN.mjs");
15758
+ const { relay } = await import("./token-goat-chunk-TX64YIIV.mjs");
15740
15759
  await relay(event);
15741
15760
  }
15742
15761
  async function cmdInstall(opts) {
@@ -16278,23 +16297,22 @@ async function cmdPdfLocate(file, pattern, opts) {
16278
16297
  if (opts.maxMatches !== void 0) locateOpts.maxMatches = requirePositiveInt("--max-matches", opts.maxMatches);
16279
16298
  if (opts.context !== void 0) locateOpts.context = requirePositiveInt("--context", opts.context);
16280
16299
  if (opts.pages !== void 0) locateOpts.pages = opts.pages;
16281
- const matches2 = await runPdfLocate(file, pattern, locateOpts);
16300
+ const { matches: matches2, truncated } = await runPdfLocate(file, pattern, locateOpts);
16282
16301
  const pages = matches2.map((m) => m.page);
16283
16302
  let printed;
16284
16303
  if (opts.json === true) {
16285
16304
  const fencedMatches = matches2.map((m) => ({ ...m, snippet: fenceFileFieldIfMatched(m.snippet) }));
16286
- printed = JSON.stringify({ file, pattern, matchCount: fencedMatches.length, pages, matches: fencedMatches }, null, 2);
16305
+ printed = JSON.stringify({ file, pattern, matchCount: fencedMatches.length, truncated, pages, matches: fencedMatches }, null, 2);
16287
16306
  out(printed);
16288
16307
  } else if (matches2.length === 0) {
16289
16308
  printed = "(no matches)";
16290
16309
  out(printed);
16291
16310
  } else {
16292
16311
  const lines = matches2.map((m) => `p${m.page}: ${m.snippet}`);
16293
- printed = fenceFileText(
16294
- `${lines.join("\n")}
16312
+ const summary = truncated ? `at least ${countNoun(matches2.length, "match", "matches")} across at least ${countNoun(pages.length, "page")}; scan stopped at --max-matches, raise it for more` : `${countNoun(matches2.length, "match", "matches")} across ${countNoun(pages.length, "page")}`;
16313
+ printed = fenceFileText(`${lines.join("\n")}
16295
16314
 
16296
- ${countNoun(matches2.length, "match", "matches")} across ${countNoun(pages.length, "page")}`
16297
- );
16315
+ ${summary}`);
16298
16316
  out(printed);
16299
16317
  }
16300
16318
  const fullSourceBytes = fileSizeOrZero(file);
@@ -16670,7 +16688,7 @@ function emitExtraFileArgsNote(command, first, extras, opts = {}) {
16670
16688
  }
16671
16689
  async function cmdCompress(opts) {
16672
16690
  try {
16673
- const bashRunner = await import("./token-goat-chunk-5BHXZEOI.mjs");
16691
+ const bashRunner = await import("./token-goat-chunk-UJORWTI4.mjs");
16674
16692
  if (opts.compress === false) {
16675
16693
  process.exitCode = bashRunner.runRaw(opts.cmd, parseTimeout(opts.timeout, bashRunner.DEFAULT_TIMEOUT_SECONDS));
16676
16694
  return;
@@ -2,7 +2,7 @@ import { createRequire as __cjsRequire } from 'node:module';
2
2
  const require = __cjsRequire(import.meta.url);
3
3
  import {
4
4
  detectHarness
5
- } from "./token-goat-chunk-3ZR4NLB3.mjs";
5
+ } from "./token-goat-chunk-T2OE7MYM.mjs";
6
6
  import {
7
7
  init_define_import_meta_env
8
8
  } from "./token-goat-chunk-A37V4PBF.mjs";
@@ -78,6 +78,7 @@ function canRunWrappedShell() {
78
78
  }
79
79
 
80
80
  export {
81
+ bashOutputCapBytes,
81
82
  deliveredOutputBytes,
82
83
  wrappedShell,
83
84
  canRunWrappedShell