token-goat 2.9.13 → 2.9.15

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 (52) hide show
  1. package/README.md +28 -1
  2. package/dist/token-goat-chunk-2X2EBBC6.mjs +277 -0
  3. package/dist/token-goat-chunk-3BTK54F3.mjs +1733 -0
  4. package/dist/token-goat-chunk-3NSDDTGL.mjs +34 -0
  5. package/dist/token-goat-chunk-4EXFN2AW.mjs +29 -0
  6. package/dist/token-goat-chunk-5V7DAC7V.mjs +123 -0
  7. package/dist/token-goat-chunk-6DLVZDB6.mjs +34 -0
  8. package/dist/token-goat-chunk-7ZYK25AO.mjs +24 -0
  9. package/dist/token-goat-chunk-A4JYKD5H.mjs +144 -0
  10. package/dist/token-goat-chunk-AH6QILZM.mjs +26 -0
  11. package/dist/token-goat-chunk-ASYEPR3S.mjs +212 -0
  12. package/dist/{token-goat-chunk-RDITECDL.mjs → token-goat-chunk-ATIFTMRC.mjs} +31 -13
  13. package/dist/{token-goat-chunk-ZZI3IDQZ.mjs → token-goat-chunk-BL5LNGBG.mjs} +3935 -11949
  14. package/dist/{token-goat-chunk-4NXUKV7D.mjs → token-goat-chunk-C5JIO6HK.mjs} +8 -4
  15. package/dist/{token-goat-chunk-FZU7GMUS.mjs → token-goat-chunk-DQ4J5AFF.mjs} +50 -18
  16. package/dist/token-goat-chunk-ERTXEKB6.mjs +228 -0
  17. package/dist/{token-goat-chunk-6B44WLIF.mjs → token-goat-chunk-GIIHUSZX.mjs} +142 -21
  18. package/dist/token-goat-chunk-GMOUBOX4.mjs +386 -0
  19. package/dist/{token-goat-chunk-3ESRORNM.mjs → token-goat-chunk-GMQQA7E4.mjs} +12576 -12326
  20. package/dist/token-goat-chunk-IVUQLQWN.mjs +2046 -0
  21. package/dist/token-goat-chunk-K7F2BFIK.mjs +2430 -0
  22. package/dist/{token-goat-chunk-U7X6LQD2.mjs → token-goat-chunk-LCZBPOIN.mjs} +10197 -9717
  23. package/dist/token-goat-chunk-LMFO66YD.mjs +331 -0
  24. package/dist/token-goat-chunk-LT7JRU6K.mjs +22 -0
  25. package/dist/token-goat-chunk-MZDIJJ3R.mjs +420 -0
  26. package/dist/token-goat-chunk-NDPO7GAH.mjs +177 -0
  27. package/dist/token-goat-chunk-NDRP4KJQ.mjs +4371 -0
  28. package/dist/token-goat-chunk-NEI4NC54.mjs +424 -0
  29. package/dist/token-goat-chunk-NU7TLMQK.mjs +585 -0
  30. package/dist/token-goat-chunk-OSUFN2FV.mjs +326 -0
  31. package/dist/token-goat-chunk-OUGNPMDA.mjs +959 -0
  32. package/dist/token-goat-chunk-PM76YS22.mjs +1341 -0
  33. package/dist/token-goat-chunk-S4XRY446.mjs +2637 -0
  34. package/dist/{token-goat-chunk-YOA4N6WA.mjs → token-goat-chunk-SFAS46RE.mjs} +5 -3
  35. package/dist/{token-goat-chunk-QWSUZWFP.mjs → token-goat-chunk-SZWYESBS.mjs} +793 -650
  36. package/dist/{token-goat-chunk-B3CTCQTH.mjs → token-goat-chunk-XEPXYDPI.mjs} +3 -2
  37. package/dist/token-goat-chunk-XTQAOTSO.mjs +89 -0
  38. package/dist/token-goat-chunk-Y4AFKTHK.mjs +22 -0
  39. package/dist/token-goat-chunk-YKG35VHC.mjs +228 -0
  40. package/dist/{token-goat-chunk-JOXLE672.mjs → token-goat-chunk-YQ7WI2CO.mjs} +990 -106
  41. package/dist/{token-goat-chunk-2WC4ZUXN.mjs → token-goat-chunk-YZX7EFG4.mjs} +1 -1
  42. package/dist/token-goat-chunk-Z6UXPYJA.mjs +62 -0
  43. package/dist/token-goat-hook.mjs +16 -8
  44. package/dist/token-goat.core.mjs +27 -10
  45. package/docs/cli.md +9 -6
  46. package/package.json +4 -2
  47. package/dist/token-goat-chunk-FQCNJV4V.mjs +0 -693
  48. package/dist/token-goat-chunk-JVNPCQB7.mjs +0 -31
  49. package/dist/token-goat-chunk-P2PU4CR5.mjs +0 -26
  50. package/dist/token-goat-chunk-QKXBGBQR.mjs +0 -3653
  51. package/dist/token-goat-chunk-UM47DRD3.mjs +0 -242
  52. package/dist/token-goat-chunk-UMXJN7DI.mjs +0 -5521
@@ -4,31 +4,26 @@ import {
4
4
  canRunWrappedShell,
5
5
  clipToDeliveryCap,
6
6
  deliveredOutputBytes
7
- } from "./token-goat-chunk-2WC4ZUXN.mjs";
7
+ } from "./token-goat-chunk-YZX7EFG4.mjs";
8
8
  import {
9
9
  DEFAULT_RECONCILE_BUDGET_MS,
10
10
  HOOK_EVENTS,
11
- accumulateResidentLines,
12
11
  buildCommandManifest,
13
12
  checkSymbolBodySize,
13
+ diagnoseSqlFailure,
14
14
  flattenCommandNames,
15
15
  getWebOutput,
16
16
  isReconcileClean,
17
- lineMayCarryResidentSignal,
18
17
  readStdinJson,
19
- readTranscriptTail,
20
18
  reconcileProject,
21
- repeatedSkillBodyHint,
22
- storeWebOutput,
23
- summarizeResidentContext,
24
- taskListPruneHint
25
- } from "./token-goat-chunk-FQCNJV4V.mjs";
19
+ storeWebOutput
20
+ } from "./token-goat-chunk-OUGNPMDA.mjs";
26
21
  import {
27
- BASH_OUTPUT_SUBDIR,
28
22
  GENERIC_SERVED_OUTPUT_KEY,
29
23
  OUTLINE_MAX_REPLACEMENT_RATIO,
30
24
  OUTLINE_MIN_HEADINGS,
31
25
  WEB_FETCH_KEY_SEP,
26
+ accumulateResidentLines,
32
27
  appendDirtyPath,
33
28
  applyHintTracking,
34
29
  buildDeltaCapsule,
@@ -36,24 +31,19 @@ import {
36
31
  classifyBashHint,
37
32
  classifyEditHint,
38
33
  clearCurlDownload,
39
- commandHash,
40
34
  compactPathFor,
41
35
  computeAdaptiveBudget,
42
- countSymbols,
43
36
  enqueueDirtyPathSafe,
44
37
  ensureWorkerAlive,
45
38
  extractCompactFromMarker,
46
- extractMarkdownHeadings,
47
39
  fenceUntrusted,
48
40
  fenceUntrustedSpans,
49
41
  fenceWithMatches,
50
42
  foldDelivery,
51
43
  foldDetail,
52
44
  foldingEnabled,
53
- formatHeadingTreeParts,
54
45
  formatProjectMap,
55
46
  formatShrinkSummary,
56
- getBashOutput,
57
47
  getBashOutputId,
58
48
  getContextPressure,
59
49
  getCurlDownloadPath,
@@ -61,7 +51,6 @@ import {
61
51
  getFileServedOutputs,
62
52
  getGlobMatchCount,
63
53
  getGrepMatchCount,
64
- getMonitoringRecallHint,
65
54
  getOutstandingAgentSpawns,
66
55
  getSessionBashOutputs,
67
56
  getSessionBashReruns,
@@ -71,15 +60,11 @@ import {
71
60
  hasSessionOutput,
72
61
  imageQualifiesForShrink,
73
62
  incrementSkillHit,
74
- indexRecallEntry,
75
63
  indexServedBody,
76
64
  installedSkillPath,
77
- isBashEntryStale,
78
- isBuildCommand,
79
- isScopedGitStatusOrDiffStatCommand,
80
65
  isStructuralRewriteAccepted,
81
- isTestRunnerCommand,
82
66
  lastTabContextMatches,
67
+ lineMayCarryResidentSignal,
83
68
  listSiblingSessionStates,
84
69
  loadSessionCache,
85
70
  loadSessionState,
@@ -96,6 +81,7 @@ import {
96
81
  planSourceSkeleton,
97
82
  preToolPathDeclined,
98
83
  probeImageMeta,
84
+ readTranscriptTail,
99
85
  recordBashOutput,
100
86
  recordBashRerun,
101
87
  recordCliRead,
@@ -113,27 +99,31 @@ import {
113
99
  recordSymbolRead,
114
100
  recordWebFetch,
115
101
  removeOutstandingAgentSpawn,
102
+ repeatedSkillBodyHint,
116
103
  saveSessionState,
117
104
  scanAndRecord,
118
105
  servedRunNotice,
119
106
  sessionOutputBodyBytes,
120
107
  sessionSidecarPath,
121
108
  setLastTabContext,
109
+ setTranscriptPath,
122
110
  shrinkImage,
123
- storeBashOutput,
124
111
  storeOutput,
125
- summarizeOutputDelta,
112
+ summarizeResidentContext,
126
113
  takePendingLargeFileHint,
114
+ taskListPruneHint,
127
115
  visionTokens,
128
116
  visionTokensSaved,
129
117
  vscodePathAllowed,
130
118
  wasCliReadThisSession,
131
119
  wasFileReadThisSession,
132
120
  wasHintShown
133
- } from "./token-goat-chunk-ZZI3IDQZ.mjs";
121
+ } from "./token-goat-chunk-BL5LNGBG.mjs";
134
122
  import {
123
+ BASH_OUTPUT_SUBDIR,
135
124
  BODY_FIRST_TOOL_RESPONSE_KEYS,
136
125
  OUTPUT_FIRST_TOOL_RESPONSE_KEYS,
126
+ commandHash,
137
127
  compressOutput,
138
128
  contextOutput,
139
129
  dedupeConsecutive,
@@ -145,14 +135,22 @@ import {
145
135
  extractToolResponseField,
146
136
  extractToolResultText,
147
137
  filterByName,
138
+ getBashOutput,
148
139
  getCwd,
149
140
  getFilePath,
141
+ getMonitoringRecallHint,
150
142
  getToolInput,
151
143
  getToolName,
144
+ getTranscriptPath,
152
145
  hasBareBackgroundOrNewline,
153
146
  hasUnquotedOperator,
147
+ indexRecallEntry,
148
+ isBashEntryStale,
149
+ isBuildCommand,
154
150
  isMcpErrorResponse,
155
151
  isRewriteWorthwhile,
152
+ isScopedGitStatusOrDiffStatCommand,
153
+ isTestRunnerCommand,
156
154
  isTextualContentBlock,
157
155
  makeDedupHintHandlers,
158
156
  mcpContentBlocks,
@@ -166,55 +164,68 @@ import {
166
164
  sessionStateKey,
167
165
  shlexSplit,
168
166
  splitOwnTrailingNotices,
167
+ storeBashOutput,
168
+ summarizeOutputDelta,
169
169
  withPreservedBlocks
170
- } from "./token-goat-chunk-JOXLE672.mjs";
170
+ } from "./token-goat-chunk-YQ7WI2CO.mjs";
171
+ import {
172
+ extractMarkdownHeadings,
173
+ formatHeadingTreeParts
174
+ } from "./token-goat-chunk-NDRP4KJQ.mjs";
171
175
  import {
172
- storeBlob
173
- } from "./token-goat-chunk-UM47DRD3.mjs";
176
+ countSymbols
177
+ } from "./token-goat-chunk-2X2EBBC6.mjs";
178
+ import {
179
+ recordStat,
180
+ redactSecrets,
181
+ savedTokensFromBytes,
182
+ storeBlob,
183
+ stripAnsiEscapes
184
+ } from "./token-goat-chunk-IVUQLQWN.mjs";
174
185
  import {
175
- detectHarness,
176
186
  detectLanguage,
187
+ getDb,
188
+ languageHasFlag
189
+ } from "./token-goat-chunk-K7F2BFIK.mjs";
190
+ import {
191
+ detectHarness,
177
192
  envBool,
178
193
  envInt,
179
194
  findProject,
180
- getDb,
181
195
  getHarnessName,
182
196
  isUnderSystemTemp,
183
- languageHasFlag,
184
197
  loadConfig,
185
- recordStat,
186
- redactSecrets,
187
- savedTokensFromBytes,
188
- shortFingerprint,
189
- stripAnsiEscapes
190
- } from "./token-goat-chunk-UMXJN7DI.mjs";
198
+ shortFingerprint
199
+ } from "./token-goat-chunk-S4XRY446.mjs";
191
200
  import {
192
201
  ENV_KEYS,
193
202
  IDENTICAL_READ_MIN_BODY_BYTES,
194
203
  PER_FILE_COUNTERFACTUAL_CEILING,
195
- UNTRUSTED_TOOL_TAG,
196
204
  VERSION,
197
205
  atomicWriteText,
198
206
  containsLineRun,
199
207
  countNoun,
200
208
  dataDir,
201
- displaySafePath,
202
- displaySafeText,
203
209
  ensureDirSync,
204
210
  escapesOntoNetworkThroughLinks,
205
211
  extractErrorMessage,
206
- fenceUntrustedFileContent,
207
212
  foldPath,
208
213
  globalDbPath,
209
214
  hasCreatedConfig,
210
215
  isInsideRoot,
216
+ runGit,
217
+ toKB
218
+ } from "./token-goat-chunk-PM76YS22.mjs";
219
+ import {
220
+ UNTRUSTED_TOOL_TAG,
221
+ displaySafePath,
222
+ displaySafeText,
223
+ fenceUntrustedFileContent,
211
224
  neutralizeSpokenMarkers,
212
225
  normalizePath,
213
226
  resolveIndexPath,
214
- runGit,
215
- toDisplayPath,
216
- toKB
217
- } from "./token-goat-chunk-QKXBGBQR.mjs";
227
+ toDisplayPath
228
+ } from "./token-goat-chunk-ERTXEKB6.mjs";
218
229
  import {
219
230
  init_define_import_meta_env
220
231
  } from "./token-goat-chunk-A37V4PBF.mjs";
@@ -747,7 +758,7 @@ function mergeManifestFiles(parent, siblingFiles) {
747
758
  }
748
759
  return Array.from(byPath.values());
749
760
  }
750
- function buildManifest(sessionId, cwd) {
761
+ function buildManifest(sessionId, cwd, transcriptPath) {
751
762
  const ownFiles = [...getSessionFiles().values()];
752
763
  const siblingFiles = sessionId !== void 0 ? listSiblingSessionStates(sessionId).flatMap((s) => s.files) : [];
753
764
  const files = siblingFiles.length > 0 ? mergeManifestFiles(ownFiles, siblingFiles) : ownFiles;
@@ -779,7 +790,7 @@ function buildManifest(sessionId, cwd) {
779
790
  );
780
791
  lines.push(...buildSafeToDiscardSection(files));
781
792
  lines.push(...buildMemEpochSection());
782
- return capManifestChars(lines.join("\n"), sessionId, cwd);
793
+ return capManifestChars(lines.join("\n"), sessionId, cwd, transcriptPath);
783
794
  }
784
795
  function gitDirtySignals(cwd) {
785
796
  const timeoutMs = loadConfig().hints.git_hint_max_ms;
@@ -797,11 +808,11 @@ function gitDirtySignals(cwd) {
797
808
  }
798
809
  return { hasPendingDiff, hasUncommittedChanges };
799
810
  }
800
- function adaptiveCharBonus(sessionId, cwd) {
811
+ function adaptiveCharBonus(sessionId, cwd, transcriptPath) {
801
812
  if (!cwd) return 0;
802
813
  const cache = loadSessionCache(sessionId ?? "") ?? {};
803
814
  const ageSecs = cache.created_ts !== void 0 ? Math.max(0, Date.now() / 1e3 - cache.created_ts) : 0;
804
- const contextPressure = getContextPressure(cache);
815
+ const contextPressure = getContextPressure(cache, transcriptPath);
805
816
  const { hasPendingDiff, hasUncommittedChanges } = gitDirtySignals(cwd);
806
817
  if (!hasPendingDiff && !hasUncommittedChanges) return 0;
807
818
  const baseline = computeAdaptiveBudget(cache, ageSecs, { contextPressure });
@@ -809,11 +820,11 @@ function adaptiveCharBonus(sessionId, cwd) {
809
820
  const deltaTokens = Math.max(0, withGitSignal - baseline);
810
821
  return deltaTokens * 3;
811
822
  }
812
- function capManifestChars(manifest, sessionId, cwd) {
823
+ function capManifestChars(manifest, sessionId, cwd, transcriptPath) {
813
824
  const cap = loadConfig().compact_assist.max_manifest_chars;
814
825
  if (cap <= 0) return manifest;
815
826
  if (manifest.length <= cap) return manifest;
816
- const effectiveCap = cap + adaptiveCharBonus(sessionId, cwd);
827
+ const effectiveCap = cap + adaptiveCharBonus(sessionId, cwd, transcriptPath);
817
828
  if (manifest.length <= effectiveCap) return manifest;
818
829
  const omitted = manifest.length - effectiveCap;
819
830
  return manifest.slice(0, effectiveCap) + `
@@ -886,7 +897,7 @@ function summaryBudgetDirective(budgetChars) {
886
897
  function preCompactHandler(event) {
887
898
  const out = loadConfig().compact_assist.enabled ? contextOutput(`${MANIFEST_PREAMBLE}${summaryBudgetDirective(loadConfig().compact_assist.summary_budget_chars)}
888
899
 
889
- ${buildManifest(event.sessionId, getCwd(event))}`) : passOutput();
900
+ ${buildManifest(event.sessionId, getCwd(event), getTranscriptPath(event))}`) : passOutput();
890
901
  markCompacted();
891
902
  return out;
892
903
  }
@@ -934,7 +945,7 @@ init_define_import_meta_env();
934
945
  var TRACKED_SKILL = "token-goat";
935
946
  var MAX_COMMANDS_SHOWN = 8;
936
947
  async function currentCommandNames() {
937
- const { buildProgram } = await import("./token-goat-chunk-JVNPCQB7.mjs");
948
+ const { buildProgram } = await import("./token-goat-chunk-NDPO7GAH.mjs");
938
949
  return flattenCommandNames(buildCommandManifest(buildProgram()));
939
950
  }
940
951
  async function recordSkillVersionSnapshot(sessionId, skillName) {
@@ -7645,114 +7656,20 @@ registerHook("post_tool_use", postSkillHandler, { toolName: "Skill" });
7645
7656
 
7646
7657
  // src/hooks_bash.ts
7647
7658
  init_define_import_meta_env();
7648
- import { statSync as statSync4, existsSync, openSync as openSync2, readSync, closeSync as closeSync2 } from "node:fs";
7649
- var CD_PREFIX_RE = /^(?:cd\s+(?:"[^"]*"|'[^']*'|\S+)[ \t]*(?:&&|;|\r?\n)\s*)+/;
7650
- function stripCdPrefix(cmd) {
7651
- const stripped = cmd.replace(CD_PREFIX_RE, "");
7652
- return stripped.trim() || cmd;
7653
- }
7654
- function stripTrailingStderrRedirect(cmd) {
7655
- const stripped = cmd.replace(/\s*2>&1\s*$/, "");
7656
- return stripped.trim() || cmd;
7657
- }
7658
- function hasTestRunScopeOrBudget(cmd) {
7659
- const hasTimeout = /(?:^|\s)(?:timeout(?:\.exe)?\s+\S+|--(?:test)?timeout(?:=|\s)\S+)/i.test(cmd);
7660
- const hasSelector = /(?:\s::\S+|\s(?:-k|-t|-run|--(?:testNamePattern|last-failed|test|project))(?:=|\s)|\b(?:tests?|spec)\S*\.(?:[cm]?[jt]sx?|py|go|rs)\b|(?:^|\s)\.\/\S+)/i.test(cmd);
7661
- return hasTimeout || hasSelector;
7662
- }
7663
- function isDirectTestRunnerCommand(cmd) {
7664
- return /^(?:pytest|(?:npx\s+)?(?:jest|vitest)|go\s+test|cargo\s+test)\b/i.test(cmd);
7665
- }
7666
- function extractCdPrefixDirs(rawCmd) {
7667
- const prefixMatch = rawCmd.match(CD_PREFIX_RE);
7668
- if (prefixMatch === null) return [];
7669
- const dirs = [];
7670
- const segmentPattern = /cd\s+(?:"([^"]*)"|'([^']*)'|(\S+))[ \t]*(?:&&|;|\r?\n)/g;
7671
- let match;
7672
- while ((match = segmentPattern.exec(prefixMatch[0])) !== null) {
7673
- const dir = match[1] ?? match[2] ?? match[3];
7674
- if (dir !== void 0) dirs.push(dir);
7675
- }
7676
- return dirs;
7677
- }
7678
- function resolveCdHintPath(rawCmd, filePath, cwd) {
7679
- if (extractCdPrefixDirs(rawCmd).length === 0) return filePath;
7680
- return resolveIndexPath(filePath, cdPrefixCwd(rawCmd, cwd));
7681
- }
7682
- function cdPrefixCwd(rawCmd, cwd) {
7683
- let dir = cwd;
7684
- for (const target of extractCdPrefixDirs(rawCmd)) dir = resolveIndexPath(target, dir);
7685
- return dir;
7686
- }
7687
- function surgicalHintFor(hintPath, isEnv, isConfig, isDoc) {
7688
- return isEnv ? 'Use `token-goat config-get "' + hintPath + '" KEY_NAME` to read a specific variable.' : isConfig ? 'Use `token-goat config-get "' + hintPath + '" KEY_NAME` or `token-goat section "' + hintPath + '::sectionName"` to read a specific value.' : isDoc ? 'Use `token-goat section "' + hintPath + '::SectionHeading"` to read one section.' : 'Use `token-goat read "' + hintPath + '::SymbolName"` to read one function or class.';
7689
- }
7690
- function surgicalHintForConfigDoc(filePath, isConfig, isDoc, isSql) {
7691
- return isConfig ? 'Use `token-goat config-get "' + filePath + '" KEY_NAME` or `token-goat section "' + filePath + '::sectionName"` to read a specific value.' : isSql ? 'Use `token-goat section "' + filePath + '::table_name"` to pull one CREATE TABLE / CREATE TYPE block.' : isDoc ? 'Use `token-goat section "' + filePath + '::SectionHeading"` to read one section.' : 'Use `token-goat read "' + filePath + '::SymbolName"` or `token-goat skeleton "' + filePath + '"` to see the file structure.';
7692
- }
7693
- function stripOutputPipeline(cmd) {
7694
- let inSingle = false;
7695
- let inDouble = false;
7696
- let cut = cmd.length;
7697
- let backslashes = 0;
7698
- for (let i = 0; i < cmd.length; i++) {
7699
- const ch = cmd[i];
7700
- if (ch === "\\") {
7701
- backslashes++;
7702
- continue;
7703
- }
7704
- const escaped = backslashes % 2 === 1;
7705
- backslashes = 0;
7706
- if (ch === "'" && !inDouble) {
7707
- if (!escaped) inSingle = !inSingle;
7708
- } else if (ch === '"' && !inSingle) {
7709
- if (!escaped) inDouble = !inDouble;
7710
- } else if (ch === "|" && !inSingle && !inDouble) {
7711
- if (cmd[i + 1] === "|") {
7712
- i++;
7713
- continue;
7714
- }
7715
- cut = i;
7716
- break;
7717
- }
7718
- }
7719
- let base = cmd.slice(0, cut);
7720
- let prev;
7721
- do {
7722
- prev = base;
7723
- const masked = base.replace(/"((?:[^"\\]|\\.)*)"/g, (_m, inner) => '"' + " ".repeat(inner.length) + '"').replace(/'([^']*)'/g, (_m, inner) => "'" + " ".repeat(inner.length) + "'");
7724
- const newMasked = masked.replace(/\s*(?:[0-9]*>&[0-9]+|[0-9&]*>>?\s*(?:"[^"]*"|'[^']*'|[^\s|]+))\s*$/, "");
7725
- if (newMasked.length < masked.length) {
7726
- base = base.slice(0, newMasked.length);
7727
- }
7728
- } while (base !== prev);
7729
- return base.trim();
7730
- }
7731
- function pipelineDivergenceNote(cmd, entryCommand) {
7732
- if (entryCommand === cmd) return "";
7733
- const preview = entryCommand.length > 60 ? entryCommand.slice(0, 57) + "..." : entryCommand;
7734
- return " (cached from a differently-piped run, `" + preview + "` \u2014 verify it covers what you need before trusting it)";
7735
- }
7736
- function extractCommand(event) {
7737
- const cmd = event.toolInput["command"];
7738
- return typeof cmd === "string" && cmd.trim() !== "" ? cmd.trim() : void 0;
7739
- }
7740
- var HEAD_MOVING_GIT_RE = /^\s*git\s+(?:checkout|switch|pull|merge|rebase|reset|cherry-pick)\b/i;
7741
- var PATH_SCOPED_CHECKOUT_RE = /^\s*git\s+checkout\s+(?:.*\s)?--(?:\s|$)/i;
7742
- function isHeadMovingGitCommand(cmd) {
7743
- if (!HEAD_MOVING_GIT_RE.test(cmd)) return false;
7744
- if (PATH_SCOPED_CHECKOUT_RE.test(cmd)) return false;
7745
- return true;
7659
+ import { statSync as statSync6, existsSync } from "node:fs";
7660
+
7661
+ // src/hooks_bash_commands.ts
7662
+ init_define_import_meta_env();
7663
+ import { statSync as statSync5 } from "node:fs";
7664
+
7665
+ // src/bash_extractors.ts
7666
+ init_define_import_meta_env();
7667
+ import { statSync as statSync4, openSync as openSync2, readSync, closeSync as closeSync2 } from "node:fs";
7668
+ function surgicalHintFor(hintPath, isEnv, isConfig, isDoc, isXml = false) {
7669
+ return isXml ? 'Use `token-goat xml-outline "' + hintPath + '"` to inspect structure, or `token-goat xml-query "' + hintPath + '" "<selector>"` to query specific nodes.' : isEnv ? 'Use `token-goat config-get "' + hintPath + '" KEY_NAME` to read a specific variable.' : isConfig ? 'Use `token-goat config-get "' + hintPath + '" KEY_NAME` or `token-goat section "' + hintPath + '::sectionName"` to read a specific value.' : isDoc ? 'Use `token-goat section "' + hintPath + '::SectionHeading"` to read one section.' : 'Use `token-goat read "' + hintPath + '::SymbolName"` to read one function or class.';
7746
7670
  }
7747
- var ORIG_HEAD_ELIGIBLE_GIT_RE = /^\s*git\s+(?:pull|merge|rebase)\b/i;
7748
- var ORIG_HEAD_REFLOG_MSG_RE = /^(merge\s|rebase\s\(|pull\s)/i;
7749
- var GIT_RESTORE_RE = /^\s*git\s+restore\b/i;
7750
- var GIT_RESTORE_STAGED_ONLY_RE = /(?:^|\s)(?:--staged|-S)(?:\s|$)/;
7751
- var GIT_STASH_APPLY_RE = /^\s*git\s+stash\s+(?:pop|apply)\b/i;
7752
- var PATCH_APPLY_SEGMENT_RE = /^\s*(?:\S*[/\\])?(?:git\s+apply|patch)\b/i;
7753
- var PATCH_APPLY_INSPECT_ONLY_RE = /(?:^|\s)--(?:check|stat|numstat|summary|dry-run)(?:\s|=|$)/i;
7754
- function isFormatterWriteSegment(segment) {
7755
- return /(?:^|\s|[/\\])(?:prettier|eslint)\b/i.test(segment) && /(?:^|\s)--(?:write|fix)(?:\s|$)/i.test(segment);
7671
+ function surgicalHintForConfigDoc(filePath, isConfig, isDoc, isSql, isXml = false) {
7672
+ return isXml ? 'Use `token-goat xml-outline "' + filePath + '"` to inspect structure, or `token-goat xml-query "' + filePath + '" "<selector>"` to query specific nodes.' : isConfig ? 'Use `token-goat config-get "' + filePath + '" KEY_NAME` or `token-goat section "' + filePath + '::sectionName"` to read a specific value.' : isSql ? 'Use `token-goat section "' + filePath + '::table_name"` to pull one CREATE TABLE / CREATE TYPE block.' : isDoc ? 'Use `token-goat section "' + filePath + '::SectionHeading"` to read one section.' : 'Use `token-goat read "' + filePath + '::SymbolName"` or `token-goat skeleton "' + filePath + '"` to see the file structure.';
7756
7673
  }
7757
7674
  function splitShellSegments(cmd) {
7758
7675
  const segments = [];
@@ -7786,174 +7703,40 @@ function splitShellSegments(cmd) {
7786
7703
  segments.push(cur);
7787
7704
  return segments.map((s) => s.trim()).filter((s) => s.length > 0);
7788
7705
  }
7789
- function safeShlexSplit(segment) {
7790
- try {
7791
- return shlexSplit(segment);
7792
- } catch {
7793
- return null;
7794
- }
7706
+ function isTempPath(fp) {
7707
+ const norm = fp.replace(/\\/g, "/");
7708
+ return /^\/tmp\//i.test(norm) || /\/var\/folders\//i.test(norm) || // Anchored to a local drive root on purpose. Unanchored, this clause said yes to
7709
+ // `//host/share/AppData/Local/Temp/x.md`, and the caller's answer to yes is to stat the path --
7710
+ // which on Windows opens an SMB session to whatever host a command named. A temp directory is
7711
+ // somewhere on a local volume; nothing that starts with two separators is one.
7712
+ /^[a-z]:\/(?:[^/]+\/)*AppData\/Local\/Temp\//i.test(norm) || norm.startsWith("/c/Users/") && norm.includes("/AppData/Local/Temp/") || isUnderSystemTemp(fp);
7795
7713
  }
7796
- function segmentCommandIs(segment, name2) {
7797
- return new RegExp("^\\s*(?:\\S*[/\\\\])?" + name2 + "(?:\\.exe)?\\b", "i").test(segment);
7714
+ function isOrchestratorStateFile(filePath) {
7715
+ const basename3 = (filePath.includes("/") ? filePath.split("/").at(-1) : filePath.split("\\").at(-1)) ?? filePath;
7716
+ return /^\.improve-state-/.test(basename3);
7798
7717
  }
7799
- function extractSedInPlaceFiles(segment) {
7800
- if (!segmentCommandIs(segment, "sed")) return [];
7801
- const tokens = safeShlexSplit(segment);
7802
- if (tokens === null) return [];
7803
- const rest = tokens.slice(1);
7804
- const inPlaceIdx = rest.findIndex((t) => t === "--in-place" || t.startsWith("--in-place=") || /^-[a-zA-Z]*i/.test(t));
7805
- if (inPlaceIdx === -1) return [];
7806
- const files = [];
7807
- let scriptTaken = rest.some((t) => t === "-e" || t === "-f" || t.startsWith("--expression") || t.startsWith("--file"));
7808
- for (let i = 0; i < rest.length; i++) {
7809
- const t = rest[i];
7810
- if (i === inPlaceIdx) {
7811
- if (t === "-i" && rest[i + 1] === "") i++;
7812
- continue;
7813
- }
7814
- if (t === "-e" || t === "-f" || t === "--expression" || t === "--file") {
7815
- i++;
7816
- continue;
7817
- }
7818
- if (t.length > 1 && t.startsWith("-")) continue;
7819
- if (!scriptTaken) {
7820
- scriptTaken = true;
7821
- continue;
7822
- }
7823
- files.push(t);
7824
- }
7825
- return files;
7718
+ function extractCatSourceFile(cmd) {
7719
+ const m = /^cat\s+(\S+\.(?:java|py|ts|tsx|js|jsx|go|rb|rs|cpp|cc|cxx|c|h|hpp|kt|swift|cs|php|scala|clj|css|scss|sass|less))\s*$/.exec(cmd);
7720
+ return m?.[1] ?? null;
7826
7721
  }
7827
- function extractTeeFiles(segment) {
7828
- if (!segmentCommandIs(segment, "tee")) return [];
7829
- const tokens = safeShlexSplit(segment);
7830
- if (tokens === null) return [];
7831
- return tokens.slice(1).filter((t) => t.length > 0 && !t.startsWith("-"));
7722
+ function classifyFileExtensions(filePath) {
7723
+ const basename3 = (filePath.includes("/") ? filePath.split("/").at(-1) : filePath.split("\\").at(-1)) ?? filePath;
7724
+ const isEnvFile = /^\.env(\.\w+)?$/i.test(basename3);
7725
+ const hasKnownExt = /\.(?:java|py|ts|tsx|js|jsx|go|rb|rs|cpp|cc|cxx|c|h|hpp|kt|swift|cs|php|scala|clj|css|scss|sass|less|md|mdx|rst|txt|json|yaml|yml|toml|xml|dtsx|ampkg|xaml|conf|cfg|ini|properties|sql|ps1|psm1|env)$/i.test(filePath);
7726
+ if (!hasKnownExt && !isEnvFile) return null;
7727
+ const isSql = /\.sql$/i.test(filePath);
7728
+ const isDoc = /\.(?:md|mdx|rst|txt)$/i.test(filePath);
7729
+ const isEnv = isEnvFile || /\.env$/i.test(filePath);
7730
+ const isConfig = /\.(?:json|yaml|yml|toml|conf|cfg|ini|properties)$/i.test(filePath);
7731
+ const isXml = /\.(?:xml|dtsx|ampkg|xaml)$/i.test(filePath);
7732
+ return { isDoc, isEnv, isConfig, isSql, isXml };
7832
7733
  }
7833
- function extractRedirectTargets(segment) {
7834
- const targets = [];
7835
- const re = /(?:^|\s)[0-9]?>>?\s*("[^"]*"|'[^']*'|[^\s|&<>]+)/g;
7836
- let m;
7837
- while ((m = re.exec(segment)) !== null) {
7838
- const raw = m[1];
7839
- targets.push(raw.length >= 2 && (raw[0] === '"' || raw[0] === "'") ? raw.slice(1, -1) : raw);
7840
- }
7841
- return targets;
7842
- }
7843
- function extractGitRestorePathspecs(cmd) {
7844
- const m = /^\s*git\s+restore\b(.*)$/i.exec(cmd);
7845
- if (m === null) return [];
7846
- const tokens = safeShlexSplit(m[1] ?? "");
7847
- if (tokens === null) return [];
7848
- const dashDash = tokens.indexOf("--");
7849
- if (dashDash !== -1) return tokens.slice(dashDash + 1);
7850
- const specs = [];
7851
- let skipNext = false;
7852
- for (const t of tokens) {
7853
- if (skipNext) {
7854
- skipNext = false;
7855
- continue;
7856
- }
7857
- if (t === "-s" || t === "--source") {
7858
- skipNext = true;
7859
- continue;
7860
- }
7861
- if (t.startsWith("-")) continue;
7862
- specs.push(t);
7863
- }
7864
- return specs;
7865
- }
7866
- function gitRepoRoot(gitDir) {
7867
- const toplevel = runGit(["rev-parse", "--show-toplevel"], { cwd: gitDir, timeoutMs: 5e3 });
7868
- return toplevel.exitCode === 0 && toplevel.stdout.trim() !== "" ? toplevel.stdout.trim() : gitDir;
7869
- }
7870
- function expandGitPathspecs(specs, gitDir) {
7871
- if (specs.length === 0) return [];
7872
- const listed = runGit(["ls-files", "--full-name", "--", ...specs], { cwd: gitDir, timeoutMs: 5e3 });
7873
- if (listed.exitCode !== 0) return [];
7874
- const repoRoot = gitRepoRoot(gitDir);
7875
- return listed.stdout.split("\n").map((l) => l.trim()).filter((l) => l.length > 0).map((rel) => resolveIndexPath(rel, repoRoot));
7876
- }
7877
- function workingTreeStatusPaths(gitDir) {
7878
- const status = runGit(["status", "--porcelain"], { cwd: gitDir, timeoutMs: 5e3 });
7879
- if (status.exitCode !== 0) return [];
7880
- const repoRoot = gitRepoRoot(gitDir);
7881
- const out = [];
7882
- for (const line of status.stdout.split("\n")) {
7883
- if (line.length < 4) continue;
7884
- let rel = line.slice(3).trim();
7885
- if (rel.length === 0) continue;
7886
- const arrow = rel.lastIndexOf(" -> ");
7887
- if (arrow !== -1) rel = rel.slice(arrow + 4);
7888
- if (rel.startsWith('"')) {
7889
- try {
7890
- rel = JSON.parse(rel);
7891
- } catch {
7892
- continue;
7893
- }
7894
- }
7895
- out.push(resolveIndexPath(rel, repoRoot));
7896
- }
7897
- return out;
7898
- }
7899
- function enqueueRewrittenPath(absPath) {
7900
- if (/(?:^|[/\\])(?:NUL|nul)$/.test(absPath) || absPath.replace(/\\/g, "/").endsWith("/dev/null")) return;
7901
- try {
7902
- if (!statSync4(absPath).isFile()) return;
7903
- } catch {
7904
- return;
7905
- }
7906
- enqueueDirtyPathSafe(absPath, { alreadyResolved: true });
7907
- }
7908
- function enqueueNonHeadMovingRewrites(cmd, rawCmd, cwd) {
7909
- const atCwd = (f) => resolveIndexPath(resolveCdHintPath(rawCmd, f, cwd), cwd);
7910
- const paths = [];
7911
- let needsStatusSweep = GIT_STASH_APPLY_RE.test(cmd);
7912
- if (GIT_RESTORE_RE.test(cmd) && !GIT_RESTORE_STAGED_ONLY_RE.test(cmd)) {
7913
- paths.push(...expandGitPathspecs(extractGitRestorePathspecs(cmd), cwd));
7914
- }
7915
- for (const segment of splitShellSegments(cmd)) {
7916
- if (PATCH_APPLY_SEGMENT_RE.test(segment) && !PATCH_APPLY_INSPECT_ONLY_RE.test(segment)) needsStatusSweep = true;
7917
- if (isFormatterWriteSegment(segment)) needsStatusSweep = true;
7918
- for (const f of extractSedInPlaceFiles(segment)) paths.push(atCwd(f));
7919
- for (const f of extractTeeFiles(segment)) paths.push(atCwd(f));
7920
- for (const f of extractRedirectTargets(segment)) paths.push(atCwd(f));
7921
- }
7922
- if (needsStatusSweep) paths.push(...workingTreeStatusPaths(cwd));
7923
- for (const p of paths) enqueueRewrittenPath(p);
7924
- }
7925
- function isTempPath(fp) {
7926
- const norm = fp.replace(/\\/g, "/");
7927
- return /^\/tmp\//i.test(norm) || /\/var\/folders\//i.test(norm) || // Anchored to a local drive root on purpose. Unanchored, this clause said yes to
7928
- // `//host/share/AppData/Local/Temp/x.md`, and the caller's answer to yes is to stat the path --
7929
- // which on Windows opens an SMB session to whatever host a command named. A temp directory is
7930
- // somewhere on a local volume; nothing that starts with two separators is one.
7931
- /^[a-z]:\/(?:[^/]+\/)*AppData\/Local\/Temp\//i.test(norm) || norm.startsWith("/c/Users/") && norm.includes("/AppData/Local/Temp/") || isUnderSystemTemp(fp);
7932
- }
7933
- function isOrchestratorStateFile(filePath) {
7934
- const basename3 = (filePath.includes("/") ? filePath.split("/").at(-1) : filePath.split("\\").at(-1)) ?? filePath;
7935
- return /^\.improve-state-/.test(basename3);
7936
- }
7937
- function extractCatSourceFile(cmd) {
7938
- const m = /^cat\s+(\S+\.(?:java|py|ts|tsx|js|jsx|go|rb|rs|cpp|cc|cxx|c|h|hpp|kt|swift|cs|php|scala|clj|css|scss|sass|less))\s*$/.exec(cmd);
7939
- return m?.[1] ?? null;
7940
- }
7941
- function classifyFileExtensions(filePath) {
7942
- const basename3 = (filePath.includes("/") ? filePath.split("/").at(-1) : filePath.split("\\").at(-1)) ?? filePath;
7943
- const isEnvFile = /^\.env(\.\w+)?$/i.test(basename3);
7944
- const hasKnownExt = /\.(?:java|py|ts|tsx|js|jsx|go|rb|rs|cpp|cc|cxx|c|h|hpp|kt|swift|cs|php|scala|clj|css|scss|sass|less|md|mdx|rst|txt|json|yaml|yml|toml|xml|conf|cfg|ini|properties|sql|ps1|psm1|env)$/i.test(filePath);
7945
- if (!hasKnownExt && !isEnvFile) return null;
7946
- const isSql = /\.sql$/i.test(filePath);
7947
- const isDoc = /\.(?:md|mdx|rst|txt)$/i.test(filePath);
7948
- const isEnv = isEnvFile || /\.env$/i.test(filePath);
7949
- const isConfig = /\.(?:json|yaml|yml|toml|conf|cfg|ini|properties)$/i.test(filePath);
7950
- return { isDoc, isEnv, isConfig, isSql };
7951
- }
7952
- function classifyDocConfig(filePath) {
7953
- const isDoc = /\.(?:md|mdx|rst|txt)$/i.test(filePath);
7954
- const isConfig = /\.(?:json|yaml|yml|toml|conf|cfg|ini|properties)$/i.test(filePath);
7955
- const isSql = /\.sql$/i.test(filePath);
7956
- return { isDoc, isConfig, isSql };
7734
+ function classifyDocConfig(filePath) {
7735
+ const isDoc = /\.(?:md|mdx|rst|txt)$/i.test(filePath);
7736
+ const isConfig = /\.(?:json|yaml|yml|toml|conf|cfg|ini|properties)$/i.test(filePath);
7737
+ const isSql = /\.sql$/i.test(filePath);
7738
+ const isXml = /\.(?:xml|dtsx|ampkg|xaml)$/i.test(filePath);
7739
+ return { isDoc, isConfig, isSql, isXml };
7957
7740
  }
7958
7741
  function classifyCatPath(filePath, cmd0) {
7959
7742
  if (isTempPath(filePath)) return null;
@@ -8030,10 +7813,10 @@ function extractPowerShellFileMethodRead(cmd, event) {
8030
7813
  }
8031
7814
  const flags = classifyFileExtensions(filePath);
8032
7815
  if (flags === null) {
8033
- const { isDoc, isConfig, isSql } = classifyDocConfig(filePath);
8034
- return { filePath, isDoc, isEnv: false, isConfig, isSql };
7816
+ const { isDoc, isConfig, isSql, isXml } = classifyDocConfig(filePath);
7817
+ return { filePath, isDoc, isEnv: false, isConfig, isSql, isXml };
8035
7818
  }
8036
- return { filePath, isDoc: flags.isDoc, isEnv: flags.isEnv, isConfig: flags.isConfig, isSql: flags.isSql };
7819
+ return { filePath, isDoc: flags.isDoc, isEnv: flags.isEnv, isConfig: flags.isConfig, isSql: flags.isSql, isXml: flags.isXml };
8037
7820
  }
8038
7821
  function extractRgSymbolSearch(cmd) {
8039
7822
  if (!/^(?:rg|grep)\s+/.test(cmd)) return null;
@@ -8188,7 +7971,7 @@ function pythonOpenPathsAreAllLiteral(text) {
8188
7971
  const calls = pythonOpenCalls(text);
8189
7972
  return calls.length > 0 && calls.every((call) => call.pathLiteral !== null);
8190
7973
  }
8191
- var KNOWN_PYTHON_EXT_STR = "java|py|ts|tsx|js|jsx|go|rb|rs|cpp|cc|cxx|c|h|hpp|kt|swift|cs|php|scala|clj|css|scss|sass|less|md|mdx|rst|txt|json|yaml|yml|toml|xml|html|htm|conf|cfg|ini|properties|sql|ps1|psm1|env";
7974
+ var KNOWN_PYTHON_EXT_STR = "java|py|ts|tsx|js|jsx|go|rb|rs|cpp|cc|cxx|c|h|hpp|kt|swift|cs|php|scala|clj|css|scss|sass|less|md|mdx|rst|txt|json|yaml|yml|toml|xml|dtsx|ampkg|xaml|html|htm|conf|cfg|ini|properties|sql|ps1|psm1|env";
8192
7975
  var OPEN_EXT = new RegExp(`\\.(?:${KNOWN_PYTHON_EXT_STR})$`, "i");
8193
7976
  function extractPythonFileRead(cmd) {
8194
7977
  let inner = cmd.trim();
@@ -8214,11 +7997,11 @@ function extractPythonFileRead(cmd) {
8214
7997
  const classifyResult = (filePath) => {
8215
7998
  const flags = classifyFileExtensions(filePath);
8216
7999
  if (flags !== null) {
8217
- return { filePath, isDoc: flags.isDoc, isConfig: flags.isConfig, isEnv: flags.isEnv, isSql: flags.isSql, isOutputFile: false };
8000
+ return { filePath, isDoc: flags.isDoc, isConfig: flags.isConfig, isEnv: flags.isEnv, isSql: flags.isSql, isXml: flags.isXml, isOutputFile: false };
8218
8001
  }
8219
- const { isDoc, isConfig, isSql } = classifyDocConfig(filePath);
8002
+ const { isDoc, isConfig, isSql, isXml } = classifyDocConfig(filePath);
8220
8003
  const isEnv = /\.env(\.\w+)?$/i.test(filePath);
8221
- return { filePath, isDoc, isConfig, isEnv, isSql, isOutputFile: false };
8004
+ return { filePath, isDoc, isConfig, isEnv, isSql, isXml, isOutputFile: false };
8222
8005
  };
8223
8006
  const heredocMatch = /^python3?\s+-\s+<<\s*'?(\w+)'?\s*\n([\s\S]*?)\n\1\s*$/.exec(pythonBody);
8224
8007
  if (heredocMatch) {
@@ -8272,9 +8055,9 @@ function extractHeadFile(cmd) {
8272
8055
  const filePath = direct !== null ? direct[3] ?? direct[4] ?? direct[5] : piped[1] ?? piped[2] ?? piped[3];
8273
8056
  if (filePath === void 0) return null;
8274
8057
  if (isTempPath(filePath)) return null;
8275
- if (!/\.(?:ts|tsx|js|jsx|py|go|java|rs|rb|cs|md|mdx|rst|txt|json|yaml|yml|toml|sql|sh)$/i.test(filePath)) return null;
8276
- const { isDoc, isConfig, isSql } = classifyDocConfig(filePath);
8277
- return { filePath, isDoc, isConfig, isSql, n };
8058
+ if (!/\.(?:ts|tsx|js|jsx|py|go|java|rs|rb|cs|md|mdx|rst|txt|json|yaml|yml|toml|sql|sh|xml|dtsx|ampkg|xaml)$/i.test(filePath)) return null;
8059
+ const { isDoc, isConfig, isSql, isXml } = classifyDocConfig(filePath);
8060
+ return { filePath, isDoc, isConfig, isSql, isXml, n };
8278
8061
  }
8279
8062
  function extractSedRange(cmd) {
8280
8063
  const m = /^sed\s+-n\s+(?:['"]((?:\d+,\d+p)(?:;\d+,\d+p)*)['"]|(\d+,\d+p))\s+(?:"([^"]+)"|'([^']+)'|(\S+))(?:\s+2>(?:\/dev\/null|&1))?\s*$/.exec(cmd);
@@ -8376,7 +8159,7 @@ function leadingLinesHint(lead, hintPath, start, end, flags, preHookCwd) {
8376
8159
  const key = resolveIndexPath(hintPath, preHookCwd ?? process.cwd());
8377
8160
  const prior = findRangeOverlap(getFileLineRanges(key), start, end);
8378
8161
  if (prior !== null) return sedOverlapHint(hintPath, prior, start, end);
8379
- return lead + surgicalHintForConfigDoc(hintPath, flags.isConfig, flags.isDoc, flags.isSql);
8162
+ return lead + surgicalHintForConfigDoc(hintPath, flags.isConfig, flags.isDoc, flags.isSql, flags.isXml ?? false);
8380
8163
  }
8381
8164
  function findRangeOverlap(prior, start, end) {
8382
8165
  let best = null;
@@ -8433,9 +8216,9 @@ function extractTailFile(cmd) {
8433
8216
  const filePath = direct !== null ? direct[3] ?? direct[4] ?? direct[5] : piped[1] ?? piped[2] ?? piped[3];
8434
8217
  if (!filePath) return null;
8435
8218
  if (isTempPath(filePath)) return null;
8436
- if (!/\.(?:ts|tsx|js|jsx|py|go|java|rs|rb|cs|md|mdx|rst|txt|json|yaml|yml|toml|sql|sh)$/i.test(filePath)) return null;
8437
- const { isDoc, isConfig, isSql } = classifyDocConfig(filePath);
8438
- return { filePath, isDoc, isConfig, isSql };
8219
+ if (!/\.(?:ts|tsx|js|jsx|py|go|java|rs|rb|cs|md|mdx|rst|txt|json|yaml|yml|toml|sql|sh|xml|dtsx|ampkg|xaml)$/i.test(filePath)) return null;
8220
+ const { isDoc, isConfig, isSql, isXml } = classifyDocConfig(filePath);
8221
+ return { filePath, isDoc, isConfig, isSql, isXml };
8439
8222
  }
8440
8223
  function extractGetContentTail(cmd) {
8441
8224
  const tailMatch = /-Tail\s+(\d+)/i.exec(cmd);
@@ -8450,9 +8233,9 @@ function extractGetContentTail(cmd) {
8450
8233
  const filePath = (beforeTail || afterTail).replace(/^["']|["']$/g, "");
8451
8234
  if (!filePath) return null;
8452
8235
  if (isTempPath(filePath)) return null;
8453
- if (!/\.(?:ts|tsx|js|jsx|py|go|java|rs|rb|cs|md|mdx|rst|txt|json|yaml|yml|toml|sql|sh|ps1|psm1)$/i.test(filePath)) return null;
8454
- const { isDoc, isConfig, isSql } = classifyDocConfig(filePath);
8455
- return { filePath, isDoc, isConfig, isSql };
8236
+ if (!/\.(?:ts|tsx|js|jsx|py|go|java|rs|rb|cs|md|mdx|rst|txt|json|yaml|yml|toml|sql|sh|ps1|psm1|xml|dtsx|ampkg|xaml)$/i.test(filePath)) return null;
8237
+ const { isDoc, isConfig, isSql, isXml } = classifyDocConfig(filePath);
8238
+ return { filePath, isDoc, isConfig, isSql, isXml };
8456
8239
  }
8457
8240
  function extractGetContentSelectFirst(cmd) {
8458
8241
  const m = /^(Get-Content|gc)\s+([^|]+)\s*\|\s*(Select-Object|select)\s+(-First\s+(\d+))/i.exec(cmd);
@@ -8462,9 +8245,9 @@ function extractGetContentSelectFirst(cmd) {
8462
8245
  if (n <= 10) return null;
8463
8246
  if (!filePath) return null;
8464
8247
  if (isTempPath(filePath)) return null;
8465
- if (!/\.(?:ts|tsx|js|jsx|py|go|java|rs|rb|cs|md|mdx|rst|txt|json|yaml|yml|toml|sql|sh|ps1|psm1)$/i.test(filePath)) return null;
8466
- const { isDoc, isConfig, isSql } = classifyDocConfig(filePath);
8467
- return { filePath, isDoc, isConfig, isSql, n };
8248
+ if (!/\.(?:ts|tsx|js|jsx|py|go|java|rs|rb|cs|md|mdx|rst|txt|json|yaml|yml|toml|sql|sh|ps1|psm1|xml|dtsx|ampkg|xaml)$/i.test(filePath)) return null;
8249
+ const { isDoc, isConfig, isSql, isXml } = classifyDocConfig(filePath);
8250
+ return { filePath, isDoc, isConfig, isSql, isXml, n };
8468
8251
  }
8469
8252
  function taskOutputIsJsonlTranscript(outPath) {
8470
8253
  let fd = null;
@@ -8839,120 +8622,549 @@ function isCompressibleSingleCommand(cmd) {
8839
8622
  if (hasBareBackgroundOrNewline(cmd)) return false;
8840
8623
  return true;
8841
8624
  }
8842
- function maybeCompressRewrite(event, rawCmd, cmd) {
8843
- if (process.env["TOKEN_GOAT_BASH_COMPRESS"] === "0") return null;
8844
- if (event.raw["_tg_harness"] === "vscode") return null;
8845
- let cfg;
8846
- try {
8847
- cfg = loadConfig().bash_compress;
8848
- } catch {
8849
- return null;
8625
+ function extractTerminalXmlParsing(cmd) {
8626
+ const trimmed = cmd.trim();
8627
+ if (!trimmed) return null;
8628
+ const selectXmlM = /\bSelect-Xml\b(?:\s+(?:-Path\s+)?["']?([^"'\s|;]+(?:\.xml|\.dtsx|\.ampkg|\.xaml))["']?)?/i.exec(trimmed);
8629
+ if (selectXmlM) {
8630
+ const rawPath = selectXmlM[1];
8631
+ const filePath = rawPath && !rawPath.startsWith("-") ? rawPath : void 0;
8632
+ return { filePath, toolOrScript: "Select-Xml" };
8633
+ }
8634
+ const psXmlCastM = /\[(?:xml|System\.Xml\.XmlDocument)\]/i.exec(trimmed);
8635
+ if (psXmlCastM) {
8636
+ const fileM = /["']([^"'\r\n]+\.(?:xml|dtsx|ampkg|xaml))["']/i.exec(trimmed) || /(?:Get-Content|gc|ReadAllText)\s+["']?([^"'\s|;)]+\.(?:xml|dtsx|ampkg|xaml))["']?/i.exec(trimmed);
8637
+ const filePath = fileM?.[1];
8638
+ return { filePath, toolOrScript: psXmlCastM[0] };
8639
+ }
8640
+ const inspectScriptM = /(?:powershell|pwsh|&|\.)?\s*["']?(\.?[/\\]?inspect_[^"'\s|;]+\.ps1)["']/i.exec(trimmed) || /\b(inspect_[^"'\s|;]+\.ps1)\b/i.exec(trimmed);
8641
+ if (inspectScriptM) {
8642
+ const fileM = /["']([^"'\r\n]+\.(?:xml|dtsx|ampkg|xaml))["']/i.exec(trimmed);
8643
+ const filePath = fileM?.[1];
8644
+ return { filePath, toolOrScript: inspectScriptM[1] ?? "inspect script" };
8645
+ }
8646
+ const pyXmlM = /\bpython(?:\d+(?:\.\d+)?)?(?:\.exe)?\b.*-c\s+["'].*?\b(xml\.etree|BeautifulSoup|minidom|lxml)\b/i.exec(trimmed);
8647
+ if (pyXmlM) {
8648
+ const fileM = /["']([^"'\r\n]+\.(?:xml|dtsx|ampkg|xaml))["']/i.exec(trimmed);
8649
+ const filePath = fileM?.[1];
8650
+ return { filePath, toolOrScript: `python ${pyXmlM[1]}` };
8651
+ }
8652
+ const cliXmlM = /\b(xmllint|xmlstarlet|xidel)\b/i.exec(trimmed);
8653
+ if (cliXmlM) {
8654
+ const fileM = /["']?([^"'\s|;]+\.(?:xml|dtsx|ampkg|xaml))["']?/i.exec(trimmed);
8655
+ const rawPath = fileM?.[1];
8656
+ const filePath = rawPath && !rawPath.startsWith("-") ? rawPath : void 0;
8657
+ return { filePath, toolOrScript: cliXmlM[1] };
8850
8658
  }
8851
- if (!cfg.enabled) return null;
8852
- if (!canRunWrappedShell()) return null;
8853
- const gateCmd = stripTrailingStderrRedirect(cmd);
8854
- const detected = detectFromCommand(gateCmd, getCwd(event));
8855
- let filterName;
8856
- if (detected !== null) {
8857
- filterName = detected.filter.name;
8858
- } else if (isCompressibleSingleCommand(gateCmd)) {
8859
- filterName = "generic";
8860
- } else {
8861
- return null;
8659
+ return null;
8660
+ }
8661
+
8662
+ // src/hooks_bash_commands.ts
8663
+ var CD_PREFIX_RE = /^(?:cd\s+(?:"[^"]*"|'[^']*'|\S+)[ \t]*(?:&&|;|\r?\n)\s*)+/;
8664
+ function stripCdPrefix(cmd) {
8665
+ const stripped = cmd.replace(CD_PREFIX_RE, "");
8666
+ return stripped.trim() || cmd;
8667
+ }
8668
+ function stripTrailingStderrRedirect(cmd) {
8669
+ const stripped = cmd.replace(/\s*2>&1\s*$/, "");
8670
+ return stripped.trim() || cmd;
8671
+ }
8672
+ function hasTestRunScopeOrBudget(cmd) {
8673
+ const hasTimeout = /(?:^|\s)(?:timeout(?:\.exe)?\s+\S+|--(?:test)?timeout(?:=|\s)\S+)/i.test(cmd);
8674
+ const hasSelector = /(?:\s::\S+|\s(?:-k|-t|-run|--(?:testNamePattern|last-failed|test|project))(?:=|\s)|\b(?:tests?|spec)\S*\.(?:[cm]?[jt]sx?|py|go|rs)\b|(?:^|\s)\.\/\S+)/i.test(cmd);
8675
+ return hasTimeout || hasSelector;
8676
+ }
8677
+ function isDirectTestRunnerCommand(cmd) {
8678
+ return /^(?:pytest|(?:npx\s+)?(?:jest|vitest)|go\s+test|cargo\s+test)\b/i.test(cmd);
8679
+ }
8680
+ function extractCdPrefixDirs(rawCmd) {
8681
+ const prefixMatch = rawCmd.match(CD_PREFIX_RE);
8682
+ if (prefixMatch === null) return [];
8683
+ const dirs = [];
8684
+ const segmentPattern = /cd\s+(?:"([^"]*)"|'([^']*)'|(\S+))[ \t]*(?:&&|;|\r?\n)/g;
8685
+ let match;
8686
+ while ((match = segmentPattern.exec(prefixMatch[0])) !== null) {
8687
+ const dir = match[1] ?? match[2] ?? match[3];
8688
+ if (dir !== void 0) dirs.push(dir);
8862
8689
  }
8863
- if (cfg.disabled_filters.includes(filterName)) return null;
8864
- const wrapped = `token-goat compress -f ${filterName} --timeout ${cfg.timeout_seconds} -c ${shellQuoteSingle(rawCmd)}`;
8865
- return { hookType: "rewriteInput", updatedInput: { ...event.toolInput, command: wrapped } };
8690
+ return dirs;
8866
8691
  }
8867
- var PIPELINE_PASSTHROUGH_HEADS = /* @__PURE__ */ new Set(["head", "tail", "cat", "tee", "less", "more"]);
8868
- var CI_BUILD_TEST_FILTER_NAMES = /* @__PURE__ */ new Set([
8869
- "generic-ci",
8870
- "jest",
8871
- "vitest",
8872
- "pytest",
8873
- "go_test",
8874
- "cargo_test",
8875
- "cargo",
8876
- "go",
8877
- "make",
8878
- "cmake",
8879
- "gradle",
8880
- "maven",
8881
- "dotnet",
8882
- "turbo",
8883
- "nx",
8884
- "lerna",
8885
- "webpack",
8886
- "eslint",
8887
- "ruff",
8888
- "clippy",
8889
- "flake8",
8890
- "mypy",
8891
- "prettier",
8892
- "tsc"
8893
- ]);
8894
- function isCiBuildTestSegment(cleaned, cwd) {
8895
- if (isTestRunnerCommand(cleaned) || isBuildCommand(cleaned) || isTscCommand(cleaned)) return true;
8896
- if (/^\s*(npm|pnpm|yarn|bun)\s+(run\s+)?(test|build|lint|typecheck|check|ci|guards)\b/i.test(cleaned)) return true;
8897
- if (/^\s*(cargo|go|dotnet|make|gradle|mvn|pytest|vitest|jest|eslint|ruff)\b/i.test(cleaned)) return true;
8898
- const detected = detectFromCommand(cleaned, cwd ?? void 0);
8899
- if (detected !== null && CI_BUILD_TEST_FILTER_NAMES.has(detected.filter.name)) return true;
8900
- return false;
8692
+ function resolveCdHintPath(rawCmd, filePath, cwd) {
8693
+ if (extractCdPrefixDirs(rawCmd).length === 0) return filePath;
8694
+ return resolveIndexPath(filePath, cdPrefixCwd(rawCmd, cwd));
8901
8695
  }
8902
- function pipelineShapeFilter(cmd, cwd) {
8903
- if (hasUnquotedOperator(cmd, ["&&", "||", ";"])) {
8904
- const forSplit2 = cmd.replace(/\s2>(?:&1|\/dev\/null)/g, "");
8905
- if (hasBareBackgroundOrNewline(forSplit2)) return null;
8906
- const segments2 = splitShellSegments(forSplit2);
8907
- if (segments2.length >= 2) {
8908
- let recognizedCiCount = 0;
8909
- for (const segment of segments2) {
8910
- const cleaned = stripOutputPipeline(segment.trim());
8911
- if (cleaned.length === 0) continue;
8912
- if (isCiBuildTestSegment(cleaned, cwd)) {
8913
- recognizedCiCount++;
8914
- }
8915
- }
8916
- if (recognizedCiCount > 0) {
8917
- return filterByName("generic-ci");
8696
+ function cdPrefixCwd(rawCmd, cwd) {
8697
+ let dir = cwd;
8698
+ for (const target of extractCdPrefixDirs(rawCmd)) dir = resolveIndexPath(target, dir);
8699
+ return dir;
8700
+ }
8701
+ function stripOutputPipeline(cmd) {
8702
+ let inSingle = false;
8703
+ let inDouble = false;
8704
+ let cut = cmd.length;
8705
+ let backslashes = 0;
8706
+ for (let i = 0; i < cmd.length; i++) {
8707
+ const ch = cmd[i];
8708
+ if (ch === "\\") {
8709
+ backslashes++;
8710
+ continue;
8711
+ }
8712
+ const escaped = backslashes % 2 === 1;
8713
+ backslashes = 0;
8714
+ if (ch === "'" && !inDouble) {
8715
+ if (!escaped) inSingle = !inSingle;
8716
+ } else if (ch === '"' && !inSingle) {
8717
+ if (!escaped) inDouble = !inDouble;
8718
+ } else if (ch === "|" && !inSingle && !inDouble) {
8719
+ if (cmd[i + 1] === "|") {
8720
+ i++;
8721
+ continue;
8918
8722
  }
8723
+ cut = i;
8724
+ break;
8725
+ }
8726
+ }
8727
+ let base = cmd.slice(0, cut);
8728
+ let prev;
8729
+ do {
8730
+ prev = base;
8731
+ const masked = base.replace(/"((?:[^"\\]|\\.)*)"/g, (_m, inner) => '"' + " ".repeat(inner.length) + '"').replace(/'([^']*)'/g, (_m, inner) => "'" + " ".repeat(inner.length) + "'");
8732
+ const newMasked = masked.replace(/\s*(?:[0-9]*>&[0-9]+|[0-9&]*>>?\s*(?:"[^"]*"|'[^']*'|[^\s|]+))\s*$/, "");
8733
+ if (newMasked.length < masked.length) {
8734
+ base = base.slice(0, newMasked.length);
8919
8735
  }
8736
+ } while (base !== prev);
8737
+ return base.trim();
8738
+ }
8739
+ function pipelineDivergenceNote(cmd, entryCommand) {
8740
+ if (entryCommand === cmd) return "";
8741
+ const preview = entryCommand.length > 60 ? entryCommand.slice(0, 57) + "..." : entryCommand;
8742
+ return " (cached from a differently-piped run, `" + preview + "` \u2014 verify it covers what you need before trusting it)";
8743
+ }
8744
+ function extractCommand(event) {
8745
+ const cmd = event.toolInput["command"];
8746
+ return typeof cmd === "string" && cmd.trim() !== "" ? cmd.trim() : void 0;
8747
+ }
8748
+ var HEAD_MOVING_GIT_RE = /^\s*git\s+(?:checkout|switch|pull|merge|rebase|reset|cherry-pick)\b/i;
8749
+ var PATH_SCOPED_CHECKOUT_RE = /^\s*git\s+checkout\s+(?:\S.*?\s)?--(?:\s|$)/i;
8750
+ function isHeadMovingGitCommand(cmd) {
8751
+ if (!HEAD_MOVING_GIT_RE.test(cmd)) return false;
8752
+ if (PATH_SCOPED_CHECKOUT_RE.test(cmd)) return false;
8753
+ return true;
8754
+ }
8755
+ var ORIG_HEAD_ELIGIBLE_GIT_RE = /^\s*git\s+(?:pull|merge|rebase)\b/i;
8756
+ var ORIG_HEAD_REFLOG_MSG_RE = /^(merge\s|rebase\s\(|pull\s)/i;
8757
+ var GIT_RESTORE_RE = /^\s*git\s+restore\b/i;
8758
+ var GIT_RESTORE_STAGED_ONLY_RE = /(?:^|\s)(?:--staged|-S)(?:\s|$)/;
8759
+ var GIT_STASH_APPLY_RE = /^\s*git\s+stash\s+(?:pop|apply)\b/i;
8760
+ var PATCH_APPLY_SEGMENT_RE = /^\s*(?:\S*[/\\])?(?:git\s+apply|patch)\b/i;
8761
+ var PATCH_APPLY_INSPECT_ONLY_RE = /(?:^|\s)--(?:check|stat|numstat|summary|dry-run)(?:\s|=|$)/i;
8762
+ function isFormatterWriteSegment(segment) {
8763
+ return /(?:^|\s|[/\\])(?:prettier|eslint)\b/i.test(segment) && /(?:^|\s)--(?:write|fix)(?:\s|$)/i.test(segment);
8764
+ }
8765
+ function safeShlexSplit(segment) {
8766
+ try {
8767
+ return shlexSplit(segment);
8768
+ } catch {
8920
8769
  return null;
8921
8770
  }
8922
- const forSplit = cmd.replace(/\s2>(?:&1|\/dev\/null)/g, "");
8923
- if (hasBareBackgroundOrNewline(forSplit)) return null;
8924
- const segments = splitShellSegments(forSplit);
8925
- if (segments.length < 2) return null;
8926
- for (const segment of segments.slice(1)) {
8927
- const head = safeShlexSplit(segment)?.[0];
8928
- if (head === void 0) return null;
8929
- if (!PIPELINE_PASSTHROUGH_HEADS.has(head.replace(/^.*[/\\]/, ""))) return null;
8771
+ }
8772
+ function segmentCommandIs(segment, name2) {
8773
+ return new RegExp("^\\s*(?:\\S*[/\\\\])?" + name2 + "(?:\\.exe)?\\b", "i").test(segment);
8774
+ }
8775
+ function extractSedInPlaceFiles(segment) {
8776
+ if (!segmentCommandIs(segment, "sed")) return [];
8777
+ const tokens = safeShlexSplit(segment);
8778
+ if (tokens === null) return [];
8779
+ const rest = tokens.slice(1);
8780
+ const inPlaceIdx = rest.findIndex((t) => t === "--in-place" || t.startsWith("--in-place=") || /^-[a-zA-Z]*i/.test(t));
8781
+ if (inPlaceIdx === -1) return [];
8782
+ const files = [];
8783
+ let scriptTaken = rest.some((t) => t === "-e" || t === "-f" || t.startsWith("--expression") || t.startsWith("--file"));
8784
+ for (let i = 0; i < rest.length; i++) {
8785
+ const t = rest[i];
8786
+ if (i === inPlaceIdx) {
8787
+ if (t === "-i" && rest[i + 1] === "") i++;
8788
+ continue;
8789
+ }
8790
+ if (t === "-e" || t === "-f" || t === "--expression" || t === "--file") {
8791
+ i++;
8792
+ continue;
8793
+ }
8794
+ if (t.length > 1 && t.startsWith("-")) continue;
8795
+ if (!scriptTaken) {
8796
+ scriptTaken = true;
8797
+ continue;
8798
+ }
8799
+ files.push(t);
8800
+ }
8801
+ return files;
8802
+ }
8803
+ function extractTeeFiles(segment) {
8804
+ if (!segmentCommandIs(segment, "tee")) return [];
8805
+ const tokens = safeShlexSplit(segment);
8806
+ if (tokens === null) return [];
8807
+ return tokens.slice(1).filter((t) => t.length > 0 && !t.startsWith("-"));
8808
+ }
8809
+ function extractRedirectTargets(segment) {
8810
+ const targets = [];
8811
+ const re = /(?:^|\s)[0-9]?>>?\s*("[^"]*"|'[^']*'|[^\s|&<>]+)/g;
8812
+ let m;
8813
+ while ((m = re.exec(segment)) !== null) {
8814
+ const raw = m[1];
8815
+ targets.push(raw.length >= 2 && (raw[0] === '"' || raw[0] === "'") ? raw.slice(1, -1) : raw);
8816
+ }
8817
+ return targets;
8818
+ }
8819
+ function extractGitRestorePathspecs(cmd) {
8820
+ const m = /^\s*git\s+restore\b(.*)$/i.exec(cmd);
8821
+ if (m === null) return [];
8822
+ const tokens = safeShlexSplit(m[1] ?? "");
8823
+ if (tokens === null) return [];
8824
+ const dashDash = tokens.indexOf("--");
8825
+ if (dashDash !== -1) return tokens.slice(dashDash + 1);
8826
+ const specs = [];
8827
+ let skipNext = false;
8828
+ for (const t of tokens) {
8829
+ if (skipNext) {
8830
+ skipNext = false;
8831
+ continue;
8832
+ }
8833
+ if (t === "-s" || t === "--source") {
8834
+ skipNext = true;
8835
+ continue;
8836
+ }
8837
+ if (t.startsWith("-")) continue;
8838
+ specs.push(t);
8839
+ }
8840
+ return specs;
8841
+ }
8842
+ function gitRepoRoot(gitDir) {
8843
+ const toplevel = runGit(["rev-parse", "--show-toplevel"], { cwd: gitDir, timeoutMs: 5e3 });
8844
+ return toplevel.exitCode === 0 && toplevel.stdout.trim() !== "" ? toplevel.stdout.trim() : gitDir;
8845
+ }
8846
+ function expandGitPathspecs(specs, gitDir) {
8847
+ if (specs.length === 0) return [];
8848
+ const listed = runGit(["ls-files", "--full-name", "--", ...specs], { cwd: gitDir, timeoutMs: 5e3 });
8849
+ if (listed.exitCode !== 0) return [];
8850
+ const repoRoot = gitRepoRoot(gitDir);
8851
+ return listed.stdout.split("\n").map((l) => l.trim()).filter((l) => l.length > 0).map((rel) => resolveIndexPath(rel, repoRoot));
8852
+ }
8853
+ function workingTreeStatusPaths(gitDir) {
8854
+ const status = runGit(["status", "--porcelain"], { cwd: gitDir, timeoutMs: 5e3 });
8855
+ if (status.exitCode !== 0) return [];
8856
+ const repoRoot = gitRepoRoot(gitDir);
8857
+ const out = [];
8858
+ for (const line of status.stdout.split("\n")) {
8859
+ if (line.length < 4) continue;
8860
+ let rel = line.slice(3).trim();
8861
+ if (rel.length === 0) continue;
8862
+ const arrow = rel.lastIndexOf(" -> ");
8863
+ if (arrow !== -1) rel = rel.slice(arrow + 4);
8864
+ if (rel.startsWith('"')) {
8865
+ try {
8866
+ rel = JSON.parse(rel);
8867
+ } catch {
8868
+ continue;
8869
+ }
8870
+ }
8871
+ out.push(resolveIndexPath(rel, repoRoot));
8872
+ }
8873
+ return out;
8874
+ }
8875
+ function enqueueRewrittenPath(absPath) {
8876
+ if (/(?:^|[/\\])(?:NUL|nul)$/.test(absPath) || absPath.replace(/\\/g, "/").endsWith("/dev/null")) return;
8877
+ try {
8878
+ if (!statSync5(absPath).isFile()) return;
8879
+ } catch {
8880
+ return;
8881
+ }
8882
+ enqueueDirtyPathSafe(absPath, { alreadyResolved: true });
8883
+ }
8884
+ function enqueueNonHeadMovingRewrites(cmd, rawCmd, cwd) {
8885
+ const atCwd = (f) => resolveIndexPath(resolveCdHintPath(rawCmd, f, cwd), cwd);
8886
+ const paths = [];
8887
+ let needsStatusSweep = GIT_STASH_APPLY_RE.test(cmd);
8888
+ if (GIT_RESTORE_RE.test(cmd) && !GIT_RESTORE_STAGED_ONLY_RE.test(cmd)) {
8889
+ paths.push(...expandGitPathspecs(extractGitRestorePathspecs(cmd), cwd));
8890
+ }
8891
+ for (const segment of splitShellSegments(cmd)) {
8892
+ if (PATCH_APPLY_SEGMENT_RE.test(segment) && !PATCH_APPLY_INSPECT_ONLY_RE.test(segment)) needsStatusSweep = true;
8893
+ if (isFormatterWriteSegment(segment)) needsStatusSweep = true;
8894
+ for (const f of extractSedInPlaceFiles(segment)) paths.push(atCwd(f));
8895
+ for (const f of extractTeeFiles(segment)) paths.push(atCwd(f));
8896
+ for (const f of extractRedirectTargets(segment)) paths.push(atCwd(f));
8897
+ }
8898
+ if (needsStatusSweep) paths.push(...workingTreeStatusPaths(cwd));
8899
+ for (const p of paths) enqueueRewrittenPath(p);
8900
+ }
8901
+ var PIPELINE_PASSTHROUGH_HEADS = /* @__PURE__ */ new Set(["head", "tail", "cat", "tee", "less", "more"]);
8902
+ var CI_FILTER_NAMES = /^(?:generic-ci|jest|vitest|pytest|go_test|cargo_test|cargo|go|make|cmake|gradle|maven|dotnet|turbo|nx|lerna|webpack|eslint|ruff|clippy|flake8|mypy|prettier|tsc)$/;
8903
+ function isCiBuildTestSegment(cleaned, cwd) {
8904
+ if (isTestRunnerCommand(cleaned) || isBuildCommand(cleaned) || isTscCommand(cleaned)) return true;
8905
+ if (/^\s*(npm|pnpm|yarn|bun)\s+(run\s+)?(test|build|lint|typecheck|check|ci|guards)\b/i.test(cleaned)) return true;
8906
+ if (/^\s*(cargo|go|dotnet|make|gradle|mvn|pytest|vitest|jest|eslint|ruff)\b/i.test(cleaned)) return true;
8907
+ const detected = detectFromCommand(cleaned, cwd ?? void 0);
8908
+ if (detected !== null && CI_FILTER_NAMES.test(detected.filter.name)) return true;
8909
+ return false;
8910
+ }
8911
+ function pipelineShapeFilter(cmd, cwd) {
8912
+ if (hasUnquotedOperator(cmd, ["&&", "||", ";"])) {
8913
+ const forSplit2 = cmd.replace(/\s2>(?:&1|\/dev\/null)/g, "");
8914
+ if (hasBareBackgroundOrNewline(forSplit2)) return null;
8915
+ const segments2 = splitShellSegments(forSplit2);
8916
+ if (segments2.length >= 2) {
8917
+ let recognizedCiCount = 0;
8918
+ for (const segment of segments2) {
8919
+ const cleaned = stripOutputPipeline(segment.trim());
8920
+ if (cleaned.length === 0) continue;
8921
+ if (isCiBuildTestSegment(cleaned, cwd)) {
8922
+ recognizedCiCount++;
8923
+ }
8924
+ }
8925
+ if (recognizedCiCount > 0) {
8926
+ return filterByName("generic-ci");
8927
+ }
8928
+ }
8929
+ return null;
8930
+ }
8931
+ const forSplit = cmd.replace(/\s2>(?:&1|\/dev\/null)/g, "");
8932
+ if (hasBareBackgroundOrNewline(forSplit)) return null;
8933
+ const segments = splitShellSegments(forSplit);
8934
+ if (segments.length < 2) return null;
8935
+ for (const segment of segments.slice(1)) {
8936
+ const head = safeShlexSplit(segment)?.[0];
8937
+ if (head === void 0) return null;
8938
+ if (!PIPELINE_PASSTHROUGH_HEADS.has(head.replace(/^.*[/\\]/, ""))) return null;
8939
+ }
8940
+ const detected = detectFromCommand(stripOutputPipeline(cmd), cwd ?? void 0);
8941
+ return detected === null ? null : detected.filter;
8942
+ }
8943
+ function pureFileReadPath(cmd) {
8944
+ const single = extractCatFile(cmd)?.filePath ?? extractHeadFile(cmd)?.filePath ?? extractTailFile(cmd)?.filePath ?? extractLineRangeRead(cmd)?.filePath;
8945
+ if (single !== void 0) return single;
8946
+ return singleFileCompoundReadPath(cmd);
8947
+ }
8948
+ function singleFileCompoundReadPath(cmd) {
8949
+ const reads = extractLineRangeReadsCompound(cmd);
8950
+ if (reads === null || reads.length !== 1) return null;
8951
+ return reads[0]?.filePath ?? null;
8952
+ }
8953
+ function deliveredLineNumbers(cmd, lineCount) {
8954
+ const ranged = extractLineRangeRead(cmd);
8955
+ if (ranged !== null) {
8956
+ const nums = [];
8957
+ for (const [lo, hi] of ranged.ranges) for (let n = lo; n <= hi; n++) nums.push(n);
8958
+ if (nums.length === 0) return null;
8959
+ while (nums.length < lineCount) nums.push((nums[nums.length - 1] ?? 0) + 1);
8960
+ return nums;
8961
+ }
8962
+ if (extractCatFile(cmd) !== null || extractHeadFile(cmd) !== null) return Array.from({ length: lineCount }, (_, i) => i + 1);
8963
+ if (singleFileCompoundReadPath(cmd) !== null) return Array.from({ length: lineCount }, () => null);
8964
+ return null;
8965
+ }
8966
+ var WHOLE_FILE_DUMP_RE = /^(?:(?:cat|type|Get-Content|gc)|(?:head|tail)[ \t]+-n[ \t]+\d+)[ \t]+(?:"([A-Za-z0-9._\-/\\:+@ ]+)"|'([A-Za-z0-9._\-/\\:+@ ]+)'|([A-Za-z0-9._\-/\\:+@]+))(?:[ \t]+2>(?:&1|\/dev\/null))?[ \t]*$/i;
8967
+ function isWholeFileDump(cmd, extractedPath) {
8968
+ const m = WHOLE_FILE_DUMP_RE.exec(cmd);
8969
+ if (m === null) return false;
8970
+ const shaped = m[1] ?? m[2] ?? m[3];
8971
+ return shaped !== void 0 && shaped === extractedPath;
8972
+ }
8973
+ function unwrapCompressCommand(executed) {
8974
+ const t = executed.trim();
8975
+ if (!/^(?:token-goat|tg)\s+compress\b/.test(t)) return null;
8976
+ let argv;
8977
+ try {
8978
+ argv = shlexSplit(t);
8979
+ } catch {
8980
+ return null;
8981
+ }
8982
+ for (let i = 0; i + 1 < argv.length; i++) {
8983
+ const tok = argv[i];
8984
+ if (tok === "-c" || tok === "--cmd") return argv[i + 1] ?? null;
8985
+ if (tok === "--cmd-b64") {
8986
+ try {
8987
+ return Buffer.from(argv[i + 1] ?? "", "base64").toString("utf8");
8988
+ } catch {
8989
+ return null;
8990
+ }
8991
+ }
8992
+ }
8993
+ return null;
8994
+ }
8995
+ function detectUnbalancedShellSyntax(cmd) {
8996
+ let inSingle = false;
8997
+ let inDouble = false;
8998
+ let i = 0;
8999
+ while (i < cmd.length) {
9000
+ const ch = cmd[i];
9001
+ if (inSingle) {
9002
+ if (ch === "'") {
9003
+ inSingle = false;
9004
+ }
9005
+ i++;
9006
+ continue;
9007
+ }
9008
+ if (inDouble) {
9009
+ if (ch === "\\" && i + 1 < cmd.length) {
9010
+ i += 2;
9011
+ continue;
9012
+ }
9013
+ if (ch === '"') {
9014
+ inDouble = false;
9015
+ }
9016
+ i++;
9017
+ continue;
9018
+ }
9019
+ if (ch === "\\" && i + 1 < cmd.length) {
9020
+ i += 2;
9021
+ continue;
9022
+ }
9023
+ if (ch === "$" && cmd[i + 1] === "(" && cmd[i + 2] === "(") {
9024
+ let depth = 2;
9025
+ let j = i + 3;
9026
+ while (j < cmd.length && depth > 0) {
9027
+ if (cmd[j] === "(") depth++;
9028
+ else if (cmd[j] === ")") depth--;
9029
+ j++;
9030
+ }
9031
+ i = j;
9032
+ continue;
9033
+ }
9034
+ if (ch === "$" && cmd[i + 1] === "'") {
9035
+ let j = i + 2;
9036
+ let closed = false;
9037
+ while (j < cmd.length) {
9038
+ if (cmd[j] === "\\") {
9039
+ j += 2;
9040
+ continue;
9041
+ }
9042
+ if (cmd[j] === "'") {
9043
+ j++;
9044
+ closed = true;
9045
+ break;
9046
+ }
9047
+ j++;
9048
+ }
9049
+ if (!closed) {
9050
+ inSingle = true;
9051
+ break;
9052
+ }
9053
+ i = j;
9054
+ continue;
9055
+ }
9056
+ if (ch === "#" && (i === 0 || /\s/.test(cmd[i - 1] ?? ""))) {
9057
+ const nl2 = cmd.indexOf("\n", i);
9058
+ i = nl2 === -1 ? cmd.length : nl2 + 1;
9059
+ continue;
9060
+ }
9061
+ if (ch === "'") {
9062
+ inSingle = true;
9063
+ i++;
9064
+ continue;
9065
+ }
9066
+ if (ch === '"') {
9067
+ inDouble = true;
9068
+ i++;
9069
+ continue;
9070
+ }
9071
+ if (ch === "<" && i + 1 < cmd.length && cmd[i + 1] === "<") {
9072
+ if (i + 2 < cmd.length && cmd[i + 2] === "<") {
9073
+ i += 3;
9074
+ continue;
9075
+ }
9076
+ let j = i + 2;
9077
+ let hasIndentModifier = false;
9078
+ if (j < cmd.length && (cmd[j] === "-" || cmd[j] === "~")) {
9079
+ hasIndentModifier = true;
9080
+ j++;
9081
+ }
9082
+ while (j < cmd.length && cmd[j] === " ") {
9083
+ j++;
9084
+ }
9085
+ if (j < cmd.length) {
9086
+ let delimStart = j;
9087
+ if (cmd[j] === '"' || cmd[j] === "'") {
9088
+ delimStart = j + 1;
9089
+ const quoteChar = cmd[j];
9090
+ let delimEnd = delimStart;
9091
+ while (delimEnd < cmd.length && cmd[delimEnd] !== quoteChar) {
9092
+ delimEnd++;
9093
+ }
9094
+ if (delimEnd >= cmd.length) {
9095
+ return "an unclosed quote in a heredoc opener";
9096
+ }
9097
+ j = delimEnd + 1;
9098
+ } else {
9099
+ let delimEnd = delimStart;
9100
+ while (delimEnd < cmd.length && /\w/.test(cmd.charAt(delimEnd))) {
9101
+ delimEnd++;
9102
+ }
9103
+ j = delimEnd;
9104
+ }
9105
+ const delimiter = cmd.slice(delimStart, j).replace(/["']/g, "");
9106
+ if (delimiter) {
9107
+ let scanPos = j;
9108
+ let terminatorEnd = -1;
9109
+ while (scanPos <= cmd.length) {
9110
+ const nl2 = cmd.indexOf("\n", scanPos);
9111
+ const lineEnd = nl2 === -1 ? cmd.length : nl2;
9112
+ let line = cmd.slice(scanPos, lineEnd);
9113
+ if (line.endsWith("\r")) {
9114
+ line = line.slice(0, -1);
9115
+ }
9116
+ const isMatch = hasIndentModifier ? line.trim() === delimiter : line === delimiter;
9117
+ if (isMatch) {
9118
+ terminatorEnd = nl2 === -1 ? cmd.length : nl2 + 1;
9119
+ break;
9120
+ }
9121
+ if (nl2 === -1) break;
9122
+ scanPos = nl2 + 1;
9123
+ }
9124
+ if (terminatorEnd === -1) {
9125
+ return `an unterminated heredoc (${delimiter} never appears on its own line)`;
9126
+ }
9127
+ i = terminatorEnd;
9128
+ continue;
9129
+ }
9130
+ }
9131
+ }
9132
+ i++;
9133
+ }
9134
+ if (inSingle) {
9135
+ return "an unclosed single quote";
9136
+ }
9137
+ if (inDouble) {
9138
+ return "an unclosed double quote";
8930
9139
  }
8931
- const detected = detectFromCommand(stripOutputPipeline(cmd), cwd ?? void 0);
8932
- return detected === null ? null : detected.filter;
8933
- }
8934
- function pureFileReadPath(cmd) {
8935
- const single = extractCatFile(cmd)?.filePath ?? extractHeadFile(cmd)?.filePath ?? extractTailFile(cmd)?.filePath ?? extractLineRangeRead(cmd)?.filePath;
8936
- if (single !== void 0) return single;
8937
- return singleFileCompoundReadPath(cmd);
8938
- }
8939
- function singleFileCompoundReadPath(cmd) {
8940
- const reads = extractLineRangeReadsCompound(cmd);
8941
- if (reads === null || reads.length !== 1) return null;
8942
- return reads[0]?.filePath ?? null;
9140
+ return null;
8943
9141
  }
8944
- function deliveredLineNumbers(cmd, lineCount) {
8945
- const ranged = extractLineRangeRead(cmd);
8946
- if (ranged !== null) {
8947
- const nums = [];
8948
- for (const [lo, hi] of ranged.ranges) for (let n = lo; n <= hi; n++) nums.push(n);
8949
- if (nums.length === 0) return null;
8950
- while (nums.length < lineCount) nums.push((nums[nums.length - 1] ?? 0) + 1);
8951
- return nums;
9142
+
9143
+ // src/hooks_bash.ts
9144
+ function maybeCompressRewrite(event, rawCmd, cmd) {
9145
+ if (process.env["TOKEN_GOAT_BASH_COMPRESS"] === "0") return null;
9146
+ if (event.raw["_tg_harness"] === "vscode") return null;
9147
+ let cfg;
9148
+ try {
9149
+ cfg = loadConfig().bash_compress;
9150
+ } catch {
9151
+ return null;
8952
9152
  }
8953
- if (extractCatFile(cmd) !== null || extractHeadFile(cmd) !== null) return Array.from({ length: lineCount }, (_, i) => i + 1);
8954
- if (singleFileCompoundReadPath(cmd) !== null) return Array.from({ length: lineCount }, () => null);
8955
- return null;
9153
+ if (!cfg.enabled) return null;
9154
+ if (!canRunWrappedShell()) return null;
9155
+ const gateCmd = stripTrailingStderrRedirect(cmd);
9156
+ const detected = detectFromCommand(gateCmd, getCwd(event));
9157
+ let filterName;
9158
+ if (detected !== null) {
9159
+ filterName = detected.filter.name;
9160
+ } else if (isCompressibleSingleCommand(gateCmd)) {
9161
+ filterName = "generic";
9162
+ } else {
9163
+ return null;
9164
+ }
9165
+ if (cfg.disabled_filters.includes(filterName)) return null;
9166
+ const wrapped = `token-goat compress -f ${filterName} --timeout ${cfg.timeout_seconds} -c ${shellQuoteSingle(rawCmd)}`;
9167
+ return { hookType: "rewriteInput", updatedInput: { ...event.toolInput, command: wrapped } };
8956
9168
  }
8957
9169
  function elideServedShellLines(cmd, output, priorIds, unknownLineNumbers = false) {
8958
9170
  if (priorIds.length === 0) return null;
@@ -9006,19 +9218,12 @@ function foldShellReadBodies(cmd, output, fileKey, cwd) {
9006
9218
  if (folded === null) return null;
9007
9219
  return { text: folded.numbered.join("\n"), folds: folded.folds };
9008
9220
  }
9009
- var WHOLE_FILE_DUMP_RE = /^(?:(?:cat|type|Get-Content|gc)|(?:head|tail)[ \t]+-n[ \t]+\d+)[ \t]+(?:"([A-Za-z0-9._\-/\\:+@ ]+)"|'([A-Za-z0-9._\-/\\:+@ ]+)'|([A-Za-z0-9._\-/\\:+@]+))(?:[ \t]+2>(?:&1|\/dev\/null))?[ \t]*$/i;
9010
- function isWholeFileDump(cmd, extractedPath) {
9011
- const m = WHOLE_FILE_DUMP_RE.exec(cmd);
9012
- if (m === null) return false;
9013
- const shaped = m[1] ?? m[2] ?? m[3];
9014
- return shaped !== void 0 && shaped === extractedPath;
9015
- }
9016
9221
  function foldShellReadStructure(cmd, filePath, output, fileKey, cwd) {
9017
9222
  if (!isWholeFileDump(cmd, filePath)) return null;
9018
9223
  const originalBytes = Buffer.byteLength(output, "utf-8");
9019
9224
  let onDisk;
9020
9225
  try {
9021
- onDisk = statSync4(fileKey).size;
9226
+ onDisk = statSync6(fileKey).size;
9022
9227
  } catch {
9023
9228
  return null;
9024
9229
  }
@@ -9123,9 +9328,9 @@ async function maybeElideServedGenericOutput(cmd, output, exitCode, cwd, cacheMi
9123
9328
  if (rewrittenText === null) return null;
9124
9329
  return emitRewrite(rewrittenText, "already-served shell output collapsed", { kind: "bash_compress:generic-served-elision", originalBytes: deliveredOutputBytes(originalBytes) });
9125
9330
  }
9126
- async function maybeCompressCompoundOutput(cmd, output, exitCode, cwd, cacheMinBytes) {
9331
+ async function maybeCompressCompoundOutput(cmd, output, exitCode, cwd, cacheMinBytes, isUnwrapped = false) {
9127
9332
  if (process.env["TOKEN_GOAT_BASH_COMPRESS"] === "0") return null;
9128
- if (isCompressibleSingleCommand(cmd)) return null;
9333
+ if (!isUnwrapped && isCompressibleSingleCommand(cmd)) return null;
9129
9334
  if (exitCode !== null && exitCode !== 0) return null;
9130
9335
  let cfg;
9131
9336
  try {
@@ -9135,7 +9340,7 @@ async function maybeCompressCompoundOutput(cmd, output, exitCode, cwd, cacheMinB
9135
9340
  }
9136
9341
  if (!cfg.enabled || cfg.disabled_filters.includes("generic")) return null;
9137
9342
  if (Buffer.byteLength(output, "utf-8") < cacheMinBytes) return null;
9138
- const shaped = pipelineShapeFilter(cmd, cwd);
9343
+ const shaped = pipelineShapeFilter(cmd, cwd) ?? (isUnwrapped ? detectFromCommand(cmd, cwd ?? void 0)?.filter ?? null : null);
9139
9344
  const filter3 = shaped !== null && !cfg.disabled_filters.includes(shaped.name) ? shaped : filterByName("generic");
9140
9345
  if (filter3 === null) return null;
9141
9346
  const compressed = compressOutput(filter3, output, "", exitCode ?? 0, [], {
@@ -9216,168 +9421,6 @@ function maybeFoldCurlHtml(cmd, output, id) {
9216
9421
  }
9217
9422
  return emitRewrite(notice + fenced, "curl HTML body cleaned", { kind: "bash_compress:curl-html", originalBytes: deliveredOutputBytes(originalBytes) });
9218
9423
  }
9219
- function unwrapCompressCommand(executed) {
9220
- const t = executed.trim();
9221
- if (!/^(?:token-goat|tg)\s+compress\b/.test(t)) return null;
9222
- let argv;
9223
- try {
9224
- argv = shlexSplit(t);
9225
- } catch {
9226
- return null;
9227
- }
9228
- for (let i = 0; i + 1 < argv.length; i++) {
9229
- const tok = argv[i];
9230
- if (tok === "-c" || tok === "--cmd") return argv[i + 1] ?? null;
9231
- }
9232
- return null;
9233
- }
9234
- function detectUnbalancedShellSyntax(cmd) {
9235
- let inSingle = false;
9236
- let inDouble = false;
9237
- let i = 0;
9238
- while (i < cmd.length) {
9239
- const ch = cmd[i];
9240
- if (inSingle) {
9241
- if (ch === "'") {
9242
- inSingle = false;
9243
- }
9244
- i++;
9245
- continue;
9246
- }
9247
- if (inDouble) {
9248
- if (ch === "\\" && i + 1 < cmd.length) {
9249
- i += 2;
9250
- continue;
9251
- }
9252
- if (ch === '"') {
9253
- inDouble = false;
9254
- }
9255
- i++;
9256
- continue;
9257
- }
9258
- if (ch === "\\" && i + 1 < cmd.length) {
9259
- i += 2;
9260
- continue;
9261
- }
9262
- if (ch === "$" && cmd[i + 1] === "(" && cmd[i + 2] === "(") {
9263
- let depth = 2;
9264
- let j = i + 3;
9265
- while (j < cmd.length && depth > 0) {
9266
- if (cmd[j] === "(") depth++;
9267
- else if (cmd[j] === ")") depth--;
9268
- j++;
9269
- }
9270
- i = j;
9271
- continue;
9272
- }
9273
- if (ch === "$" && cmd[i + 1] === "'") {
9274
- let j = i + 2;
9275
- let closed = false;
9276
- while (j < cmd.length) {
9277
- if (cmd[j] === "\\") {
9278
- j += 2;
9279
- continue;
9280
- }
9281
- if (cmd[j] === "'") {
9282
- j++;
9283
- closed = true;
9284
- break;
9285
- }
9286
- j++;
9287
- }
9288
- if (!closed) {
9289
- inSingle = true;
9290
- break;
9291
- }
9292
- i = j;
9293
- continue;
9294
- }
9295
- if (ch === "#" && (i === 0 || /\s/.test(cmd[i - 1] ?? ""))) {
9296
- const nl2 = cmd.indexOf("\n", i);
9297
- i = nl2 === -1 ? cmd.length : nl2 + 1;
9298
- continue;
9299
- }
9300
- if (ch === "'") {
9301
- inSingle = true;
9302
- i++;
9303
- continue;
9304
- }
9305
- if (ch === '"') {
9306
- inDouble = true;
9307
- i++;
9308
- continue;
9309
- }
9310
- if (ch === "<" && i + 1 < cmd.length && cmd[i + 1] === "<") {
9311
- if (i + 2 < cmd.length && cmd[i + 2] === "<") {
9312
- i += 3;
9313
- continue;
9314
- }
9315
- let j = i + 2;
9316
- let hasIndentModifier = false;
9317
- if (j < cmd.length && (cmd[j] === "-" || cmd[j] === "~")) {
9318
- hasIndentModifier = true;
9319
- j++;
9320
- }
9321
- while (j < cmd.length && cmd[j] === " ") {
9322
- j++;
9323
- }
9324
- if (j < cmd.length) {
9325
- let delimStart = j;
9326
- if (cmd[j] === '"' || cmd[j] === "'") {
9327
- delimStart = j + 1;
9328
- const quoteChar = cmd[j];
9329
- let delimEnd = delimStart;
9330
- while (delimEnd < cmd.length && cmd[delimEnd] !== quoteChar) {
9331
- delimEnd++;
9332
- }
9333
- if (delimEnd >= cmd.length) {
9334
- return "an unclosed quote in a heredoc opener";
9335
- }
9336
- j = delimEnd + 1;
9337
- } else {
9338
- let delimEnd = delimStart;
9339
- while (delimEnd < cmd.length && /\w/.test(cmd.charAt(delimEnd))) {
9340
- delimEnd++;
9341
- }
9342
- j = delimEnd;
9343
- }
9344
- const delimiter = cmd.slice(delimStart, j).replace(/["']/g, "");
9345
- if (delimiter) {
9346
- let scanPos = j;
9347
- let terminatorEnd = -1;
9348
- while (scanPos <= cmd.length) {
9349
- const nl2 = cmd.indexOf("\n", scanPos);
9350
- const lineEnd = nl2 === -1 ? cmd.length : nl2;
9351
- let line = cmd.slice(scanPos, lineEnd);
9352
- if (line.endsWith("\r")) {
9353
- line = line.slice(0, -1);
9354
- }
9355
- const isMatch = hasIndentModifier ? line.trim() === delimiter : line === delimiter;
9356
- if (isMatch) {
9357
- terminatorEnd = nl2 === -1 ? cmd.length : nl2 + 1;
9358
- break;
9359
- }
9360
- if (nl2 === -1) break;
9361
- scanPos = nl2 + 1;
9362
- }
9363
- if (terminatorEnd === -1) {
9364
- return `an unterminated heredoc (${delimiter} never appears on its own line)`;
9365
- }
9366
- i = terminatorEnd;
9367
- continue;
9368
- }
9369
- }
9370
- }
9371
- i++;
9372
- }
9373
- if (inSingle) {
9374
- return "an unclosed single quote";
9375
- }
9376
- if (inDouble) {
9377
- return "an unclosed double quote";
9378
- }
9379
- return null;
9380
- }
9381
9424
  function preBashHandlerInner(event) {
9382
9425
  const rawCmd = extractCommand(event);
9383
9426
  if (rawCmd === void 0) return passOutput();
@@ -9408,7 +9451,7 @@ function preBashHandlerInner(event) {
9408
9451
  }
9409
9452
  const toolResults = extractToolResultsFile(cmd);
9410
9453
  if (toolResults !== null) {
9411
- const { path: outPath } = toolResults;
9454
+ const outPath = displaySafePath(toolResults.path);
9412
9455
  recordStat("session_hint", 0, 0);
9413
9456
  return contextOutput(
9414
9457
  "Tool output " + outPath + ' is a plain-text artifact. Use `token-goat bash-output --file "' + outPath + '"` to read it with surgical narrowing via `--grep PATTERN` or `--tail N`, instead of reading the whole file.'
@@ -9440,6 +9483,15 @@ function preBashHandlerInner(event) {
9440
9483
  "Use `token-goat outline <file>` to see symbol names and line counts without loading files."
9441
9484
  );
9442
9485
  }
9486
+ const terminalXml = extractTerminalXmlParsing(cmd);
9487
+ if (terminalXml !== null) {
9488
+ const { filePath, toolOrScript } = terminalXml;
9489
+ recordStat("session_hint", 0, 0);
9490
+ const target = filePath ? displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath) : "<file>";
9491
+ return contextOutput(
9492
+ `token-goat available for this file type, consider 'token-goat xml-query "${target}" "<xpath>"' or 'token-goat xml-outline "${target}"' first instead of terminal XML parsing (${toolOrScript}).`
9493
+ );
9494
+ }
9443
9495
  const singleLineRangeRead = extractLineRangeRead(cmd);
9444
9496
  const sedReads = singleLineRangeRead !== null ? [singleLineRangeRead] : extractLineRangeReadsCompound(cmd);
9445
9497
  if (sedReads !== null) {
@@ -9466,17 +9518,17 @@ function preBashHandlerInner(event) {
9466
9518
  }
9467
9519
  const gcTailResult = extractGetContentTail(cmd);
9468
9520
  if (gcTailResult !== null) {
9469
- const { filePath, isDoc, isConfig, isSql } = gcTailResult;
9521
+ const { filePath, isDoc, isConfig, isSql, isXml } = gcTailResult;
9470
9522
  const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
9471
9523
  recordStat("session_hint", 0, 0);
9472
- return contextOutput("`Get-Content -Tail` bypasses read hooks. " + surgicalHintForConfigDoc(hintPath, isConfig, isDoc, isSql));
9524
+ return contextOutput("`Get-Content -Tail` bypasses read hooks. " + surgicalHintForConfigDoc(hintPath, isConfig, isDoc, isSql, isXml));
9473
9525
  }
9474
9526
  const gcSelectResult = extractGetContentSelectFirst(cmd);
9475
9527
  if (gcSelectResult !== null) {
9476
- const { filePath, isDoc, isConfig, isSql, n } = gcSelectResult;
9528
+ const { filePath, isDoc, isConfig, isSql, isXml, n } = gcSelectResult;
9477
9529
  const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
9478
9530
  recordStat("session_hint", 0, 0);
9479
- return contextOutput(leadingLinesHint("`Select-Object -First` bypasses read hooks. ", hintPath, 1, n, { isConfig, isDoc, isSql }, preHookCwd));
9531
+ return contextOutput(leadingLinesHint("`Select-Object -First` bypasses read hooks. ", hintPath, 1, n, { isConfig, isDoc, isSql, isXml }, preHookCwd));
9480
9532
  }
9481
9533
  const catJsonPipe = extractCatJsonPipe(cmd);
9482
9534
  if (catJsonPipe !== null) {
@@ -9489,7 +9541,7 @@ function preBashHandlerInner(event) {
9489
9541
  }
9490
9542
  const catResult = extractCatFile(cmd);
9491
9543
  if (catResult !== null) {
9492
- const { filePath, isDoc, isEnv, isConfig, isSql, cmd0, advisoryOnly } = catResult;
9544
+ const { filePath, isDoc, isEnv, isConfig, isSql, isXml, cmd0, advisoryOnly } = catResult;
9493
9545
  const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
9494
9546
  recordStat("session_hint", 0, 0);
9495
9547
  if (isSql) {
@@ -9497,16 +9549,16 @@ function preBashHandlerInner(event) {
9497
9549
  "`" + cmd0 + '` loads the entire file into context. Use `token-goat section "' + hintPath + '::table_name"` to pull one CREATE TABLE / CREATE TYPE block.'
9498
9550
  );
9499
9551
  }
9500
- const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc);
9552
+ const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc, isXml);
9501
9553
  return cdStripped || advisoryOnly ? contextOutput("`" + cmd0 + "` loads the entire file into context. " + hint) : denyOutput("`" + cmd0 + "` loads the entire file into context. " + hint);
9502
9554
  }
9503
9555
  const catMulti = extractCatFilesMulti(cmd);
9504
9556
  if (catMulti !== null) {
9505
9557
  recordStat("session_hint", 0, 0);
9506
9558
  const cmd0 = catMulti[0].cmd0;
9507
- const perPath = catMulti.map(({ filePath, isDoc, isEnv, isConfig, isSql }) => {
9559
+ const perPath = catMulti.map(({ filePath, isDoc, isEnv, isConfig, isSql, isXml }) => {
9508
9560
  const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
9509
- const how = isSql ? 'token-goat section "' + hintPath + '::table_name"' : isEnv || isConfig ? 'token-goat config-get "' + hintPath + '" KEY_NAME' : isDoc ? 'token-goat section "' + hintPath + '::SectionHeading"' : 'token-goat read "' + hintPath + '::SymbolName"';
9561
+ const how = isXml ? 'token-goat xml-outline "' + hintPath + '" or token-goat xml-query "' + hintPath + '" "<selector>"' : isSql ? 'token-goat section "' + hintPath + '::table_name"' : isEnv || isConfig ? 'token-goat config-get "' + hintPath + '" KEY_NAME' : isDoc ? 'token-goat section "' + hintPath + '::SectionHeading"' : 'token-goat read "' + hintPath + '::SymbolName"';
9510
9562
  return " " + hintPath + " -> `" + how + "`";
9511
9563
  });
9512
9564
  const msg = "`" + cmd0 + "` on multiple files loads them all into context. Read each surgically instead:\n" + perPath.join("\n");
@@ -9514,19 +9566,19 @@ function preBashHandlerInner(event) {
9514
9566
  }
9515
9567
  const psGetContentResult = extractPowerShellWrappedGetContent(cmd, event);
9516
9568
  if (psGetContentResult !== null) {
9517
- const { filePath, isDoc, isEnv, isConfig, isSql } = psGetContentResult;
9569
+ const { filePath, isDoc, isEnv, isConfig, isSql, isXml } = psGetContentResult;
9518
9570
  const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
9519
9571
  recordStat("session_hint", 0, 0);
9520
9572
  const lead = "`Get-Content` via a `powershell -Command` wrapper bypasses read hooks and loads the entire file into context. ";
9521
9573
  if (isSql) {
9522
9574
  return contextOutput(lead + 'Use `token-goat section "' + hintPath + '::table_name"` to pull one CREATE TABLE / CREATE TYPE block.');
9523
9575
  }
9524
- const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc);
9576
+ const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc, isXml);
9525
9577
  return cdStripped ? contextOutput(lead + hint) : denyOutput(lead + hint);
9526
9578
  }
9527
9579
  const wslCatResult = extractWslCatFile(cmd);
9528
9580
  if (wslCatResult !== null) {
9529
- const { filePath, isDoc, isEnv, isConfig, isSql } = wslCatResult;
9581
+ const { filePath, isDoc, isEnv, isConfig, isSql, isXml } = wslCatResult;
9530
9582
  const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
9531
9583
  recordStat("session_hint", 0, 0);
9532
9584
  if (isSql) {
@@ -9534,12 +9586,12 @@ function preBashHandlerInner(event) {
9534
9586
  '`cat` loads the entire file into context. Use `token-goat section "' + hintPath + '::table_name"` to pull one CREATE TABLE / CREATE TYPE block.'
9535
9587
  );
9536
9588
  }
9537
- const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc);
9589
+ const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc, isXml);
9538
9590
  return cdStripped ? contextOutput("`cat` loads the entire file into context. " + hint) : denyOutput("`cat` loads the entire file into context. " + hint);
9539
9591
  }
9540
9592
  const pyRead = extractPythonFileRead(cmd);
9541
9593
  if (pyRead !== null) {
9542
- const { filePath, isDoc, isConfig, isEnv, isSql, isOutputFile } = pyRead;
9594
+ const { filePath, isDoc, isConfig, isEnv, isSql, isXml, isOutputFile } = pyRead;
9543
9595
  const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
9544
9596
  recordStat("session_hint", 0, 0);
9545
9597
  if (isOutputFile) {
@@ -9556,22 +9608,22 @@ function preBashHandlerInner(event) {
9556
9608
  'Python `open()` file reads bypass read hooks. Use `token-goat section "' + hintPath + '::table_name"` to pull one CREATE TABLE / CREATE TYPE block.'
9557
9609
  );
9558
9610
  }
9559
- const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc);
9611
+ const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc, isXml);
9560
9612
  return cdStripped ? contextOutput("Python `open()` file reads bypass read hooks. " + hint) : denyOutput("Python `open()` file reads bypass read hooks. " + hint);
9561
9613
  }
9562
9614
  const tailResult = extractTailFile(cmd);
9563
9615
  if (tailResult !== null) {
9564
- const { filePath, isDoc, isConfig, isSql } = tailResult;
9616
+ const { filePath, isDoc, isConfig, isSql, isXml } = tailResult;
9565
9617
  const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
9566
9618
  recordStat("session_hint", 0, 0);
9567
- return contextOutput("`tail` bypasses read hooks. " + surgicalHintForConfigDoc(hintPath, isConfig, isDoc, isSql));
9619
+ return contextOutput("`tail` bypasses read hooks. " + surgicalHintForConfigDoc(hintPath, isConfig, isDoc, isSql, isXml));
9568
9620
  }
9569
9621
  const headResult = extractHeadFile(cmd);
9570
9622
  if (headResult !== null) {
9571
- const { filePath, isDoc, isConfig, isSql, n } = headResult;
9623
+ const { filePath, isDoc, isConfig, isSql, isXml, n } = headResult;
9572
9624
  const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
9573
9625
  recordStat("session_hint", 0, 0);
9574
- return contextOutput(leadingLinesHint("`head` bypasses read hooks. ", hintPath, 1, n, { isConfig, isDoc, isSql }, preHookCwd));
9626
+ return contextOutput(leadingLinesHint("`head` bypasses read hooks. ", hintPath, 1, n, { isConfig, isDoc, isSql, isXml }, preHookCwd));
9575
9627
  }
9576
9628
  const nodeRead = extractNodeFileRead(cmd);
9577
9629
  if (nodeRead !== null) {
@@ -9588,14 +9640,14 @@ function preBashHandlerInner(event) {
9588
9640
  }
9589
9641
  const psMethodRead = extractPowerShellFileMethodRead(cmd, event);
9590
9642
  if (psMethodRead !== null) {
9591
- const { filePath, isDoc, isEnv, isConfig, isSql } = psMethodRead;
9643
+ const { filePath, isDoc, isEnv, isConfig, isSql, isXml } = psMethodRead;
9592
9644
  const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
9593
9645
  const lead = "PowerShell `[IO.File]::ReadAllText()` bypasses read hooks. ";
9594
9646
  if (isSql) {
9595
9647
  recordStat("session_hint", 0, 0);
9596
9648
  return contextOutput(lead + 'Use `token-goat section "' + hintPath + '::table_name"` to pull one CREATE TABLE / CREATE TYPE block.');
9597
9649
  }
9598
- const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc);
9650
+ const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc, isXml);
9599
9651
  recordStat("session_hint", 0, 0);
9600
9652
  return cdStripped ? contextOutput(lead + hint) : denyOutput(lead + hint);
9601
9653
  }
@@ -9659,7 +9711,7 @@ function preBashHandlerInner(event) {
9659
9711
  const prevResolvedPath = prevPath !== null && commandPathIsTouchable(prevPath, event) ? resolveIndexPath(prevPath, preHookCwd ?? process.cwd()) : null;
9660
9712
  if (prevPath !== null && prevResolvedPath !== null && !existsSync(prevResolvedPath)) {
9661
9713
  clearCurlDownload(curlDl.url);
9662
- } else if (prevPath !== null && prevResolvedPath !== null && statSync4(prevResolvedPath).size >= loadConfig().hints.bash_dedup_min_bytes) {
9714
+ } else if (prevPath !== null && prevResolvedPath !== null && statSync6(prevResolvedPath).size >= loadConfig().hints.bash_dedup_min_bytes) {
9663
9715
  recordStat("session_hint", 0, 0);
9664
9716
  return denyOutput(
9665
9717
  "Already downloaded to " + prevPath + " earlier this session. Use `rg '<pattern>' " + prevPath + '` to search it, or `token-goat read "' + prevPath + '::SectionName"` to read a part of it.'
@@ -9767,15 +9819,25 @@ function extractExitCode(raw) {
9767
9819
  return null;
9768
9820
  }
9769
9821
  var GH_SECURITY_PATHS = ["/security_advisories", "/advisories", "security_events"];
9770
- var GH_SCOPE_PHRASES = ["Must have push access", "Resource not accessible by integration", "Must be an admin"];
9822
+ var GH_SCOPE_PHRASES = [
9823
+ "Must have push access",
9824
+ "Resource not accessible by integration",
9825
+ "Must be an admin",
9826
+ "You are not authorized to perform this operation"
9827
+ ];
9828
+ var GH_NOT_FOUND = /^gh: .*\(HTTP 404\)|"status":\s*"404"/m;
9771
9829
  function buildGhApiHint(cmd, stdout, exitCode) {
9772
9830
  if (stdout === "" || !cmd.startsWith("gh api")) return null;
9773
9831
  const hints = [];
9774
9832
  const isSecurityPath = GH_SECURITY_PATHS.some((p) => cmd.includes(p));
9775
9833
  const hasScopePhrase = GH_SCOPE_PHRASES.some((p) => stdout.includes(p));
9776
- const failedSecurityCall = exitCode !== null && exitCode !== 0 && isSecurityPath;
9777
- if (hasScopePhrase || failedSecurityCall) {
9834
+ const ambiguousSecurity404 = exitCode !== null && exitCode !== 0 && isSecurityPath && GH_NOT_FOUND.test(stdout);
9835
+ if (hasScopePhrase) {
9778
9836
  hints.push("[token-goat] GitHub API scope issue: try gh auth refresh -s security_events");
9837
+ } else if (ambiguousSecurity404) {
9838
+ hints.push(
9839
+ "[token-goat] GitHub answers 404 both for a resource that does not exist and for one your token cannot see: check the owner/repo spelling, then gh auth status for the security_events scope."
9840
+ );
9779
9841
  }
9780
9842
  const trimmed = stdout.trimStart();
9781
9843
  if (trimmed.startsWith("{")) {
@@ -9849,6 +9911,7 @@ async function postBashHandler(event) {
9849
9911
  try {
9850
9912
  const rawCmdRaw = extractCommand(event);
9851
9913
  if (rawCmdRaw === void 0) return passOutput();
9914
+ const isUnwrapped = unwrapCompressCommand(rawCmdRaw) === null;
9852
9915
  const rawCmd = unwrapCompressCommand(rawCmdRaw) ?? rawCmdRaw;
9853
9916
  const cmd = stripCdPrefix(rawCmd);
9854
9917
  const output = extractBashOutput(event.raw);
@@ -9932,13 +9995,19 @@ async function postBashHandler(event) {
9932
9995
  const gitScopedCacheId = await storeBashOutput(cmd, output, exitCode ?? 0, cwd);
9933
9996
  recordBashOutput(gitScopedCacheHash, gitScopedCacheId, Buffer.byteLength(output, "utf-8"));
9934
9997
  }
9998
+ if (isUnwrapped && /^git(?:\s+-[^\s]+|\s+--[^\s]+)*\s+diff\b/i.test(cmd)) {
9999
+ const unwrappedCompressed = await maybeCompressCompoundOutput(cmd, output, exitCode, cwd, cacheMinBytes, isUnwrapped);
10000
+ if (unwrappedCompressed !== null) return unwrappedCompressed;
10001
+ }
9935
10002
  const isMonitoring = getMonitoringRecallHint(cmd) !== null;
9936
10003
  const isFileRead = pureFileReadPath(cmd) !== null;
9937
10004
  if (isFileRead || !isMonitoring && !isBuildCommand(cmd) && !isCurlGetCommand(cmd)) {
9938
10005
  const identical = await maybeCollapseIdenticalRead(cmd, rawCmd, output, exitCode, cwd, cacheMinBytes);
9939
10006
  if (identical !== null) return identical;
9940
- const compound = await maybeCompressCompoundOutput(cmd, output, exitCode, cwd, cacheMinBytes);
9941
- if (compound !== null) return compound;
10007
+ if (!isFileRead && (!isUnwrapped || !isCompressibleSingleCommand(cmd))) {
10008
+ const compound = await maybeCompressCompoundOutput(cmd, output, exitCode, cwd, cacheMinBytes, isUnwrapped);
10009
+ if (compound !== null) return compound;
10010
+ }
9942
10011
  if (!isFileRead && extractLineRangeReadsCompound(cmd) === null) {
9943
10012
  const genericElision = await maybeElideServedGenericOutput(cmd, output, exitCode, cwd, cacheMinBytes);
9944
10013
  if (genericElision !== null) return genericElision;
@@ -10151,7 +10220,7 @@ registerHook("post_tool_use", postTaskOutputHandler, { toolName: "TaskOutput" })
10151
10220
 
10152
10221
  // src/hooks_tool_failure.ts
10153
10222
  init_define_import_meta_env();
10154
- import { existsSync as existsSync2, readFileSync as readFileSync3, statSync as statSync5, writeFileSync as writeFileSync2 } from "node:fs";
10223
+ import { existsSync as existsSync2, readFileSync as readFileSync3, statSync as statSync7, writeFileSync as writeFileSync2 } from "node:fs";
10155
10224
  import { dirname as dirname2, relative } from "node:path";
10156
10225
  var FAILURE_SUFFIX = ".tool-failures.json";
10157
10226
  var MAX_TRACKED_FAILURES = 64;
@@ -10220,7 +10289,7 @@ function diagnoseEditFailure(event, errorText) {
10220
10289
  const absPath = normalizePath(filePath);
10221
10290
  if (!existsSync2(absPath)) return null;
10222
10291
  try {
10223
- const stat = statSync5(absPath);
10292
+ const stat = statSync7(absPath);
10224
10293
  if (stat.size > MAX_EDIT_DIAGNOSE_BYTES) return null;
10225
10294
  const fileContent = readFileSync3(absPath, "utf8");
10226
10295
  const relDisplay = displaySafeText(relative(process.cwd(), absPath).replace(/\\/g, "/") || absPath);
@@ -10279,6 +10348,7 @@ function postToolUseFailureHandler(event) {
10279
10348
  const ledger = readLedger(target);
10280
10349
  const priorState = ledger.seen[signature];
10281
10350
  const editDiagnostic = diagnoseEditFailure(event, errorText);
10351
+ const sqlDiagnostic = diagnoseSqlFailure(event, errorText);
10282
10352
  if (priorState === void 0) {
10283
10353
  ledger.seen[signature] = false;
10284
10354
  ledger.order.push(signature);
@@ -10292,6 +10362,11 @@ function postToolUseFailureHandler(event) {
10292
10362
  writeLedger(target, ledger);
10293
10363
  return contextOutput(editDiagnostic);
10294
10364
  }
10365
+ if (sqlDiagnostic !== null) {
10366
+ ledger.seen[signature] = true;
10367
+ writeLedger(target, ledger);
10368
+ return contextOutput(sqlDiagnostic);
10369
+ }
10295
10370
  return passOutput();
10296
10371
  }
10297
10372
  if (priorState) return passOutput();
@@ -10300,6 +10375,9 @@ function postToolUseFailureHandler(event) {
10300
10375
  if (editDiagnostic !== null) {
10301
10376
  return contextOutput(editDiagnostic);
10302
10377
  }
10378
+ if (sqlDiagnostic !== null) {
10379
+ return contextOutput(sqlDiagnostic);
10380
+ }
10303
10381
  return contextOutput(repeatFailureNotice(toolName));
10304
10382
  } catch {
10305
10383
  return passOutput();
@@ -10576,6 +10654,37 @@ function compressGithubMcpResult(toolName, resultText) {
10576
10654
  const stripped = deepStrip(parsed, githubShouldStrip);
10577
10655
  return finishWithGenericPass(resultText, stripped);
10578
10656
  }
10657
+ var ATLASSIAN_TOOL_RE = /(?:jira|confluence|atlassian)/i;
10658
+ var ATLASSIAN_STRIP_EXACT_KEYS = /* @__PURE__ */ new Set([
10659
+ "avatarUrls",
10660
+ "avatarUrl",
10661
+ "iconUrl",
10662
+ "iconUrls",
10663
+ "self",
10664
+ "expand",
10665
+ "schema",
10666
+ "operations",
10667
+ "editmeta",
10668
+ "names",
10669
+ "_links",
10670
+ "_expandable",
10671
+ "extensions",
10672
+ "timeZone"
10673
+ ]);
10674
+ function atlassianShouldStrip(key) {
10675
+ return ATLASSIAN_STRIP_EXACT_KEYS.has(key);
10676
+ }
10677
+ function compressAtlassianMcpResult(toolName, resultText) {
10678
+ if (!ATLASSIAN_TOOL_RE.test(toolName)) return null;
10679
+ let parsed;
10680
+ try {
10681
+ parsed = JSON.parse(resultText);
10682
+ } catch {
10683
+ return null;
10684
+ }
10685
+ const stripped = deepStrip(parsed, atlassianShouldStrip);
10686
+ return finishWithGenericPass(resultText, stripped);
10687
+ }
10579
10688
  var BROWSER_TOOL_RE = /^mcp__.*(?:chrome-devtools|claude-in-chrome|playwright).*__/i;
10580
10689
  var BROWSER_CONSOLE_METHOD_RE = /(?:^|_)(?:read|list)_console_messages$/i;
10581
10690
  var BROWSER_NETWORK_METHOD_RE = /(?:^|_)(?:read|list)_network_requests$/i;
@@ -10726,10 +10835,11 @@ function stripBase64DataUrls(resultText) {
10726
10835
  function compressMcpResultWithPacks(toolName, resultText) {
10727
10836
  const base64Stripped = stripBase64DataUrls(resultText);
10728
10837
  const text = base64Stripped ?? resultText;
10729
- return compressGithubMcpResult(toolName, text) ?? compressBrowserMcpResult(toolName, text) ?? compressGWorkspaceMcpResult(toolName, text) ?? base64Stripped;
10838
+ return compressGithubMcpResult(toolName, text) ?? compressAtlassianMcpResult(toolName, text) ?? compressBrowserMcpResult(toolName, text) ?? compressGWorkspaceMcpResult(toolName, text) ?? base64Stripped;
10730
10839
  }
10731
10840
 
10732
10841
  // src/hooks_mcp.ts
10842
+ var MCP_OVERSIZED_THRESHOLD_BYTES = 25e3;
10733
10843
  function preMcpHandler(event) {
10734
10844
  const toolName = getToolName(event);
10735
10845
  if (!toolName || !event.sessionId) return passOutput();
@@ -10787,9 +10897,15 @@ function postMcpHandler(event) {
10787
10897
  minNetSavingsBytes: resolveMinNetSavingsBytes()
10788
10898
  });
10789
10899
  if (worthwhile) {
10900
+ const bodyBytes = Buffer.byteLength(redactedBody, "utf-8");
10901
+ let finalBody = redactedBody;
10902
+ if (bodyBytes >= MCP_OVERSIZED_THRESHOLD_BYTES) {
10903
+ const preview = clipToDeliveryCap(redactedBody, Buffer.byteLength(notice, "utf-8") + 500);
10904
+ finalBody = preview.text + (preview.clipped ? "\n\n...[preview truncated by token-goat; use mcp-output above to query full payload]..." : "");
10905
+ }
10790
10906
  return preserve(
10791
10907
  emitRewrite(
10792
- `${notice}${fenceWithMatches(redactedBody, injectionMatches, UNTRUSTED_TOOL_TAG)}`,
10908
+ `${notice}${fenceWithMatches(finalBody, injectionMatches, UNTRUSTED_TOOL_TAG)}`,
10793
10909
  "mcp",
10794
10910
  { kind: "mcp:compress", originalBytes }
10795
10911
  )
@@ -10798,6 +10914,29 @@ function postMcpHandler(event) {
10798
10914
  }
10799
10915
  }
10800
10916
  }
10917
+ const rawBytes = Buffer.byteLength(resultText, "utf-8");
10918
+ if (rawBytes >= MCP_OVERSIZED_THRESHOLD_BYTES && process.env["TOKEN_GOAT_MCP_COMPRESS"] !== "0") {
10919
+ if (id === null) id = storeMcpOutput(event.sessionId, toolName, toolInput, resultText);
10920
+ if (id !== null) {
10921
+ const notice = `[token-goat: oversized MCP result (${rawBytes} bytes) cached as ${id}]
10922
+ The full payload was cached to prevent harness context spill. Slicing commands:
10923
+ token-goat mcp-output ${id} --json-query '<path>' (e.g. 'issues[*].key', 'values[*].id')
10924
+ token-goat mcp-output ${id} --section '<heading>'
10925
+ token-goat mcp-output ${id} --grep '<regex>' --max-matches 20
10926
+ token-goat mcp-output ${id} --head 50
10927
+
10928
+ `;
10929
+ const preview = clipToDeliveryCap(redactedResult.text, Buffer.byteLength(notice, "utf-8") + 500);
10930
+ const previewBody = preview.text + (preview.clipped ? "\n\n...[preview truncated by token-goat; use mcp-output above to query full payload]..." : "");
10931
+ return preserve(
10932
+ emitRewrite(
10933
+ `${notice}${fenceWithMatches(previewBody, injectionMatches, UNTRUSTED_TOOL_TAG)}`,
10934
+ "mcp",
10935
+ { kind: "mcp:oversized", originalBytes: rawBytes }
10936
+ )
10937
+ );
10938
+ }
10939
+ }
10801
10940
  return passOrFence();
10802
10941
  }
10803
10942
  registerHook("pre_tool_use", preMcpHandler, { toolPattern: "^mcp__" });
@@ -11419,6 +11558,10 @@ async function relayInProcess(eventName, rawPayload) {
11419
11558
  if (!process.env["CLAUDE_CODE_SESSION_ID"] && event.sessionId) {
11420
11559
  process.env["CLAUDE_CODE_SESSION_ID"] = event.sessionId;
11421
11560
  }
11561
+ const rawTranscriptPath = event.raw["transcript_path"];
11562
+ if (typeof rawTranscriptPath === "string" && rawTranscriptPath !== "") {
11563
+ setTranscriptPath(rawTranscriptPath, event.sessionId ?? "");
11564
+ }
11422
11565
  if (!process.env["TRACEPARENT"] && event.traceparent) {
11423
11566
  process.env["TRACEPARENT"] = event.traceparent;
11424
11567
  }