token-goat 2.6.35 → 2.6.36

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.
Files changed (26) hide show
  1. package/README.md +4 -2
  2. package/SECURITY.md +3 -1
  3. package/dist/{token-goat-chunk-DRAIYVUI.mjs → token-goat-chunk-65BKISIS.mjs} +3 -3
  4. package/dist/{token-goat-chunk-UIZVX3HN.mjs → token-goat-chunk-CNDOJ3ZP.mjs} +2 -2
  5. package/dist/{token-goat-chunk-Y2NH6DGZ.mjs → token-goat-chunk-FRTBMRP7.mjs} +5 -5
  6. package/dist/{token-goat-chunk-D5IZEGN2.mjs → token-goat-chunk-IYTVE6KN.mjs} +13 -8
  7. package/dist/{token-goat-chunk-HW4VUKJ5.mjs → token-goat-chunk-KYFJC37X.mjs} +14 -13
  8. package/dist/{token-goat-chunk-YF67EWRH.mjs → token-goat-chunk-LN6OUHTV.mjs} +5 -5
  9. package/dist/{token-goat-hook-chunk-NIPXIAPA.mjs → token-goat-chunk-MGOUYAA2.mjs} +1 -1
  10. package/dist/{token-goat-chunk-I2KCEGIP.mjs → token-goat-chunk-UFOVM7ZN.mjs} +85 -45
  11. package/dist/{token-goat-chunk-K7W3P2TJ.mjs → token-goat-chunk-V465YKOR.mjs} +61 -17
  12. package/dist/{token-goat-chunk-SIHYQCTM.mjs → token-goat-chunk-VYMGEVZS.mjs} +16 -4
  13. package/dist/{token-goat-chunk-DQIT5JIF.mjs → token-goat-hook-chunk-3ZDBWJDF.mjs} +1 -1
  14. package/dist/{token-goat-hook-chunk-CRBMPT74.mjs → token-goat-hook-chunk-5UH54CW6.mjs} +16 -4
  15. package/dist/{token-goat-hook-chunk-VU6VGZBM.mjs → token-goat-hook-chunk-6ODM3MP7.mjs} +3 -3
  16. package/dist/{token-goat-hook-chunk-XM4W3WCO.mjs → token-goat-hook-chunk-A77A26A7.mjs} +85 -45
  17. package/dist/{token-goat-hook-chunk-4POU6OHK.mjs → token-goat-hook-chunk-BDR6C6IE.mjs} +5 -5
  18. package/dist/{token-goat-hook-chunk-3AAH72SO.mjs → token-goat-hook-chunk-C6GIABOX.mjs} +14 -13
  19. package/dist/{token-goat-hook-chunk-D6UJEFHX.mjs → token-goat-hook-chunk-E257IGSN.mjs} +2 -2
  20. package/dist/{token-goat-hook-chunk-NXVT4F3S.mjs → token-goat-hook-chunk-MW5HPEGD.mjs} +13 -8
  21. package/dist/{token-goat-hook-chunk-J4HKWUWZ.mjs → token-goat-hook-chunk-QSCYNJ2B.mjs} +5 -5
  22. package/dist/{token-goat-hook-chunk-2O7P4Z6Q.mjs → token-goat-hook-chunk-Y2WHX2P3.mjs} +61 -17
  23. package/dist/token-goat-hook.mjs +5 -5
  24. package/dist/token-goat.core.mjs +5 -5
  25. package/package.json +10 -9
  26. package/scripts/install-git-hooks.mjs +55 -0
@@ -56,6 +56,7 @@ import {
56
56
  parse,
57
57
  precedingDocComment,
58
58
  propagateEndLinesToSymbols,
59
+ pushAll,
59
60
  recordStat,
60
61
  redactIfDotenv,
61
62
  redactSecrets,
@@ -88,7 +89,7 @@ import {
88
89
  withFileLock,
89
90
  writeIfDifferent,
90
91
  writeJsonSettings
91
- } from "./token-goat-chunk-K7W3P2TJ.mjs";
92
+ } from "./token-goat-chunk-V465YKOR.mjs";
92
93
  import {
93
94
  registerReset
94
95
  } from "./token-goat-chunk-AO2QD2AG.mjs";
@@ -211,7 +212,7 @@ function collectElements(node, tag) {
211
212
  const obj = n;
212
213
  for (const [key, val] of Object.entries(obj)) {
213
214
  if (key === tag) {
214
- if (Array.isArray(val)) out.push(...val);
215
+ if (Array.isArray(val)) pushAll(out, val);
215
216
  else out.push(val);
216
217
  } else if (val !== null && typeof val === "object") {
217
218
  walk(val);
@@ -2488,7 +2489,10 @@ function makeWorksheet(name, data) {
2488
2489
  },
2489
2490
  eachCell(opts, cb) {
2490
2491
  if (opts.includeEmpty) {
2491
- const maxCol = data.columnCount;
2492
+ let maxCol = 0;
2493
+ if (rowCells !== void 0) {
2494
+ for (const c of rowCells.keys()) if (c > maxCol) maxCol = c;
2495
+ }
2492
2496
  for (let c = 1; c <= maxCol; c++) cb(rowCells?.get(c) ?? EMPTY_CELL, c);
2493
2497
  return;
2494
2498
  }
@@ -5761,6 +5765,7 @@ function uninstallVscode(opts = {}) {
5761
5765
  }
5762
5766
 
5763
5767
  // src/session_store.ts
5768
+ import { createHash as createHash2 } from "node:crypto";
5764
5769
  import * as fs13 from "node:fs";
5765
5770
  import * as path10 from "node:path";
5766
5771
 
@@ -5987,9 +5992,22 @@ function sweepCacheRoots(extraRoots = []) {
5987
5992
  var MAX_FILES = 500;
5988
5993
  var SESSIONS_SUBDIR = "sessions";
5989
5994
  var AGENT_SALT_MARKER = sanitizeIdForFilename(":agent:");
5995
+ var AGENT_SALT_SEPARATOR = ":agent:";
5996
+ var AGENT_DIGEST_CHARS = 12;
5997
+ var SALTED_SESSION_MAX = 64 - AGENT_SALT_MARKER.length - AGENT_DIGEST_CHARS;
5998
+ function saltedStemPrefix(sessionId) {
5999
+ return `${sanitizeIdForFilename(sessionId, SALTED_SESSION_MAX)}${AGENT_SALT_MARKER}`;
6000
+ }
6001
+ function sessionFileStem(sessionId) {
6002
+ const sep = sessionId.indexOf(AGENT_SALT_SEPARATOR);
6003
+ if (sep < 0) return sanitizeIdForFilename(sessionId, 64);
6004
+ const agentId = sessionId.slice(sep + AGENT_SALT_SEPARATOR.length);
6005
+ const digest = createHash2("sha256").update(agentId).digest("hex").slice(0, AGENT_DIGEST_CHARS);
6006
+ return `${saltedStemPrefix(sessionId.slice(0, sep))}${digest}`;
6007
+ }
5990
6008
  function sessionPath(sessionId) {
5991
6009
  if (!sessionId) return null;
5992
- const safe = sanitizeIdForFilename(sessionId, 64);
6010
+ const safe = sessionFileStem(sessionId);
5993
6011
  if (!safe) return null;
5994
6012
  const dir = path10.join(tokenGoatHome(), SESSIONS_SUBDIR);
5995
6013
  const candidate = path10.join(dir, `${safe}.json`);
@@ -6241,9 +6259,8 @@ function readSessionStateFile(sessionId) {
6241
6259
  }
6242
6260
  function listSiblingSessionStates(sessionId) {
6243
6261
  if (!sessionId) return [];
6244
- const safeSessionId = sanitizeIdForFilename(sessionId);
6245
- if (!safeSessionId) return [];
6246
- const prefix = `${safeSessionId}${AGENT_SALT_MARKER}`;
6262
+ const prefix = saltedStemPrefix(sessionId);
6263
+ if (prefix === AGENT_SALT_MARKER) return [];
6247
6264
  const dir = path10.join(tokenGoatHome(), SESSIONS_SUBDIR);
6248
6265
  const out = [];
6249
6266
  try {
@@ -6307,7 +6324,7 @@ function scanForInjectionPatterns(text) {
6307
6324
  var UNTRUSTED_WEB_TAG = "untrusted-web-content";
6308
6325
  function neutralizeFenceMarkers(text, tag) {
6309
6326
  const escapedTag = tag.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6310
- const marker = new RegExp(`<\\s*/?\\s*${escapedTag}(?:\\s[^>]*)?\\s*/?\\s*>`, "gi");
6327
+ const marker = new RegExp(`<\\s*/?\\s*${escapedTag}(?=[\\s/>])[^>]*>`, "gi");
6311
6328
  return text.replace(marker, (m) => m.replace(/</g, "&lt;").replace(/>/g, "&gt;"));
6312
6329
  }
6313
6330
  function fenceUntrustedContent(text, matchedPatternNames, tag = UNTRUSTED_WEB_TAG) {
@@ -6905,7 +6922,7 @@ function pruneSkillOutputs(maxCount = DEFAULT_MAX_COUNT, maxAgeMs = DEFAULT_MAX_
6905
6922
  }
6906
6923
 
6907
6924
  // src/image_shrink.ts
6908
- import { createHash as createHash2 } from "node:crypto";
6925
+ import { createHash as createHash3 } from "node:crypto";
6909
6926
  import * as fs16 from "node:fs";
6910
6927
  import * as path12 from "node:path";
6911
6928
 
@@ -7107,7 +7124,7 @@ function imageShrinkCacheDir() {
7107
7124
  return path12.join(tokenGoatHome(), "image_shrink_cache");
7108
7125
  }
7109
7126
  function shrinkCacheKey(originalPath, size, mtimeMs, quality) {
7110
- return createHash2("sha256").update(`${originalPath}:${size}:${mtimeMs}:${quality}`).digest("hex").slice(0, 16);
7127
+ return createHash3("sha256").update(`${originalPath}:${size}:${mtimeMs}:${quality}`).digest("hex").slice(0, 16);
7111
7128
  }
7112
7129
  function findCachedShrink(originalPath, size, mtimeMs, quality) {
7113
7130
  const key = shrinkCacheKey(originalPath, size, mtimeMs, quality);
@@ -7156,18 +7173,18 @@ function pruneShrinkCache() {
7156
7173
  }
7157
7174
  async function finalizeShrinkResult(result, filePath) {
7158
7175
  const basename12 = path12.basename(filePath);
7176
+ const shrinkSaved = result.originalBytes - result.shrunkBytes;
7177
+ recordStat("image_shrink", shrinkSaved, Math.round(shrinkSaved / 4), void 0, basename12);
7159
7178
  if (loadConfig().image_shrink.ocr_enabled) {
7160
7179
  const ocr = await ocrImage(result.data);
7161
7180
  if (ocr !== null && isTextHeavy(ocr, loadConfig().image_shrink.ocr_min_confidence)) {
7162
7181
  const textBytes = Buffer.byteLength(ocr.text, "utf8");
7163
- const saved2 = Math.max(0, result.shrunkBytes - textBytes);
7164
- recordStat("image_ocr", saved2, Math.round(saved2 / 4), void 0, basename12);
7182
+ const saved = Math.max(0, result.shrunkBytes - textBytes);
7183
+ recordStat("image_ocr", saved, Math.round(saved / 4), void 0, basename12);
7165
7184
  return contextOutput(formatOcrSummary(ocr, basename12, result.originalBytes));
7166
7185
  }
7167
7186
  }
7168
- const saved = result.originalBytes - result.shrunkBytes;
7169
7187
  const { summary, dataUrl } = formatShrinkSummary(result, basename12);
7170
- recordStat("image_shrink", saved, Math.round(saved / 4), void 0, basename12);
7171
7188
  return contextOutput(`${summary}
7172
7189
  ${dataUrl}`);
7173
7190
  }
@@ -7423,7 +7440,8 @@ function splitRangeIntoChunks(filePath, lines2, rangeStart, rangeEnd, chunkSize,
7423
7440
  const line = lines2[lineNo - 1] ?? "";
7424
7441
  const lineWithNewline = line + "\n";
7425
7442
  if (currentChunk.length + lineWithNewline.length > chunkSize && currentChunk.length > 0) {
7426
- const currentChunkTooSmall = currentChunk.trim().length < MIN_CHUNK_CHARS;
7443
+ const trimmedLength = currentChunk.trim().length;
7444
+ const currentChunkTooSmall = trimmedLength < MIN_CHUNK_CHARS;
7427
7445
  if (!currentChunkTooSmall) {
7428
7446
  chunks.push({
7429
7447
  filePath,
@@ -7433,12 +7451,21 @@ function splitRangeIntoChunks(filePath, lines2, rangeStart, rangeEnd, chunkSize,
7433
7451
  kind
7434
7452
  });
7435
7453
  }
7436
- const overlapLines = Math.ceil(overlap / 40);
7437
- const computedOverlapStart = Math.max(rangeStart, currentLine - overlapLines);
7438
- const overlapStart = currentChunkTooSmall ? Math.min(computedOverlapStart, startLine) : computedOverlapStart;
7439
- const overlapText = lines2.slice(overlapStart - 1, currentLine - 1).join("\n");
7440
- currentChunk = overlapText + "\n";
7441
- startLine = overlapStart;
7454
+ let overlapChars = 0;
7455
+ let computedOverlapStart = currentLine;
7456
+ while (computedOverlapStart > rangeStart) {
7457
+ const candidateChars = (lines2[computedOverlapStart - 2] ?? "").length + 1;
7458
+ if (overlapChars + candidateChars > overlap) break;
7459
+ overlapChars += candidateChars;
7460
+ computedOverlapStart--;
7461
+ }
7462
+ const droppedChunkHadContent = currentChunkTooSmall && trimmedLength > 0;
7463
+ const overlapStart = droppedChunkHadContent ? Math.min(computedOverlapStart, startLine) : computedOverlapStart;
7464
+ if (overlapStart !== startLine) {
7465
+ const overlapText = lines2.slice(overlapStart - 1, currentLine - 1).join("\n");
7466
+ currentChunk = overlapText + "\n";
7467
+ startLine = overlapStart;
7468
+ }
7442
7469
  }
7443
7470
  currentChunk += lineWithNewline;
7444
7471
  currentLine++;
@@ -7587,7 +7614,7 @@ async function upsertChunks(db, chunks) {
7587
7614
  insertChunkVector(vectorInsertStmt, chunkResult.lastInsertRowid, embedding);
7588
7615
  }
7589
7616
  });
7590
- tx();
7617
+ tx.immediate();
7591
7618
  return "embedded";
7592
7619
  }
7593
7620
  var BACKFILL_MULTIPLIER = 3;
@@ -12862,7 +12889,7 @@ function bracedBodyEndLine(content, lineIndex, parenIndex, totalLines, fallback)
12862
12889
  j++;
12863
12890
  }
12864
12891
  if (content[j] !== "{") return fallback;
12865
- return findMatchingBraceEndLine(content, j, totalLines, lineIndex, "#");
12892
+ return findMatchingBraceEndLine(content, j, totalLines, lineIndex, "#", { backtickQuote: true });
12866
12893
  }
12867
12894
  function callEndLine(content, lineIndex, parenIndex, fallback) {
12868
12895
  const close = matchingParenIndex(content, parenIndex);
@@ -14747,21 +14774,22 @@ function maskSpans(content, spans) {
14747
14774
  }
14748
14775
  return chars.join("");
14749
14776
  }
14750
- function componentSymbol(filePath, name, kind, totalLines) {
14751
- return { filePath, name, kind, lineStart: 1, lineEnd: totalLines, body: "", docstring: "", parent: "" };
14777
+ function componentSymbols(filePath, name, kind, totalLines) {
14778
+ if (totalLines < 1) return [];
14779
+ return [{ filePath, name, kind, lineStart: 1, lineEnd: totalLines, body: "", docstring: "", parent: "" }];
14752
14780
  }
14753
14781
  function extractVue(content, filePath) {
14754
14782
  const totalLines = countContentLines(content);
14755
14783
  const lineIndex = buildLineIndex(content);
14756
14784
  const name = componentName(filePath);
14757
- const symbols = [componentSymbol(filePath, name, "vue_component", totalLines)];
14785
+ const symbols = componentSymbols(filePath, name, "vue_component", totalLines);
14758
14786
  const refs = [];
14759
14787
  for (const block of extractTagBlocks(content, lineIndex, "script")) {
14760
- symbols.push(...extractTopLevelDeclarations(block.content, filePath, block.contentStartLine));
14788
+ pushAll(symbols, extractTopLevelDeclarations(block.content, filePath, block.contentStartLine));
14761
14789
  }
14762
14790
  for (const block of extractTagBlocks(content, lineIndex, "template")) {
14763
14791
  const markup = stripXmlComments(block.content);
14764
- refs.push(...extractComponentRefs(markup, filePath, block.contentStartLine, true));
14792
+ pushAll(refs, extractComponentRefs(markup, filePath, block.contentStartLine, true));
14765
14793
  }
14766
14794
  return finalize(symbols, refs);
14767
14795
  }
@@ -14769,11 +14797,11 @@ function extractSvelte(content, filePath) {
14769
14797
  const totalLines = countContentLines(content);
14770
14798
  const lineIndex = buildLineIndex(content);
14771
14799
  const name = componentName(filePath);
14772
- const symbols = [componentSymbol(filePath, name, "svelte_component", totalLines)];
14800
+ const symbols = componentSymbols(filePath, name, "svelte_component", totalLines);
14773
14801
  const refs = [];
14774
14802
  const scriptBlocks = extractTagBlocks(content, lineIndex, "script");
14775
14803
  for (const block of scriptBlocks) {
14776
- symbols.push(...extractTopLevelDeclarations(block.content, filePath, block.contentStartLine));
14804
+ pushAll(symbols, extractTopLevelDeclarations(block.content, filePath, block.contentStartLine));
14777
14805
  }
14778
14806
  const styleBlocks = extractTagBlocks(content, lineIndex, "style");
14779
14807
  const spans = [...scriptBlocks, ...styleBlocks].map((b) => [
@@ -14781,7 +14809,7 @@ function extractSvelte(content, filePath) {
14781
14809
  b.matchEnd
14782
14810
  ]);
14783
14811
  const markup = stripXmlComments(maskSpans(content, spans));
14784
- refs.push(...extractComponentRefs(markup, filePath, 1, true));
14812
+ pushAll(refs, extractComponentRefs(markup, filePath, 1, true));
14785
14813
  return finalize(symbols, refs);
14786
14814
  }
14787
14815
  function detectAstroFrontmatter(content) {
@@ -14802,7 +14830,7 @@ function extractAstro(content, filePath) {
14802
14830
  const totalLines = countContentLines(content);
14803
14831
  const lineIndex = buildLineIndex(content);
14804
14832
  const name = componentName(filePath);
14805
- const symbols = [componentSymbol(filePath, name, "astro_component", totalLines)];
14833
+ const symbols = componentSymbols(filePath, name, "astro_component", totalLines);
14806
14834
  const refs = [];
14807
14835
  const lines2 = content.split("\n");
14808
14836
  const fm = detectAstroFrontmatter(content);
@@ -14810,7 +14838,7 @@ function extractAstro(content, filePath) {
14810
14838
  if (fm) {
14811
14839
  const frontmatterContent = lines2.slice(fm.openLine + 1, fm.closeLine).join("\n");
14812
14840
  const contentStartLine = fm.openLine + 2;
14813
- symbols.push(...extractTopLevelDeclarations(frontmatterContent, filePath, contentStartLine));
14841
+ pushAll(symbols, extractTopLevelDeclarations(frontmatterContent, filePath, contentStartLine));
14814
14842
  const fenceStartOffset = lineIndex[fm.openLine] ?? 0;
14815
14843
  const fenceEndOffset = lineIndex[fm.closeLine + 1] ?? content.length;
14816
14844
  spans.push([fenceStartOffset, fenceEndOffset]);
@@ -14818,7 +14846,7 @@ function extractAstro(content, filePath) {
14818
14846
  const styleBlocks = extractTagBlocks(content, lineIndex, "style");
14819
14847
  for (const block of styleBlocks) spans.push([block.matchStart, block.matchEnd]);
14820
14848
  const markup = stripXmlComments(maskSpans(content, spans));
14821
- refs.push(...extractComponentRefs(markup, filePath, 1, false));
14849
+ pushAll(refs, extractComponentRefs(markup, filePath, 1, false));
14822
14850
  return finalize(symbols, refs);
14823
14851
  }
14824
14852
 
@@ -16668,7 +16696,7 @@ function writeParseResult(filePath, content, result, dbPath) {
16668
16696
  insRef.run(r.filePath, r.name, r.line, r.col, r.context);
16669
16697
  }
16670
16698
  });
16671
- writeAll();
16699
+ writeAll.immediate();
16672
16700
  }
16673
16701
  function indexFileSync(filePath, dbPath = globalDbPath(), preReadBytes) {
16674
16702
  const ixCfg = loadConfig().indexing;
@@ -16848,11 +16876,13 @@ function removeFileFromIndex(db, filePath) {
16848
16876
  deleteFileRows(db, filePath);
16849
16877
  deleteFileEmbeddings(db, filePath);
16850
16878
  });
16851
- tx();
16879
+ tx.immediate();
16852
16880
  }
16853
16881
  function isTooShallowToPrune(rootPrefix) {
16854
- const segments = rootPrefix.split("/").filter((s) => s.length > 0 && !/^[a-z]:$/i.test(s));
16882
+ const normalized = normalizePath(rootPrefix);
16883
+ const segments = normalized.split("/").filter((s) => s.length > 0 && !/^[a-z]:$/i.test(s));
16855
16884
  if (segments.length === 2 && segments[0]?.toLowerCase() === "mnt" && /^[a-z]$/i.test(segments[1] ?? "")) return true;
16885
+ if (normalized.startsWith("//") && segments.length <= 2) return true;
16856
16886
  return segments.length === 0;
16857
16887
  }
16858
16888
  function foldedBounds(rootPrefix) {
@@ -16877,7 +16907,8 @@ function findDeletablePaths(rootPrefix, dbPath) {
16877
16907
  for (const p of foldedPathsUnderRoot(rootPrefix, dbPath)) {
16878
16908
  let stillExists;
16879
16909
  try {
16880
- stillExists = fs27.statSync(p, { throwIfNoEntry: false }) !== void 0;
16910
+ const st = fs27.statSync(p, { throwIfNoEntry: false });
16911
+ stillExists = st !== void 0 && st.isFile();
16881
16912
  } catch {
16882
16913
  continue;
16883
16914
  }
@@ -16900,14 +16931,23 @@ function removeFilesBestEffort(db, paths) {
16900
16931
  return removed;
16901
16932
  }
16902
16933
  function removeDeletedFilesBestEffort(db, paths) {
16903
- const stillGone = paths.filter((p) => {
16934
+ const removed = [];
16935
+ for (const p of paths) {
16936
+ let gone;
16904
16937
  try {
16905
- return fs27.statSync(p, { throwIfNoEntry: false }) === void 0;
16938
+ const st = fs27.statSync(p, { throwIfNoEntry: false });
16939
+ gone = st === void 0 || !st.isFile();
16906
16940
  } catch {
16907
- return false;
16941
+ continue;
16908
16942
  }
16909
- });
16910
- return removeFilesBestEffort(db, stillGone);
16943
+ if (!gone) continue;
16944
+ try {
16945
+ removeFileFromIndex(db, p);
16946
+ removed.push(p);
16947
+ } catch {
16948
+ }
16949
+ }
16950
+ return removed;
16911
16951
  }
16912
16952
  function pruneDeletedFiles(rootPrefix, dbPath = globalDbPath()) {
16913
16953
  if (isTooShallowToPrune(rootPrefix)) return 0;
@@ -16985,7 +17025,7 @@ function sweepKnownRoots(dbPath = globalDbPath(), opts) {
16985
17025
  if (isTooShallowToPrune(root)) continue;
16986
17026
  let reachable;
16987
17027
  try {
16988
- reachable = fs27.existsSync(root);
17028
+ reachable = fs27.statSync(root, { throwIfNoEntry: false })?.isDirectory() === true;
16989
17029
  } catch {
16990
17030
  reachable = false;
16991
17031
  }
@@ -17114,7 +17154,7 @@ function bumpRetryCount(dbPath, absPath) {
17114
17154
  db.prepare("INSERT INTO files (path, retry_count) VALUES (?, 1)").run(normalized);
17115
17155
  return 1;
17116
17156
  });
17117
- return tx();
17157
+ return tx.immediate();
17118
17158
  }
17119
17159
  function clearRetryCount(dbPath, absPath) {
17120
17160
  try {
@@ -8,7 +8,7 @@ import {
8
8
  import { createRequire } from "node:module";
9
9
  function resolveVersion() {
10
10
  if (true) {
11
- return "2.6.35";
11
+ return "2.6.36";
12
12
  }
13
13
  const require2 = createRequire(import.meta.url);
14
14
  const pkg = require2("../package.json");
@@ -770,6 +770,9 @@ function countContentLines(content) {
770
770
  const n = content.split("\n").length;
771
771
  return content.endsWith("\n") ? n - 1 : n;
772
772
  }
773
+ function pushAll(target, items) {
774
+ for (const item of items) target.push(item);
775
+ }
773
776
  function escapeRegExp(s) {
774
777
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
775
778
  }
@@ -2449,7 +2452,6 @@ var ENV_KEYS2 = [
2449
2452
  "TOKEN_GOAT_OVERFLOW_MAX_TOKENS",
2450
2453
  "TOKEN_GOAT_HINT_JSON_SIDECAR",
2451
2454
  "TOKEN_GOAT_LARGE_READ_BYTES",
2452
- "TOKEN_GOAT_CURATOR",
2453
2455
  "TOKEN_GOAT_HINT_BUDGET",
2454
2456
  "TOKEN_GOAT_HOOK_WATCHDOG_MS",
2455
2457
  "TOKEN_GOAT_WEB_COMPRESS",
@@ -2510,8 +2512,19 @@ var _lastProjectConfigParseError = null;
2510
2512
  function getLastProjectConfigParseError() {
2511
2513
  return _lastProjectConfigParseError;
2512
2514
  }
2513
- var PROJECT_LOCKED_SECTIONS = ["injection", "webfetch", "gdrive", "mcp", "network"];
2514
- var PROJECT_LOCKED_KEYS = ["indexing.cross_project_symbols", "worker.blocked_roots"];
2515
+ var PROJECT_LOCKED_SECTIONS = [
2516
+ "injection",
2517
+ "webfetch",
2518
+ "gdrive",
2519
+ "mcp",
2520
+ "network",
2521
+ "screenshot"
2522
+ ];
2523
+ var PROJECT_LOCKED_KEYS = [
2524
+ "image_shrink.max_image_pixels",
2525
+ "indexing.cross_project_symbols",
2526
+ "worker.blocked_roots"
2527
+ ];
2515
2528
  var _lastProjectConfigLockedKeys = [];
2516
2529
  function stripLockedProjectKeys(projectRaw) {
2517
2530
  const cleaned = {};
@@ -2642,10 +2655,12 @@ function resolveConfigKeyLayer(key, effectiveValue, cfg, projectInfo) {
2642
2655
  if (rawValueEquals(rawValue, effectiveValue)) return { layer: "project", path: projectInfo.path };
2643
2656
  return { layer: "project-invalid", path: projectInfo.path, rawValue, effectiveValue, reason: rejectionReason(key, rawValue, effectiveValue, cfg) };
2644
2657
  }
2658
+ function readConfigSource(p) {
2659
+ return decodeSource(fs5.readFileSync(p));
2660
+ }
2645
2661
  function readConfigToml(p) {
2646
2662
  try {
2647
- const text = fs5.readFileSync(p, "utf8");
2648
- return { raw: parse(text), parseError: null };
2663
+ return { raw: parse(readConfigSource(p)), parseError: null };
2649
2664
  } catch (e) {
2650
2665
  const code = e.code;
2651
2666
  if (code === "ENOENT") return { raw: {}, parseError: null };
@@ -2654,7 +2669,7 @@ function readConfigToml(p) {
2654
2669
  }
2655
2670
  function readConfigText(p) {
2656
2671
  try {
2657
- return { text: fs5.readFileSync(p, "utf8"), readError: null };
2672
+ return { text: readConfigSource(p), readError: null };
2658
2673
  } catch (e) {
2659
2674
  const code = e.code;
2660
2675
  if (code === "ENOENT") return { text: null, readError: null };
@@ -2696,11 +2711,11 @@ function isAutoTriggerMultiplierExplicit() {
2696
2711
  return ca_raw["auto_trigger_multiplier"] !== void 0;
2697
2712
  };
2698
2713
  try {
2699
- if (setsMultiplier(fs5.readFileSync(configPath(), "utf8"))) return true;
2714
+ if (setsMultiplier(readConfigSource(configPath()))) return true;
2700
2715
  } catch {
2701
2716
  }
2702
2717
  try {
2703
- return setsMultiplier(fs5.readFileSync(projectConfigPath(resolveConfigProjectRoot()), "utf8"));
2718
+ return setsMultiplier(readConfigSource(projectConfigPath(resolveConfigProjectRoot())));
2704
2719
  } catch {
2705
2720
  return false;
2706
2721
  }
@@ -4179,6 +4194,7 @@ function propagateEndLinesToSymbols(symbols, sections) {
4179
4194
  }
4180
4195
  function findMatchingBraceEndLine(content, openBraceIndex, totalLines, lineIndex, lineCommentPrefix, opts) {
4181
4196
  const block = opts?.blockComment;
4197
+ const backtick = opts?.backtickQuote === true;
4182
4198
  let depth = 0;
4183
4199
  let quote = null;
4184
4200
  for (let i = openBraceIndex; i < content.length; i++) {
@@ -4200,7 +4216,7 @@ function findMatchingBraceEndLine(content, openBraceIndex, totalLines, lineIndex
4200
4216
  while (i < content.length && content[i] !== "\n") i++;
4201
4217
  continue;
4202
4218
  }
4203
- if (ch === '"' || ch === "'") {
4219
+ if (ch === '"' || ch === "'" || backtick && ch === "`") {
4204
4220
  quote = ch;
4205
4221
  continue;
4206
4222
  }
@@ -4723,7 +4739,7 @@ function purgeDotenvEmbeddings(conn) {
4723
4739
  var MIGRATIONS = {
4724
4740
  // v1 -> v2: adds files.embed_sha, tracked separately from files.sha so embedding freshness can be gated independently of parse freshness (see makeIndexer in worker.ts). A pre-existing v1 database's `files` table predates the column, so it needs an explicit ALTER TABLE here; a brand-new database already has the column from SCHEMA_SQL's CREATE TABLE above, so the ALTER TABLE would fail with "duplicate column name" there -- swallow exactly that error and rethrow anything else, so a genuine ALTER TABLE failure is never silently lost.
4725
4741
  1: (conn) => alterTableIdempotent(conn, "ALTER TABLE files ADD COLUMN embed_sha TEXT"),
4726
- // v2 -> v3: adds files.retry_count, a durable per-path counter for consecutive transient-read-failure requeues (see MAX_TRANSIENT_RETRIES / requeueDirtyPath / resetTransientRetryCount in worker.ts). Previously this counter lived only in an in-memory Map inside worker.ts, which meant resetTransientRetryCount -- called from appendDirtyPath (hooks_index.ts) in the short-lived hook CLI process -- could never actually reach the long-lived detached daemon process's own copy of that Map: they are different Node processes with no shared memory, so the reset was a silent no-op in the real deployed topology. Persisting the counter in `files` makes it visible to both processes via the one thing they do share: the index DB. Same swallow-duplicate-column pattern as v1 -> v2 above.
4742
+ // v2 -> v3: adds files.retry_count, a durable per-path counter for consecutive transient-read-failure requeues (see MAX_TRANSIENT_RETRIES / requeueDirtyPath / clearRetryCount in worker.ts). Previously this counter lived only in an in-memory Map inside worker.ts, which meant the retry-count reset -- run at the time in the short-lived hook CLI process -- could never actually reach the long-lived detached daemon process's own copy of that Map: they are different Node processes with no shared memory, so the reset was a silent no-op in the real deployed topology. Persisting the counter in `files` makes it visible to both processes via the one thing they do share: the index DB. Same swallow-duplicate-column pattern as v1 -> v2 above.
4727
4743
  2: (conn) => alterTableIdempotent(conn, "ALTER TABLE files ADD COLUMN retry_count INTEGER NOT NULL DEFAULT 0"),
4728
4744
  // v8 -> v9: adds symbols.parent (see SCHEMA_VERSION comment above for why). A pre-existing v8 database's `symbols` table predates the column, so it needs an explicit ALTER TABLE here; a brand-new database already has the column from SCHEMA_SQL's CREATE TABLE above, so the ALTER TABLE would fail with "duplicate column name" there -- swallow exactly that error and rethrow anything else, same pattern as v1 -> v2 / v2 -> v3 above.
4729
4745
  8: (conn) => alterTableIdempotent(conn, "ALTER TABLE symbols ADD COLUMN parent TEXT NOT NULL DEFAULT ''"),
@@ -4743,13 +4759,32 @@ function runMigrations(conn, fromVersion, toVersion) {
4743
4759
  MIGRATIONS[v]?.(conn);
4744
4760
  }
4745
4761
  }
4746
- function initConnection(conn) {
4747
- const walMode = conn.pragma("journal_mode = WAL", { simple: true });
4748
- if (String(walMode).toLowerCase() !== "wal") {
4749
- throw new Error(`db: failed to enable WAL mode (got: ${walMode})`);
4762
+ var WAL_SWITCH_DEADLINE_MS = 15e3;
4763
+ function enableWalWithRetry(conn, budgetMs = WAL_SWITCH_DEADLINE_MS) {
4764
+ const deadline = Date.now() + budgetMs;
4765
+ let lastError;
4766
+ for (; ; ) {
4767
+ try {
4768
+ const mode = conn.pragma("journal_mode = WAL", { simple: true });
4769
+ if (String(mode).toLowerCase() === "wal") return;
4770
+ lastError = new Error(`got: ${String(mode)}`);
4771
+ } catch (e) {
4772
+ lastError = e;
4773
+ }
4774
+ try {
4775
+ if (String(conn.pragma("journal_mode", { simple: true })).toLowerCase() === "wal") return;
4776
+ } catch {
4777
+ }
4778
+ if (Date.now() >= deadline) {
4779
+ throw new Error(`db: failed to enable WAL mode (${lastError instanceof Error ? lastError.message : String(lastError)})`);
4780
+ }
4781
+ sleepSync(25);
4750
4782
  }
4751
- conn.pragma("synchronous = NORMAL");
4783
+ }
4784
+ function initConnection(conn) {
4752
4785
  conn.pragma("busy_timeout = 15000");
4786
+ enableWalWithRetry(conn);
4787
+ conn.pragma("synchronous = NORMAL");
4753
4788
  conn.function(
4754
4789
  "TG_LOWER",
4755
4790
  { deterministic: true },
@@ -6106,7 +6141,14 @@ var SECRET_PATTERNS = [
6106
6141
  // realistic header ('{"alg":' or similar) always starts with "eyJ", so that's the practical
6107
6142
  // anchor here -- each of the three dot-separated segments requires a minimum length to avoid
6108
6143
  // matching a short, coincidentally dotted token.
6109
- ["jwt", /eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/g],
6144
+ //
6145
+ // The trailing `={0,2}` on each segment is what makes a padded token match. Base64url as the JWT
6146
+ // spec defines it drops the `=` padding, but producers that reach for a plain base64 encoder
6147
+ // emit it anyway, and a `=` in the header or payload segment used to defeat the match outright:
6148
+ // not a partial redaction, but none at all, so the entire token was printed. `=` cannot appear
6149
+ // anywhere except the end of a segment, since it is not one of the characters the segment body
6150
+ // allows, so accepting it here cannot widen the match onto anything else.
6151
+ ["jwt", /eyJ[A-Za-z0-9_-]{10,}={0,2}\.[A-Za-z0-9_-]{10,}={0,2}\.[A-Za-z0-9_-]{10,}={0,2}/g],
6110
6152
  ["npm_token", /npm_[A-Za-z0-9]{36}/g],
6111
6153
  // rk_live_ (restricted keys) share the sk_live_/sk_test_ secret-key shape and risk level, so
6112
6154
  // one pattern covers all three rather than adding a near-duplicate entry.
@@ -6301,6 +6343,7 @@ export {
6301
6343
  countNoun,
6302
6344
  excludeTestsHiddenNote,
6303
6345
  countContentLines,
6346
+ pushAll,
6304
6347
  escapeRegExp,
6305
6348
  windowsCmdQuoteArg,
6306
6349
  redactUrlQuery,
@@ -6338,6 +6381,7 @@ export {
6338
6381
  lastProjectConfigLockedKeys,
6339
6382
  getProjectConfigInfo,
6340
6383
  resolveConfigKeyLayer,
6384
+ readConfigSource,
6341
6385
  isAutoTriggerMultiplierExplicit,
6342
6386
  loadConfig,
6343
6387
  invalidateConfigCache,
@@ -6,7 +6,7 @@ import {
6
6
  loadBlob,
7
7
  sanitizeFtsQuery,
8
8
  storeBlob
9
- } from "./token-goat-chunk-I2KCEGIP.mjs";
9
+ } from "./token-goat-chunk-UFOVM7ZN.mjs";
10
10
  import {
11
11
  SYMBOL_BODY_CHAR_CAP,
12
12
  extractErrorMessage,
@@ -16,7 +16,7 @@ import {
16
16
  redactSecrets,
17
17
  runGit,
18
18
  shortFingerprint
19
- } from "./token-goat-chunk-K7W3P2TJ.mjs";
19
+ } from "./token-goat-chunk-V465YKOR.mjs";
20
20
  import {
21
21
  registerReset
22
22
  } from "./token-goat-chunk-AO2QD2AG.mjs";
@@ -790,6 +790,7 @@ function formatCommandManifest(manifest) {
790
790
 
791
791
  // src/stdin_json.ts
792
792
  var DEFAULT_STDIN_TIMEOUT_MS = 5e3;
793
+ var MAX_STDIN_WALL_MS = 6e4;
793
794
  var MAX_STDIN_BYTES = 64 * 1024 * 1024;
794
795
  function readStdinJson(timeoutMs = DEFAULT_STDIN_TIMEOUT_MS, maxBytes = MAX_STDIN_BYTES) {
795
796
  return new Promise((resolve2, reject) => {
@@ -799,16 +800,27 @@ function readStdinJson(timeoutMs = DEFAULT_STDIN_TIMEOUT_MS, maxBytes = MAX_STDI
799
800
  const finish = (fn) => {
800
801
  if (settled) return;
801
802
  settled = true;
802
- clearTimeout(timer);
803
+ clearTimeout(idleTimer);
804
+ clearTimeout(wallTimer);
803
805
  process.stdin.removeListener("data", onData);
804
806
  process.stdin.removeListener("end", onEnd);
805
807
  process.stdin.removeListener("error", onError);
806
808
  fn();
807
809
  };
808
- const timer = setTimeout(() => {
810
+ let idleTimer = setTimeout(() => {
809
811
  finish(() => reject(new Error("readStdinJson: timed out waiting for stdin")));
810
812
  }, timeoutMs);
813
+ const wallTimer = setTimeout(() => {
814
+ finish(() => {
815
+ process.stdin.destroy();
816
+ reject(new Error(`readStdinJson: stdin took longer than ${MAX_STDIN_WALL_MS} ms`));
817
+ });
818
+ }, MAX_STDIN_WALL_MS);
811
819
  const onData = (chunk) => {
820
+ clearTimeout(idleTimer);
821
+ idleTimer = setTimeout(() => {
822
+ finish(() => reject(new Error("readStdinJson: timed out waiting for stdin")));
823
+ }, timeoutMs);
812
824
  totalBytes += chunk.length;
813
825
  if (totalBytes > maxBytes) {
814
826
  finish(() => {
@@ -4,7 +4,7 @@ import {
4
4
  loadConfig,
5
5
  redactSecrets,
6
6
  stripAnsiCodes
7
- } from "./token-goat-chunk-K7W3P2TJ.mjs";
7
+ } from "./token-goat-hook-chunk-Y2WHX2P3.mjs";
8
8
 
9
9
  // src/tool_filters/helpers.ts
10
10
  import * as fs from "node:fs";
@@ -6,7 +6,7 @@ import {
6
6
  loadBlob,
7
7
  sanitizeFtsQuery,
8
8
  storeBlob
9
- } from "./token-goat-hook-chunk-XM4W3WCO.mjs";
9
+ } from "./token-goat-hook-chunk-A77A26A7.mjs";
10
10
  import {
11
11
  SYMBOL_BODY_CHAR_CAP,
12
12
  extractErrorMessage,
@@ -16,7 +16,7 @@ import {
16
16
  redactSecrets,
17
17
  runGit,
18
18
  shortFingerprint
19
- } from "./token-goat-hook-chunk-2O7P4Z6Q.mjs";
19
+ } from "./token-goat-hook-chunk-Y2WHX2P3.mjs";
20
20
  import {
21
21
  registerReset
22
22
  } from "./token-goat-hook-chunk-BUOCULAM.mjs";
@@ -183,6 +183,7 @@ var HOOK_EVENTS = [
183
183
 
184
184
  // src/stdin_json.ts
185
185
  var DEFAULT_STDIN_TIMEOUT_MS = 5e3;
186
+ var MAX_STDIN_WALL_MS = 6e4;
186
187
  var MAX_STDIN_BYTES = 64 * 1024 * 1024;
187
188
  function readStdinJson(timeoutMs = DEFAULT_STDIN_TIMEOUT_MS, maxBytes = MAX_STDIN_BYTES) {
188
189
  return new Promise((resolve2, reject) => {
@@ -192,16 +193,27 @@ function readStdinJson(timeoutMs = DEFAULT_STDIN_TIMEOUT_MS, maxBytes = MAX_STDI
192
193
  const finish = (fn) => {
193
194
  if (settled) return;
194
195
  settled = true;
195
- clearTimeout(timer);
196
+ clearTimeout(idleTimer);
197
+ clearTimeout(wallTimer);
196
198
  process.stdin.removeListener("data", onData);
197
199
  process.stdin.removeListener("end", onEnd);
198
200
  process.stdin.removeListener("error", onError);
199
201
  fn();
200
202
  };
201
- const timer = setTimeout(() => {
203
+ let idleTimer = setTimeout(() => {
202
204
  finish(() => reject(new Error("readStdinJson: timed out waiting for stdin")));
203
205
  }, timeoutMs);
206
+ const wallTimer = setTimeout(() => {
207
+ finish(() => {
208
+ process.stdin.destroy();
209
+ reject(new Error(`readStdinJson: stdin took longer than ${MAX_STDIN_WALL_MS} ms`));
210
+ });
211
+ }, MAX_STDIN_WALL_MS);
204
212
  const onData = (chunk) => {
213
+ clearTimeout(idleTimer);
214
+ idleTimer = setTimeout(() => {
215
+ finish(() => reject(new Error("readStdinJson: timed out waiting for stdin")));
216
+ }, timeoutMs);
205
217
  totalBytes += chunk.length;
206
218
  if (totalBytes > maxBytes) {
207
219
  finish(() => {
@@ -25,7 +25,7 @@ import {
25
25
  runSkeleton,
26
26
  runSymbol,
27
27
  withPinnedReads
28
- } from "./token-goat-hook-chunk-NXVT4F3S.mjs";
28
+ } from "./token-goat-hook-chunk-MW5HPEGD.mjs";
29
29
  import {
30
30
  buildProjectMap,
31
31
  embeddingsDepsAvailable,
@@ -34,7 +34,7 @@ import {
34
34
  getProjectIndexCounts,
35
35
  isWorkerRunning,
36
36
  mapLookupBytesSaved
37
- } from "./token-goat-hook-chunk-XM4W3WCO.mjs";
37
+ } from "./token-goat-hook-chunk-A77A26A7.mjs";
38
38
  import {
39
39
  VERSION,
40
40
  dataDir,
@@ -45,7 +45,7 @@ import {
45
45
  normalizePath,
46
46
  recordStat,
47
47
  resolveProjectRoot
48
- } from "./token-goat-hook-chunk-2O7P4Z6Q.mjs";
48
+ } from "./token-goat-hook-chunk-Y2WHX2P3.mjs";
49
49
  import "./token-goat-hook-chunk-BUOCULAM.mjs";
50
50
  import "./token-goat-hook-chunk-RFRLWOQH.mjs";
51
51