token-goat 2.8.4 → 2.8.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,11 +86,12 @@ import {
86
86
  runZipRead,
87
87
  symbolNamesInFile,
88
88
  upsertNote
89
- } from "./token-goat-chunk-RE7S7H26.mjs";
89
+ } from "./token-goat-chunk-XAWIELXH.mjs";
90
90
  import {
91
91
  BASH_OUTPUT_SUBDIR,
92
92
  GEMINI_TOOL_NAME_MAP,
93
93
  HOOK_EVENTS,
94
+ RECALL_DEFAULT_LIMIT,
94
95
  WEB_OUTPUT_SUBDIR,
95
96
  accumulateResidentLine,
96
97
  buildCommandManifest,
@@ -113,7 +114,7 @@ import {
113
114
  searchRecall,
114
115
  storeWebOutput,
115
116
  summarizeResidentContext
116
- } from "./token-goat-chunk-IZRXU64B.mjs";
117
+ } from "./token-goat-chunk-TELKICYU.mjs";
117
118
  import {
118
119
  AGENT_SALT_MARKER,
119
120
  CONTEXT_AUTOCOMPACT_TOKENS,
@@ -131,6 +132,7 @@ import {
131
132
  WORKER_HEARTBEAT_STALE_MS,
132
133
  WorkerAlreadyRunningError,
133
134
  anchoredMarkerPattern,
135
+ applyIndexingPriority,
134
136
  auditClaudeMd,
135
137
  buildExtractiveCompact,
136
138
  buildLineDiff,
@@ -160,6 +162,7 @@ import {
160
162
  extractChecklistSection,
161
163
  extractCompactFromMarker,
162
164
  extractNamedSection,
165
+ fenceUntrusted,
163
166
  fenceUntrustedContent,
164
167
  fenceUntrustedOcrText,
165
168
  findClaudeMdFiles,
@@ -177,6 +180,7 @@ import {
177
180
  formatXlsxRange,
178
181
  getContextPressure,
179
182
  getDirtyPathsFor,
183
+ getEmbeddingCoverage,
180
184
  getFileEntry,
181
185
  getHintStatsSummary,
182
186
  getHintStatsTotals,
@@ -190,6 +194,7 @@ import {
190
194
  indexFileEmbeddings,
191
195
  indexFileSync,
192
196
  indexedPathSpellingIsStale,
197
+ injectionFencingEnabled,
193
198
  installClaudeMd,
194
199
  installCodex,
195
200
  installCopilotCli,
@@ -239,7 +244,7 @@ import {
239
244
  resetHintStats,
240
245
  runContextStats,
241
246
  runDetachedWorkerDaemon,
242
- scanForInjectionPatterns,
247
+ scanAndRecord,
243
248
  shrinkImage,
244
249
  skillOutputsDir,
245
250
  startDetachedWorker,
@@ -260,7 +265,7 @@ import {
260
265
  vscodeDecoderConfigured,
261
266
  walkProject,
262
267
  writeCompact
263
- } from "./token-goat-chunk-4OTIB7SB.mjs";
268
+ } from "./token-goat-chunk-DK4VLLYB.mjs";
264
269
  import {
265
270
  C,
266
271
  CONFIG_KEY_ENV_OVERRIDES,
@@ -343,7 +348,7 @@ import {
343
348
  withFileLock,
344
349
  withRetryOnLock,
345
350
  writeJsonSettings
346
- } from "./token-goat-chunk-E76UNTVK.mjs";
351
+ } from "./token-goat-chunk-2JZ66BBE.mjs";
347
352
  import {
348
353
  __export
349
354
  } from "./token-goat-chunk-AEX54RUZ.mjs";
@@ -6027,6 +6032,42 @@ function checkSymbolCount(dbPath, rootDir) {
6027
6032
  };
6028
6033
  }
6029
6034
  }
6035
+ var EMBED_COVERAGE_WARN_FRACTION = 0.25;
6036
+ function checkEmbeddingCoverage(dbPath, rootDir) {
6037
+ if (!fs12.existsSync(dbPath)) {
6038
+ return { name: "Embedding coverage", status: "ok", message: "no database yet" };
6039
+ }
6040
+ const cfg = loadConfig();
6041
+ if (!cfg.indexing.embeddings_enabled) {
6042
+ return { name: "Embedding coverage", status: "ok", message: "disabled (indexing.embeddings_enabled = false)" };
6043
+ }
6044
+ try {
6045
+ const { indexedFiles, embeddedFiles } = getEmbeddingCoverage(dbPath, rootDir);
6046
+ if (indexedFiles === 0) {
6047
+ return { name: "Embedding coverage", status: "ok", message: "no indexed files yet" };
6048
+ }
6049
+ const pct2 = Math.round(embeddedFiles / indexedFiles * 100);
6050
+ const sizeKb = cfg.indexing.large_file_symbol_only_kb;
6051
+ if (embeddedFiles / indexedFiles < EMBED_COVERAGE_WARN_FRACTION) {
6052
+ return {
6053
+ name: "Embedding coverage",
6054
+ status: "warn",
6055
+ message: `only ${embeddedFiles} of ${indexedFiles} indexed file(s) (${pct2}%) have embeddings \u2014 'semantic' searches those files only, and reports finding nothing in the same words it uses after searching everything. Files over indexing.large_file_symbol_only_kb (currently ${sizeKb} KB) are indexed for symbols only and are the usual reason; raise it with 'token-goat config set indexing.large_file_symbol_only_kb <KB>' and re-embed with 'token-goat index --force' to widen coverage. Exact symbol lookups are unaffected`
6056
+ };
6057
+ }
6058
+ return {
6059
+ name: "Embedding coverage",
6060
+ status: "ok",
6061
+ message: `${embeddedFiles} of ${indexedFiles} indexed file(s) (${pct2}%) have embeddings`
6062
+ };
6063
+ } catch (err2) {
6064
+ return {
6065
+ name: "Embedding coverage",
6066
+ status: "warn",
6067
+ message: `could not query embedding coverage: ${extractErrorMessage(err2)}`
6068
+ };
6069
+ }
6070
+ }
6030
6071
  var DIRTY_QUEUE_BACKLOG_WARN_THRESHOLD = 500;
6031
6072
  function checkDirtyQueueHealth(dataDir2) {
6032
6073
  let pendingCount = 0;
@@ -6395,6 +6436,7 @@ function runDoctor(dataDir2, configPath2, rootDir, processes) {
6395
6436
  const actualConfigPath = configPath2 || configPath();
6396
6437
  results.push(checkConfigValid(actualConfigPath));
6397
6438
  results.push(checkEmbeddings(loadConfig(rootDir)));
6439
+ results.push(checkEmbeddingCoverage(path11.join(actualDataDir, "global.db"), rootDir));
6398
6440
  for (const result of checkSecurityPosture(loadConfig(rootDir), actualDataDir)) results.push(result);
6399
6441
  results.push(checkDiskSpace(actualDataDir));
6400
6442
  const copilotResult = checkCopilotCli(copilotCliConfigPath(), copilotCliScriptPath());
@@ -10366,14 +10408,23 @@ function applyFiltersAndFold(lines, noNormalize, foldRepeats) {
10366
10408
  }
10367
10409
  function cmdLogfold(src, opts) {
10368
10410
  const text = readInput(src);
10369
- let lines = splitLines(text);
10411
+ const rawLines = splitLines(text);
10412
+ const allLines = rawLines.length > 1 && rawLines[rawLines.length - 1] === "" ? rawLines.slice(0, -1) : rawLines;
10413
+ let lines = allLines;
10370
10414
  if (opts.tail !== void 0) {
10371
10415
  const n = requireNonNegativeStrictInt("--tail", opts.tail);
10372
10416
  lines = lines.slice(Math.max(0, lines.length - n));
10373
10417
  }
10418
+ const inputLines = allLines.length;
10419
+ const shownLines = lines.length;
10420
+ const truncated = shownLines < inputLines;
10374
10421
  const folded = applyFiltersAndFold(lines, opts.noNormalize === true, opts.foldRepeats === true);
10422
+ if (truncated) {
10423
+ process.stderr.write(`Showing last ${shownLines} of ${inputLines} lines (raise --tail to see more).
10424
+ `);
10425
+ }
10375
10426
  if (opts.json === true) {
10376
- process.stdout.write(JSON.stringify({ lines: folded }, null, 2) + "\n");
10427
+ process.stdout.write(JSON.stringify({ lines: folded, truncated, inputLines, shownLines }, null, 2) + "\n");
10377
10428
  return;
10378
10429
  }
10379
10430
  for (const item of folded) {
@@ -10859,9 +10910,11 @@ function cmdHot(opts) {
10859
10910
  }
10860
10911
  }
10861
10912
  entries.sort((a, b) => b.readCount - a.readCount);
10913
+ const eligibleCount = entries.length;
10862
10914
  entries = entries.slice(0, limit);
10915
+ const truncated = entries.length < eligibleCount;
10863
10916
  if (opts.json === true) {
10864
- process.stdout.write(JSON.stringify({ entries }, null, 2) + "\n");
10917
+ process.stdout.write(JSON.stringify({ entries, truncated, totalCount: eligibleCount }, null, 2) + "\n");
10865
10918
  return;
10866
10919
  }
10867
10920
  if (entries.length === 0) {
@@ -10877,6 +10930,10 @@ function cmdHot(opts) {
10877
10930
  const hotDisplayRoot = getDisplayRoot();
10878
10931
  for (const e of entries) {
10879
10932
  process.stdout.write(`${e.readCount} ${toDisplayPath(hotDisplayRoot, e.path)}
10933
+ `);
10934
+ }
10935
+ if (truncated) {
10936
+ process.stdout.write(`...and ${eligibleCount - entries.length} more (raise --limit to see them).
10880
10937
  `);
10881
10938
  }
10882
10939
  }
@@ -11330,6 +11387,13 @@ async function buildResumePacket(sessionId) {
11330
11387
  function emitErr(text) {
11331
11388
  process.stderr.write(ensureNewline(text));
11332
11389
  }
11390
+ function capAndNote(rows, limit) {
11391
+ const shown = rows.slice(0, limit);
11392
+ if (shown.length < rows.length) {
11393
+ emitErr(`Showing ${shown.length} of ${rows.length} entries (raise --limit to see the rest).`);
11394
+ }
11395
+ return shown;
11396
+ }
11333
11397
  function parseLimitOpt(cmdName, limitStr, dflt = 30) {
11334
11398
  if (limitStr === void 0) return dflt;
11335
11399
  let n;
@@ -11369,7 +11433,7 @@ function getNewestSessionFiles() {
11369
11433
  function cmdBashHistory(opts) {
11370
11434
  const limit = parseLimitOpt("bash-history", opts.limit);
11371
11435
  const blobs = listBlobs(BASH_OUTPUT_SUBDIR);
11372
- const items = blobs.map(({ id, mtime, value }) => {
11436
+ const allItems = blobs.map(({ id, mtime, value }) => {
11373
11437
  if (typeof value !== "object" || value === null) return null;
11374
11438
  const v = value;
11375
11439
  return {
@@ -11379,7 +11443,8 @@ function cmdBashHistory(opts) {
11379
11443
  exitCode: typeof v["exitCode"] === "number" ? v["exitCode"] : -1,
11380
11444
  sizeBytes: typeof v["sizeBytes"] === "number" ? v["sizeBytes"] : 0
11381
11445
  };
11382
- }).filter((x) => x !== null).sort((a, b) => b.storedAt - a.storedAt).slice(0, limit);
11446
+ }).filter((x) => x !== null).sort((a, b) => b.storedAt - a.storedAt);
11447
+ const items = capAndNote(allItems, limit);
11383
11448
  if (opts.json === true) {
11384
11449
  process.stdout.write(JSON.stringify(items, null, 2) + "\n");
11385
11450
  return;
@@ -11400,7 +11465,7 @@ function cmdBashHistory(opts) {
11400
11465
  function cmdWebHistory(opts) {
11401
11466
  const limit = parseLimitOpt("web-history", opts.limit);
11402
11467
  const blobs = listBlobs(WEB_OUTPUT_SUBDIR);
11403
- const items = blobs.map(({ id, mtime, value }) => {
11468
+ const allItems = blobs.map(({ id, mtime, value }) => {
11404
11469
  if (typeof value !== "object" || value === null) return null;
11405
11470
  const v = value;
11406
11471
  return {
@@ -11409,7 +11474,8 @@ function cmdWebHistory(opts) {
11409
11474
  bytes: typeof v["content"] === "string" ? Buffer.byteLength(v["content"], "utf8") : 0,
11410
11475
  storedAt: mtime
11411
11476
  };
11412
- }).filter((x) => x !== null).sort((a, b) => b.storedAt - a.storedAt).slice(0, limit);
11477
+ }).filter((x) => x !== null).sort((a, b) => b.storedAt - a.storedAt);
11478
+ const items = capAndNote(allItems, limit);
11413
11479
  if (opts.json === true) {
11414
11480
  process.stdout.write(JSON.stringify(items, null, 2) + "\n");
11415
11481
  return;
@@ -11428,7 +11494,7 @@ function cmdWebHistory(opts) {
11428
11494
  function cmdMcpHistory(opts) {
11429
11495
  const limit = parseLimitOpt("mcp-history", opts.limit);
11430
11496
  const blobs = listBlobs(BASH_OUTPUT_SUBDIR).filter((b) => b.id.startsWith("mcp_"));
11431
- const items = blobs.map(({ id, mtime, value }) => {
11497
+ const allItems = blobs.map(({ id, mtime, value }) => {
11432
11498
  if (typeof value !== "object" || value === null) return null;
11433
11499
  const v = value;
11434
11500
  const command = typeof v["command"] === "string" ? v["command"] : "";
@@ -11439,7 +11505,8 @@ function cmdMcpHistory(opts) {
11439
11505
  storedAt: typeof v["storedAt"] === "number" ? v["storedAt"] : mtime,
11440
11506
  sizeBytes: typeof v["sizeBytes"] === "number" ? v["sizeBytes"] : 0
11441
11507
  };
11442
- }).filter((x) => x !== null).sort((a, b) => b.storedAt - a.storedAt).slice(0, limit);
11508
+ }).filter((x) => x !== null).sort((a, b) => b.storedAt - a.storedAt);
11509
+ const items = capAndNote(allItems, limit);
11443
11510
  if (opts.json === true) {
11444
11511
  process.stdout.write(JSON.stringify(items, null, 2) + "\n");
11445
11512
  return;
@@ -12442,7 +12509,12 @@ function cmdHistory(opts) {
12442
12509
  summary: typeof v["url"] === "string" ? v["url"] : ""
12443
12510
  };
12444
12511
  }).filter((x) => x !== null);
12445
- const items = [...bashItems, ...webItems].sort((a, b) => b.storedAt - a.storedAt).slice(0, limit);
12512
+ const allItems = [...bashItems, ...webItems].sort((a, b) => b.storedAt - a.storedAt);
12513
+ const items = allItems.slice(0, limit);
12514
+ if (items.length < allItems.length) {
12515
+ process.stderr.write(`Showing ${items.length} of ${allItems.length} entries (raise --limit to see the rest).
12516
+ `);
12517
+ }
12446
12518
  if (opts.json === true) {
12447
12519
  process.stdout.write(JSON.stringify(items, null, 2) + "\n");
12448
12520
  return;
@@ -12611,7 +12683,9 @@ async function buildBootstrapAudit(opts = {}) {
12611
12683
  const seenFiles = /* @__PURE__ */ new Set();
12612
12684
  const agents = await scanMetadataRoot(path20.join(home, ".claude", "agents"), "agent", diagnostics, visitedDirs, seenFiles, opts.followLinks === true);
12613
12685
  const skills = await scanMetadataRoot(path20.join(home, ".claude", "skills"), "skill", diagnostics, visitedDirs, seenFiles, opts.followLinks === true);
12614
- const largest = [...agents, ...skills].sort((a, b) => b.metadata_bytes - a.metadata_bytes || a.path.localeCompare(b.path)).slice(0, top);
12686
+ const rankedEntries = [...agents, ...skills].sort((a, b) => b.metadata_bytes - a.metadata_bytes || a.path.localeCompare(b.path));
12687
+ const largestTotal = rankedEntries.length;
12688
+ const largest = rankedEntries.slice(0, top);
12615
12689
  const metadataBytes = [...agents, ...skills].reduce((sum, entry) => sum + entry.metadata_bytes, 0);
12616
12690
  const totalTokens = context.total_tokens + Math.floor(metadataBytes / 4);
12617
12691
  const warnTokens = parseBudget("--warn-tokens", opts.warnTokens);
@@ -12635,6 +12709,8 @@ async function buildBootstrapAudit(opts = {}) {
12635
12709
  total_estimated_tokens: totalTokens,
12636
12710
  counts: { agents: agents.length, skills: skills.length, metadata_files: agents.length + skills.length },
12637
12711
  largest,
12712
+ largestTotal,
12713
+ largestTruncated: largest.length < largestTotal,
12638
12714
  diagnostics,
12639
12715
  budgets: { warn_tokens: warnTokens, fail_tokens: failTokens, warn_bytes: warnBytes, fail_bytes: failBytes, warnings, failures }
12640
12716
  };
@@ -12658,6 +12734,8 @@ async function runBootstrapAudit(opts = {}) {
12658
12734
  `);
12659
12735
  process.stdout.write("Largest metadata entries:\n");
12660
12736
  for (const entry of result.largest) process.stdout.write(` ${entry.metadata_bytes.toString().padStart(7)} bytes ${entry.path}
12737
+ `);
12738
+ if (result.largestTruncated) process.stdout.write(` ...and ${result.largestTotal - result.largest.length} more (raise --top to see them).
12661
12739
  `);
12662
12740
  for (const diagnostic of result.diagnostics) process.stderr.write(`token-goat: bootstrap-audit: skipped ${diagnostic.path} (${diagnostic.reason})
12663
12741
  `);
@@ -14387,15 +14465,14 @@ var RECALL_COMMAND = {
14387
14465
  function fenceTagForCacheType(cacheType) {
14388
14466
  return cacheType === "web" ? UNTRUSTED_WEB_TAG : UNTRUSTED_TOOL_TAG;
14389
14467
  }
14468
+ function fenceRecallListing(text, hits) {
14469
+ const tags = new Set(hits.map((hit) => fenceTagForCacheType(hit.cacheType)));
14470
+ const tag = tags.size === 1 ? [...tags][0] ?? UNTRUSTED_TOOL_TAG : UNTRUSTED_TOOL_TAG;
14471
+ return fenceUntrusted(text, tag);
14472
+ }
14390
14473
  function fenceSnippetIfMatched(hit) {
14391
- let matches2 = [];
14392
- try {
14393
- if (loadConfig().injection.enabled) matches2 = scanForInjectionPatterns(hit.snippet);
14394
- } catch {
14395
- matches2 = [];
14396
- }
14474
+ const matches2 = scanAndRecord(hit.snippet);
14397
14475
  if (matches2.length === 0) return hit.snippet;
14398
- recordStat("injection_detected", 0, 0, void 0, matches2.join(","));
14399
14476
  return fenceUntrustedContent(hit.snippet, matches2, fenceTagForCacheType(hit.cacheType));
14400
14477
  }
14401
14478
  function printHits(query, hits) {
@@ -14407,16 +14484,15 @@ function printHits(query, hits) {
14407
14484
  `);
14408
14485
  return;
14409
14486
  }
14410
- for (const hit of hits) {
14487
+ const body = hits.map((hit) => {
14411
14488
  const label = hit.label.length > 80 ? hit.label.slice(0, 77) + "..." : hit.label;
14412
- w(`[${pad(hit.cacheType, 4)}] ${hit.id} (token-goat ${RECALL_COMMAND[hit.cacheType]} ${hit.id})
14413
- `);
14414
- w(` ${label}
14415
- `);
14416
- w(` ${fenceSnippetIfMatched(hit)}
14417
-
14489
+ return `[${pad(hit.cacheType, 4)}] ${hit.id} (token-goat ${RECALL_COMMAND[hit.cacheType]} ${hit.id})
14490
+ ${label}
14491
+ ${hit.snippet}
14492
+ `;
14493
+ }).join("\n");
14494
+ w(`${fenceRecallListing(body, hits)}
14418
14495
  `);
14419
- }
14420
14496
  }
14421
14497
  function runRecallCommand(query, opts = {}) {
14422
14498
  const browse = query === void 0 || query.trim() === "";
@@ -14424,7 +14500,13 @@ function runRecallCommand(query, opts = {}) {
14424
14500
  ...opts.type !== void 0 ? { type: opts.type } : {},
14425
14501
  ...opts.limit !== void 0 ? { limit: opts.limit } : {}
14426
14502
  };
14427
- const hits = browse ? listRecentRecall(scope) : searchRecall(query, scope);
14503
+ const effectiveLimit = opts.limit ?? RECALL_DEFAULT_LIMIT;
14504
+ const fetched = browse ? listRecentRecall({ ...scope, limit: effectiveLimit + 1 }) : searchRecall(query, { ...scope, limit: effectiveLimit + 1 });
14505
+ const hits = fetched.slice(0, effectiveLimit);
14506
+ if (fetched.length > hits.length) {
14507
+ process.stderr.write(`Showing ${hits.length} entries; more are available (raise --limit to see them).
14508
+ `);
14509
+ }
14428
14510
  if (opts.json === true) {
14429
14511
  const fenced = hits.map((hit) => ({ ...hit, snippet: fenceSnippetIfMatched(hit) }));
14430
14512
  process.stdout.write(`${JSON.stringify(fenced)}
@@ -14681,6 +14763,7 @@ async function cmdSemantic(query, opts) {
14681
14763
  process.exitCode = code;
14682
14764
  }
14683
14765
  async function cmdIndex(pathArg, opts = {}) {
14766
+ applyIndexingPriority();
14684
14767
  const root = pathArg ?? process.cwd();
14685
14768
  const dbPath = opts.dbPath ?? globalDbPath();
14686
14769
  const force = opts.force === true;
@@ -14823,7 +14906,7 @@ async function cmdMcpServe() {
14823
14906
  let StdioServerTransport;
14824
14907
  try {
14825
14908
  ;
14826
- ({ createMcpServer } = await import("./token-goat-chunk-LZOAPGWR.mjs"));
14909
+ ({ createMcpServer } = await import("./token-goat-chunk-ZIPBLIUZ.mjs"));
14827
14910
  ({ StdioServerTransport } = await import("./token-goat-chunk-324QOJYZ.mjs"));
14828
14911
  } catch (err2) {
14829
14912
  process.stderr.write(
@@ -14848,7 +14931,7 @@ async function cmdHook(event, opts) {
14848
14931
  if (typeof opts.harness === "string" && opts.harness.length > 0) {
14849
14932
  process.env[ENV_KEYS.HARNESS_OVERRIDE] = opts.harness;
14850
14933
  }
14851
- const { relay } = await import("./token-goat-chunk-L6YP6FKQ.mjs");
14934
+ const { relay } = await import("./token-goat-chunk-LJEETTER.mjs");
14852
14935
  await relay(event);
14853
14936
  }
14854
14937
  async function cmdInstall(opts) {
@@ -15244,24 +15327,13 @@ function cmdHintStats(opts = {}) {
15244
15327
  ...opts.markIneffective !== void 0 && isHintCategory(opts.markIneffective) ? { markIneffective: opts.markIneffective } : {}
15245
15328
  });
15246
15329
  }
15247
- function _applyFiltersAndPrint(content, opts, fenceUntrusted = false, fenceTag = UNTRUSTED_WEB_TAG) {
15330
+ function _applyFiltersAndPrint(content, opts, fenceByProvenance = false, fenceTag = UNTRUSTED_WEB_TAG) {
15248
15331
  const emit2 = (text) => {
15249
- if (!fenceUntrusted || text === "") {
15250
- out(text);
15251
- return text;
15252
- }
15253
- let matches2 = [];
15254
- try {
15255
- if (loadConfig().injection.enabled) matches2 = scanForInjectionPatterns(text);
15256
- } catch {
15257
- matches2 = [];
15258
- }
15259
- if (matches2.length === 0) {
15332
+ if (!fenceByProvenance || text === "") {
15260
15333
  out(text);
15261
15334
  return text;
15262
15335
  }
15263
- recordStat("injection_detected", 0, 0, void 0, matches2.join(","));
15264
- const fenced = fenceUntrustedContent(text, matches2, fenceTag);
15336
+ const fenced = fenceUntrusted(text, fenceTag);
15265
15337
  out(fenced);
15266
15338
  return fenced;
15267
15339
  };
@@ -15299,6 +15371,11 @@ function _applyFiltersAndPrint(content, opts, fenceUntrusted = false, fenceTag =
15299
15371
  const headN = opts.head !== void 0 ? requireNonNegativeInt("--head", opts.head) : 30;
15300
15372
  const tailN = opts.tail !== void 0 ? requireNonNegativeInt("--tail", opts.tail) : 80;
15301
15373
  const applyElision = (lines2, headN2, tailN2) => lines2.length > headN2 + tailN2 + 1 ? [...lines2.slice(0, headN2), "...(elided)...", ...lines2.slice(lines2.length - tailN2)] : lines2;
15374
+ const noteLineCap = (which, flag, shown, total) => {
15375
+ if (shown >= total) return;
15376
+ process.stderr.write(`Showing ${which} ${shown} of ${total} lines (raise --${flag}, or --full for the whole body).
15377
+ `);
15378
+ };
15302
15379
  let result = lines;
15303
15380
  if (opts.head === void 0 && opts.tail === void 0) {
15304
15381
  result = applyElision(lines, headN, tailN);
@@ -15306,20 +15383,19 @@ function _applyFiltersAndPrint(content, opts, fenceUntrusted = false, fenceTag =
15306
15383
  result = applyElision(lines, headN, tailN);
15307
15384
  } else if (opts.head !== void 0) {
15308
15385
  result = lines.slice(0, headN);
15386
+ noteLineCap("first", "head", result.length, lines.length);
15309
15387
  } else if (opts.tail !== void 0) {
15310
15388
  result = lines.slice(Math.max(0, lines.length - tailN));
15389
+ noteLineCap("last", "tail", result.length, lines.length);
15311
15390
  }
15312
15391
  return emit2(result.join("\n"));
15313
15392
  }
15314
- function fenceFileTextIfMatched(text) {
15315
- let matches2 = [];
15316
- try {
15317
- if (loadConfig().injection.enabled) matches2 = scanForInjectionPatterns(text);
15318
- } catch {
15319
- matches2 = [];
15320
- }
15393
+ function fenceFileText(text) {
15394
+ return fenceUntrusted(text, UNTRUSTED_FILE_TAG);
15395
+ }
15396
+ function fenceFileFieldIfMatched(text) {
15397
+ const matches2 = scanAndRecord(text);
15321
15398
  if (matches2.length === 0) return text;
15322
- recordStat("injection_detected", 0, 0, void 0, matches2.join(","));
15323
15399
  return fenceUntrustedContent(text, matches2, UNTRUSTED_FILE_TAG);
15324
15400
  }
15325
15401
  function fileSizeOrZero(filePath) {
@@ -15398,20 +15474,22 @@ async function cmdPdfLocate(file, pattern, opts) {
15398
15474
  if (opts.context !== void 0) locateOpts.context = requirePositiveInt("--context", opts.context);
15399
15475
  if (opts.pages !== void 0) locateOpts.pages = opts.pages;
15400
15476
  const matches2 = await runPdfLocate(file, pattern, locateOpts);
15401
- const fencedMatches = matches2.map((m) => ({ ...m, snippet: fenceFileTextIfMatched(m.snippet) }));
15402
- const pages = fencedMatches.map((m) => m.page);
15477
+ const pages = matches2.map((m) => m.page);
15403
15478
  let printed;
15404
15479
  if (opts.json === true) {
15480
+ const fencedMatches = matches2.map((m) => ({ ...m, snippet: fenceFileFieldIfMatched(m.snippet) }));
15405
15481
  printed = JSON.stringify({ file, pattern, matchCount: fencedMatches.length, pages, matches: fencedMatches }, null, 2);
15406
15482
  out(printed);
15407
- } else if (fencedMatches.length === 0) {
15483
+ } else if (matches2.length === 0) {
15408
15484
  printed = "(no matches)";
15409
15485
  out(printed);
15410
15486
  } else {
15411
- const lines = fencedMatches.map((m) => `p${m.page}: ${m.snippet}`);
15412
- printed = `${lines.join("\n")}
15487
+ const lines = matches2.map((m) => `p${m.page}: ${m.snippet}`);
15488
+ printed = fenceFileText(
15489
+ `${lines.join("\n")}
15413
15490
 
15414
- ${countNoun(fencedMatches.length, "match", "matches")} across ${countNoun(pages.length, "page")}`;
15491
+ ${countNoun(matches2.length, "match", "matches")} across ${countNoun(pages.length, "page")}`
15492
+ );
15415
15493
  out(printed);
15416
15494
  }
15417
15495
  const fullSourceBytes = fileSizeOrZero(file);
@@ -15428,8 +15506,7 @@ async function cmdPdfOutline(file, opts) {
15428
15506
  }
15429
15507
  return;
15430
15508
  }
15431
- const fencedEntries = entries.map((e) => ({ ...e, title: fenceFileTextIfMatched(e.title) }));
15432
- const text = opts.json === true ? JSON.stringify(fencedEntries, null, 2) : fencedEntries.map((e) => `${" ".repeat(e.level)}${e.title}${e.page !== null ? ` (p.${e.page})` : ""}`).join("\n");
15509
+ const text = opts.json === true ? JSON.stringify(entries.map((e) => ({ ...e, title: fenceFileFieldIfMatched(e.title) })), null, 2) : fenceFileText(entries.map((e) => `${" ".repeat(e.level)}${e.title}${e.page !== null ? ` (p.${e.page})` : ""}`).join("\n"));
15433
15510
  out(text);
15434
15511
  const fullSourceBytes = fileSizeOrZero(file);
15435
15512
  const bytesSaved = Math.max(1, fullSourceBytes - Buffer.byteLength(text, "utf8"));
@@ -15472,13 +15549,8 @@ ${msg}`;
15472
15549
  recordStat("image_meta", bytesSaved, savedTokensFromBytes(bytesSaved));
15473
15550
  }
15474
15551
  function fenceOcrText(text) {
15475
- try {
15476
- if (loadConfig().injection.enabled) {
15477
- const matches2 = scanForInjectionPatterns(text);
15478
- if (matches2.length > 0) recordStat("injection_detected", 0, 0, void 0, matches2.join(","));
15479
- }
15480
- } catch {
15481
- }
15552
+ if (!injectionFencingEnabled()) return text;
15553
+ scanAndRecord(text);
15482
15554
  return fenceUntrustedOcrText(text);
15483
15555
  }
15484
15556
  async function cmdImageText(file, opts = {}) {
@@ -15563,20 +15635,19 @@ function recordXlsxStat(kind, file, emitted) {
15563
15635
  }
15564
15636
  async function cmdXlsxSheets(file, opts = {}) {
15565
15637
  const sheets = await listSheets(file);
15566
- const fencedSheets = sheets.map((s) => ({ ...s, name: fenceFileTextIfMatched(s.name) }));
15567
- const text = opts.json === true ? JSON.stringify(fencedSheets.map((s) => ({ name: s.name, ref: s.ref, rows: s.rows, cols: s.cols })), null, 2) : fencedSheets.map((s) => `${s.name} ${s.ref} (${s.rows} rows x ${s.cols} cols)`).join("\n");
15638
+ const text = opts.json === true ? JSON.stringify(sheets.map((s) => ({ name: fenceFileFieldIfMatched(s.name), ref: s.ref, rows: s.rows, cols: s.cols })), null, 2) : fenceFileText(sheets.map((s) => `${s.name} ${s.ref} (${s.rows} rows x ${s.cols} cols)`).join("\n"));
15568
15639
  out(text);
15569
15640
  recordXlsxStat("xlsx_sheets", file, text);
15570
15641
  }
15571
15642
  async function cmdXlsxHead(file, opts) {
15572
15643
  const rows = opts.rows !== void 0 ? requireNonNegativeInt("--rows", opts.rows) : 20;
15573
- const text = fenceFileTextIfMatched(await headSheet(file, opts.sheet, rows));
15644
+ const text = fenceFileText(await headSheet(file, opts.sheet, rows));
15574
15645
  out(text);
15575
15646
  recordXlsxStat("xlsx_head", file, text);
15576
15647
  }
15577
15648
  async function cmdXlsxRange(file, opts) {
15578
15649
  const result = await rangeSheet(file, opts.sheet, opts.range, opts.formulas === true);
15579
- const text = fenceFileTextIfMatched(formatXlsxRange(result));
15650
+ const text = fenceFileText(formatXlsxRange(result));
15580
15651
  out(text);
15581
15652
  recordXlsxStat("xlsx_range", file, text);
15582
15653
  }
@@ -15588,7 +15659,7 @@ async function cmdXlsxQuery(file, opts) {
15588
15659
  ...wheres !== void 0 ? { wheres } : {},
15589
15660
  ...opts.head !== void 0 ? { head: requireNonNegativeInt("--head", opts.head) } : {}
15590
15661
  });
15591
- const text = fenceFileTextIfMatched(formatCsvTable(result, (opts.where ?? []).map((w) => `--where ${w}`)));
15662
+ const text = fenceFileText(formatCsvTable(result, (opts.where ?? []).map((w) => `--where ${w}`)));
15592
15663
  out(text);
15593
15664
  recordXlsxStat("xlsx_query", file, text);
15594
15665
  }
@@ -15599,21 +15670,22 @@ function recordDocStat(kind, file, emitted) {
15599
15670
  }
15600
15671
  async function cmdPptxOutline(file, opts) {
15601
15672
  const slides = await pptxOutline(file);
15602
- const fencedSlides = slides.map((s) => ({ ...s, title: fenceFileTextIfMatched(s.title) }));
15603
- const text = opts.json === true ? JSON.stringify(fencedSlides, null, 2) : fencedSlides.map((s) => `${s.slide}. ${s.title || "(untitled)"} [${s.bodyChars} body chars${s.hasNotes ? ", has notes" : ""}]`).join("\n");
15673
+ const text = opts.json === true ? JSON.stringify(slides.map((s) => ({ ...s, title: fenceFileFieldIfMatched(s.title) })), null, 2) : fenceFileText(
15674
+ slides.map((s) => `${s.slide}. ${s.title || "(untitled)"} [${s.bodyChars} body chars${s.hasNotes ? ", has notes" : ""}]`).join("\n")
15675
+ );
15604
15676
  out(text);
15605
15677
  recordDocStat("pptx_outline", file, text);
15606
15678
  }
15607
15679
  async function cmdPptxSlide(file, opts) {
15608
15680
  const n = requireNonNegativeInt("--slide", opts.slide);
15609
- const text = fenceFileTextIfMatched(await pptxSlideText(file, n, opts.notes === true));
15681
+ const text = fenceFileText(await pptxSlideText(file, n, opts.notes === true));
15610
15682
  out(text);
15611
15683
  recordDocStat("pptx_slide", file, text);
15612
15684
  }
15613
15685
  async function cmdPptxNotes(file, opts) {
15614
15686
  const n = opts.slide !== void 0 ? requireNonNegativeInt("--slide", opts.slide) : void 0;
15615
15687
  const text = await pptxNotesText(file, n);
15616
- const printed = text.length > 0 ? fenceFileTextIfMatched(text) : "no speaker notes found";
15688
+ const printed = text.length > 0 ? fenceFileText(text) : "no speaker notes found";
15617
15689
  out(printed);
15618
15690
  recordDocStat("pptx_notes", file, printed);
15619
15691
  }
@@ -15623,7 +15695,7 @@ async function cmdPptxText(file, opts) {
15623
15695
  out("no matches");
15624
15696
  return;
15625
15697
  }
15626
- const text = fenceFileTextIfMatched(matches2.map((m) => `Slide ${m.slide}: ...${m.snippet}...`).join("\n"));
15698
+ const text = fenceFileText(matches2.map((m) => `Slide ${m.slide}: ...${m.snippet}...`).join("\n"));
15627
15699
  out(text);
15628
15700
  recordDocStat("pptx_text", file, text);
15629
15701
  }
@@ -15637,8 +15709,7 @@ async function cmdDocxOutline(file, opts) {
15637
15709
  }
15638
15710
  return;
15639
15711
  }
15640
- const fencedHeadings = headings.map((h) => ({ ...h, text: fenceFileTextIfMatched(h.text) }));
15641
- const text = opts.json === true ? JSON.stringify(fencedHeadings, null, 2) : fencedHeadings.map((h) => `${" ".repeat(h.level - 1)}${h.text}`).join("\n");
15712
+ const text = opts.json === true ? JSON.stringify(headings.map((h) => ({ ...h, text: fenceFileFieldIfMatched(h.text) })), null, 2) : fenceFileText(headings.map((h) => `${" ".repeat(h.level - 1)}${h.text}`).join("\n"));
15642
15713
  out(text);
15643
15714
  recordDocStat("docx_outline", file, text);
15644
15715
  }
@@ -15782,7 +15853,7 @@ function emitExtraFileArgsNote(command, first, extras, opts = {}) {
15782
15853
  }
15783
15854
  async function cmdCompress(opts) {
15784
15855
  try {
15785
- const bashRunner = await import("./token-goat-chunk-NZFTWBGV.mjs");
15856
+ const bashRunner = await import("./token-goat-chunk-L2XHDICZ.mjs");
15786
15857
  if (opts.compress === false) {
15787
15858
  process.exitCode = bashRunner.runRaw(opts.cmd, parseTimeout(opts.timeout, bashRunner.DEFAULT_TIMEOUT_SECONDS));
15788
15859
  return;
@@ -16710,17 +16781,7 @@ ${content}`;
16710
16781
  const sections = await getDocSections(fileId, { fresh: false });
16711
16782
  emitted = formatSections(sections);
16712
16783
  }
16713
- let toEmit = emitted;
16714
- try {
16715
- if (loadConfig().injection.enabled) {
16716
- const matches2 = scanForInjectionPatterns(emitted);
16717
- if (matches2.length > 0) {
16718
- recordStat("injection_detected", 0, 0, void 0, matches2.join(","));
16719
- toEmit = fenceUntrustedContent(emitted, matches2, UNTRUSTED_WEB_TAG);
16720
- }
16721
- }
16722
- } catch {
16723
- }
16784
+ const toEmit = fenceUntrusted(emitted, UNTRUSTED_WEB_TAG);
16724
16785
  out(toEmit);
16725
16786
  const bytesSaved = Math.max(1, fullSourceBytes - Buffer.byteLength(toEmit, "utf8"));
16726
16787
  recordStat("gdrive_sections", bytesSaved, savedTokensFromBytes(bytesSaved));
@@ -16803,9 +16864,11 @@ function cmdTokens(patterns, opts) {
16803
16864
  const result = estimateBudget(root, expandGlobs(root, patterns ?? []));
16804
16865
  let entries = [...result.entries];
16805
16866
  if (opts.asc === true) entries.reverse();
16867
+ const eligibleCount = entries.length;
16806
16868
  if (opts.top !== void 0) entries = entries.slice(0, requireNonNegativeInt("--top", opts.top));
16869
+ const truncated = entries.length < eligibleCount;
16807
16870
  if (opts.json === true) {
16808
- out(JSON.stringify({ entries, total_tokens: result.total_tokens, total_lines: result.total_lines }, null, 2));
16871
+ out(JSON.stringify({ entries, truncated, totalCount: eligibleCount, total_tokens: result.total_tokens, total_lines: result.total_lines }, null, 2));
16809
16872
  return;
16810
16873
  }
16811
16874
  if (opts.tree === true) {
@@ -16839,6 +16902,9 @@ function cmdTokens(patterns, opts) {
16839
16902
  for (const e of entries) {
16840
16903
  lines.push(`${e.rel_path.padEnd(colW)} ${String(e.tokens).padStart(8)} ${String(e.lines).padStart(6)}`);
16841
16904
  }
16905
+ if (truncated) {
16906
+ lines.push(`...and ${eligibleCount - entries.length} more (raise --top to see them).`);
16907
+ }
16842
16908
  out(lines.join("\n"));
16843
16909
  }
16844
16910
  function cmdBudget(patterns, opts) {