token-goat 2.9.13 → 2.9.14

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 (44) hide show
  1. package/README.md +13 -0
  2. package/dist/token-goat-chunk-2ESBO4IN.mjs +209 -0
  3. package/dist/token-goat-chunk-3BTK54F3.mjs +1733 -0
  4. package/dist/{token-goat-chunk-2WC4ZUXN.mjs → token-goat-chunk-3XQPEJMV.mjs} +1 -1
  5. package/dist/token-goat-chunk-4SDX3QP3.mjs +122 -0
  6. package/dist/token-goat-chunk-7OGKZ7AP.mjs +142 -0
  7. package/dist/token-goat-chunk-AH6QILZM.mjs +26 -0
  8. package/dist/token-goat-chunk-AMYCQJX4.mjs +1548 -0
  9. package/dist/{token-goat-chunk-6B44WLIF.mjs → token-goat-chunk-ASVVF4JV.mjs} +10 -7
  10. package/dist/{token-goat-chunk-3ESRORNM.mjs → token-goat-chunk-DAMXYVIW.mjs} +12026 -11796
  11. package/dist/{token-goat-chunk-YOA4N6WA.mjs → token-goat-chunk-DBNY4RLN.mjs} +2 -2
  12. package/dist/token-goat-chunk-EG3663UT.mjs +226 -0
  13. package/dist/token-goat-chunk-EZNVAIR3.mjs +21 -0
  14. package/dist/token-goat-chunk-GMOUBOX4.mjs +386 -0
  15. package/dist/{token-goat-chunk-B3CTCQTH.mjs → token-goat-chunk-HF6H7RNK.mjs} +1 -1
  16. package/dist/{token-goat-chunk-ZZI3IDQZ.mjs → token-goat-chunk-IT6O3PNN.mjs} +2874 -7088
  17. package/dist/token-goat-chunk-NEI4NC54.mjs +424 -0
  18. package/dist/token-goat-chunk-O5WAMISC.mjs +167 -0
  19. package/dist/{token-goat-chunk-RDITECDL.mjs → token-goat-chunk-PGGDW7DZ.mjs} +28 -13
  20. package/dist/token-goat-chunk-POBYR64E.mjs +1632 -0
  21. package/dist/{token-goat-chunk-4NXUKV7D.mjs → token-goat-chunk-PRJVGIC5.mjs} +6 -4
  22. package/dist/{token-goat-chunk-U7X6LQD2.mjs → token-goat-chunk-RUDOKYPJ.mjs} +10160 -10053
  23. package/dist/token-goat-chunk-SAQ5PG4L.mjs +2634 -0
  24. package/dist/{token-goat-chunk-UMXJN7DI.mjs → token-goat-chunk-T2IWWTHB.mjs} +288 -2654
  25. package/dist/{token-goat-chunk-JOXLE672.mjs → token-goat-chunk-VZYD4OZB.mjs} +1164 -92
  26. package/dist/token-goat-chunk-XEH6KBWW.mjs +23 -0
  27. package/dist/token-goat-chunk-XQF5J25J.mjs +33 -0
  28. package/dist/token-goat-chunk-XTQAOTSO.mjs +89 -0
  29. package/dist/{token-goat-chunk-QWSUZWFP.mjs → token-goat-chunk-XVZ4MNQC.mjs} +587 -587
  30. package/dist/token-goat-chunk-Y4AFKTHK.mjs +22 -0
  31. package/dist/token-goat-chunk-YHGTGG6K.mjs +396 -0
  32. package/dist/{token-goat-chunk-FZU7GMUS.mjs → token-goat-chunk-YTQHZJXW.mjs} +20 -11
  33. package/dist/token-goat-chunk-Z6UXPYJA.mjs +62 -0
  34. package/dist/token-goat-chunk-ZD4EM4LR.mjs +30 -0
  35. package/dist/token-goat-chunk-ZFM4PWXL.mjs +585 -0
  36. package/dist/{token-goat-chunk-FQCNJV4V.mjs → token-goat-chunk-ZYNNQ36L.mjs} +116 -330
  37. package/dist/token-goat-hook.mjs +12 -8
  38. package/dist/token-goat.core.mjs +22 -10
  39. package/docs/cli.md +1 -0
  40. package/package.json +4 -2
  41. package/dist/token-goat-chunk-JVNPCQB7.mjs +0 -31
  42. package/dist/token-goat-chunk-P2PU4CR5.mjs +0 -26
  43. package/dist/token-goat-chunk-QKXBGBQR.mjs +0 -3653
  44. package/dist/token-goat-chunk-UM47DRD3.mjs +0 -242
@@ -4,31 +4,25 @@ import {
4
4
  canRunWrappedShell,
5
5
  clipToDeliveryCap,
6
6
  deliveredOutputBytes
7
- } from "./token-goat-chunk-2WC4ZUXN.mjs";
7
+ } from "./token-goat-chunk-3XQPEJMV.mjs";
8
8
  import {
9
9
  DEFAULT_RECONCILE_BUDGET_MS,
10
10
  HOOK_EVENTS,
11
- accumulateResidentLines,
12
11
  buildCommandManifest,
13
12
  checkSymbolBodySize,
14
13
  flattenCommandNames,
15
14
  getWebOutput,
16
15
  isReconcileClean,
17
- lineMayCarryResidentSignal,
18
16
  readStdinJson,
19
- readTranscriptTail,
20
17
  reconcileProject,
21
- repeatedSkillBodyHint,
22
- storeWebOutput,
23
- summarizeResidentContext,
24
- taskListPruneHint
25
- } from "./token-goat-chunk-FQCNJV4V.mjs";
18
+ storeWebOutput
19
+ } from "./token-goat-chunk-ZYNNQ36L.mjs";
26
20
  import {
27
- BASH_OUTPUT_SUBDIR,
28
21
  GENERIC_SERVED_OUTPUT_KEY,
29
22
  OUTLINE_MAX_REPLACEMENT_RATIO,
30
23
  OUTLINE_MIN_HEADINGS,
31
24
  WEB_FETCH_KEY_SEP,
25
+ accumulateResidentLines,
32
26
  appendDirtyPath,
33
27
  applyHintTracking,
34
28
  buildDeltaCapsule,
@@ -36,10 +30,8 @@ import {
36
30
  classifyBashHint,
37
31
  classifyEditHint,
38
32
  clearCurlDownload,
39
- commandHash,
40
33
  compactPathFor,
41
34
  computeAdaptiveBudget,
42
- countSymbols,
43
35
  enqueueDirtyPathSafe,
44
36
  ensureWorkerAlive,
45
37
  extractCompactFromMarker,
@@ -53,7 +45,6 @@ import {
53
45
  formatHeadingTreeParts,
54
46
  formatProjectMap,
55
47
  formatShrinkSummary,
56
- getBashOutput,
57
48
  getBashOutputId,
58
49
  getContextPressure,
59
50
  getCurlDownloadPath,
@@ -61,7 +52,6 @@ import {
61
52
  getFileServedOutputs,
62
53
  getGlobMatchCount,
63
54
  getGrepMatchCount,
64
- getMonitoringRecallHint,
65
55
  getOutstandingAgentSpawns,
66
56
  getSessionBashOutputs,
67
57
  getSessionBashReruns,
@@ -71,15 +61,11 @@ import {
71
61
  hasSessionOutput,
72
62
  imageQualifiesForShrink,
73
63
  incrementSkillHit,
74
- indexRecallEntry,
75
64
  indexServedBody,
76
65
  installedSkillPath,
77
- isBashEntryStale,
78
- isBuildCommand,
79
- isScopedGitStatusOrDiffStatCommand,
80
66
  isStructuralRewriteAccepted,
81
- isTestRunnerCommand,
82
67
  lastTabContextMatches,
68
+ lineMayCarryResidentSignal,
83
69
  listSiblingSessionStates,
84
70
  loadSessionCache,
85
71
  loadSessionState,
@@ -96,6 +82,7 @@ import {
96
82
  planSourceSkeleton,
97
83
  preToolPathDeclined,
98
84
  probeImageMeta,
85
+ readTranscriptTail,
99
86
  recordBashOutput,
100
87
  recordBashRerun,
101
88
  recordCliRead,
@@ -113,29 +100,34 @@ import {
113
100
  recordSymbolRead,
114
101
  recordWebFetch,
115
102
  removeOutstandingAgentSpawn,
103
+ repeatedSkillBodyHint,
116
104
  saveSessionState,
117
105
  scanAndRecord,
118
106
  servedRunNotice,
119
107
  sessionOutputBodyBytes,
120
108
  sessionSidecarPath,
121
109
  setLastTabContext,
110
+ setTranscriptPath,
122
111
  shrinkImage,
123
- storeBashOutput,
124
112
  storeOutput,
125
- summarizeOutputDelta,
113
+ summarizeResidentContext,
126
114
  takePendingLargeFileHint,
115
+ taskListPruneHint,
127
116
  visionTokens,
128
117
  visionTokensSaved,
129
118
  vscodePathAllowed,
130
119
  wasCliReadThisSession,
131
120
  wasFileReadThisSession,
132
121
  wasHintShown
133
- } from "./token-goat-chunk-ZZI3IDQZ.mjs";
122
+ } from "./token-goat-chunk-IT6O3PNN.mjs";
134
123
  import {
124
+ BASH_OUTPUT_SUBDIR,
135
125
  BODY_FIRST_TOOL_RESPONSE_KEYS,
136
126
  OUTPUT_FIRST_TOOL_RESPONSE_KEYS,
127
+ commandHash,
137
128
  compressOutput,
138
129
  contextOutput,
130
+ countSymbols,
139
131
  dedupeConsecutive,
140
132
  denyOutput,
141
133
  detectFromCommand,
@@ -145,14 +137,22 @@ import {
145
137
  extractToolResponseField,
146
138
  extractToolResultText,
147
139
  filterByName,
140
+ getBashOutput,
148
141
  getCwd,
149
142
  getFilePath,
143
+ getMonitoringRecallHint,
150
144
  getToolInput,
151
145
  getToolName,
146
+ getTranscriptPath,
152
147
  hasBareBackgroundOrNewline,
153
148
  hasUnquotedOperator,
149
+ indexRecallEntry,
150
+ isBashEntryStale,
151
+ isBuildCommand,
154
152
  isMcpErrorResponse,
155
153
  isRewriteWorthwhile,
154
+ isScopedGitStatusOrDiffStatCommand,
155
+ isTestRunnerCommand,
156
156
  isTextualContentBlock,
157
157
  makeDedupHintHandlers,
158
158
  mcpContentBlocks,
@@ -166,28 +166,30 @@ import {
166
166
  sessionStateKey,
167
167
  shlexSplit,
168
168
  splitOwnTrailingNotices,
169
+ storeBashOutput,
170
+ summarizeOutputDelta,
169
171
  withPreservedBlocks
170
- } from "./token-goat-chunk-JOXLE672.mjs";
172
+ } from "./token-goat-chunk-VZYD4OZB.mjs";
171
173
  import {
172
- storeBlob
173
- } from "./token-goat-chunk-UM47DRD3.mjs";
174
+ detectLanguage,
175
+ getDb,
176
+ languageHasFlag,
177
+ recordStat,
178
+ redactSecrets,
179
+ savedTokensFromBytes,
180
+ storeBlob,
181
+ stripAnsiEscapes
182
+ } from "./token-goat-chunk-T2IWWTHB.mjs";
174
183
  import {
175
184
  detectHarness,
176
- detectLanguage,
177
185
  envBool,
178
186
  envInt,
179
187
  findProject,
180
- getDb,
181
188
  getHarnessName,
182
189
  isUnderSystemTemp,
183
- languageHasFlag,
184
190
  loadConfig,
185
- recordStat,
186
- redactSecrets,
187
- savedTokensFromBytes,
188
- shortFingerprint,
189
- stripAnsiEscapes
190
- } from "./token-goat-chunk-UMXJN7DI.mjs";
191
+ shortFingerprint
192
+ } from "./token-goat-chunk-SAQ5PG4L.mjs";
191
193
  import {
192
194
  ENV_KEYS,
193
195
  IDENTICAL_READ_MIN_BODY_BYTES,
@@ -214,7 +216,7 @@ import {
214
216
  runGit,
215
217
  toDisplayPath,
216
218
  toKB
217
- } from "./token-goat-chunk-QKXBGBQR.mjs";
219
+ } from "./token-goat-chunk-AMYCQJX4.mjs";
218
220
  import {
219
221
  init_define_import_meta_env
220
222
  } from "./token-goat-chunk-A37V4PBF.mjs";
@@ -747,7 +749,7 @@ function mergeManifestFiles(parent, siblingFiles) {
747
749
  }
748
750
  return Array.from(byPath.values());
749
751
  }
750
- function buildManifest(sessionId, cwd) {
752
+ function buildManifest(sessionId, cwd, transcriptPath) {
751
753
  const ownFiles = [...getSessionFiles().values()];
752
754
  const siblingFiles = sessionId !== void 0 ? listSiblingSessionStates(sessionId).flatMap((s) => s.files) : [];
753
755
  const files = siblingFiles.length > 0 ? mergeManifestFiles(ownFiles, siblingFiles) : ownFiles;
@@ -779,7 +781,7 @@ function buildManifest(sessionId, cwd) {
779
781
  );
780
782
  lines.push(...buildSafeToDiscardSection(files));
781
783
  lines.push(...buildMemEpochSection());
782
- return capManifestChars(lines.join("\n"), sessionId, cwd);
784
+ return capManifestChars(lines.join("\n"), sessionId, cwd, transcriptPath);
783
785
  }
784
786
  function gitDirtySignals(cwd) {
785
787
  const timeoutMs = loadConfig().hints.git_hint_max_ms;
@@ -797,11 +799,11 @@ function gitDirtySignals(cwd) {
797
799
  }
798
800
  return { hasPendingDiff, hasUncommittedChanges };
799
801
  }
800
- function adaptiveCharBonus(sessionId, cwd) {
802
+ function adaptiveCharBonus(sessionId, cwd, transcriptPath) {
801
803
  if (!cwd) return 0;
802
804
  const cache = loadSessionCache(sessionId ?? "") ?? {};
803
805
  const ageSecs = cache.created_ts !== void 0 ? Math.max(0, Date.now() / 1e3 - cache.created_ts) : 0;
804
- const contextPressure = getContextPressure(cache);
806
+ const contextPressure = getContextPressure(cache, transcriptPath);
805
807
  const { hasPendingDiff, hasUncommittedChanges } = gitDirtySignals(cwd);
806
808
  if (!hasPendingDiff && !hasUncommittedChanges) return 0;
807
809
  const baseline = computeAdaptiveBudget(cache, ageSecs, { contextPressure });
@@ -809,11 +811,11 @@ function adaptiveCharBonus(sessionId, cwd) {
809
811
  const deltaTokens = Math.max(0, withGitSignal - baseline);
810
812
  return deltaTokens * 3;
811
813
  }
812
- function capManifestChars(manifest, sessionId, cwd) {
814
+ function capManifestChars(manifest, sessionId, cwd, transcriptPath) {
813
815
  const cap = loadConfig().compact_assist.max_manifest_chars;
814
816
  if (cap <= 0) return manifest;
815
817
  if (manifest.length <= cap) return manifest;
816
- const effectiveCap = cap + adaptiveCharBonus(sessionId, cwd);
818
+ const effectiveCap = cap + adaptiveCharBonus(sessionId, cwd, transcriptPath);
817
819
  if (manifest.length <= effectiveCap) return manifest;
818
820
  const omitted = manifest.length - effectiveCap;
819
821
  return manifest.slice(0, effectiveCap) + `
@@ -886,7 +888,7 @@ function summaryBudgetDirective(budgetChars) {
886
888
  function preCompactHandler(event) {
887
889
  const out = loadConfig().compact_assist.enabled ? contextOutput(`${MANIFEST_PREAMBLE}${summaryBudgetDirective(loadConfig().compact_assist.summary_budget_chars)}
888
890
 
889
- ${buildManifest(event.sessionId, getCwd(event))}`) : passOutput();
891
+ ${buildManifest(event.sessionId, getCwd(event), getTranscriptPath(event))}`) : passOutput();
890
892
  markCompacted();
891
893
  return out;
892
894
  }
@@ -934,7 +936,7 @@ init_define_import_meta_env();
934
936
  var TRACKED_SKILL = "token-goat";
935
937
  var MAX_COMMANDS_SHOWN = 8;
936
938
  async function currentCommandNames() {
937
- const { buildProgram } = await import("./token-goat-chunk-JVNPCQB7.mjs");
939
+ const { buildProgram } = await import("./token-goat-chunk-O5WAMISC.mjs");
938
940
  return flattenCommandNames(buildCommandManifest(buildProgram()));
939
941
  }
940
942
  async function recordSkillVersionSnapshot(sessionId, skillName) {
@@ -7645,115 +7647,21 @@ registerHook("post_tool_use", postSkillHandler, { toolName: "Skill" });
7645
7647
 
7646
7648
  // src/hooks_bash.ts
7647
7649
  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
- }
7650
+ import { statSync as statSync6, existsSync } from "node:fs";
7651
+
7652
+ // src/hooks_bash_commands.ts
7653
+ init_define_import_meta_env();
7654
+ import { statSync as statSync5 } from "node:fs";
7655
+
7656
+ // src/bash_extractors.ts
7657
+ init_define_import_meta_env();
7658
+ import { statSync as statSync4, openSync as openSync2, readSync, closeSync as closeSync2 } from "node:fs";
7687
7659
  function surgicalHintFor(hintPath, isEnv, isConfig, isDoc) {
7688
7660
  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
7661
  }
7690
7662
  function surgicalHintForConfigDoc(filePath, isConfig, isDoc, isSql) {
7691
7663
  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
7664
  }
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;
7746
- }
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);
7756
- }
7757
7665
  function splitShellSegments(cmd) {
7758
7666
  const segments = [];
7759
7667
  let cur = "";
@@ -7786,174 +7694,38 @@ function splitShellSegments(cmd) {
7786
7694
  segments.push(cur);
7787
7695
  return segments.map((s) => s.trim()).filter((s) => s.length > 0);
7788
7696
  }
7789
- function safeShlexSplit(segment) {
7790
- try {
7791
- return shlexSplit(segment);
7792
- } catch {
7793
- return null;
7794
- }
7795
- }
7796
- function segmentCommandIs(segment, name2) {
7797
- return new RegExp("^\\s*(?:\\S*[/\\\\])?" + name2 + "(?:\\.exe)?\\b", "i").test(segment);
7697
+ function isTempPath(fp) {
7698
+ const norm = fp.replace(/\\/g, "/");
7699
+ return /^\/tmp\//i.test(norm) || /\/var\/folders\//i.test(norm) || // Anchored to a local drive root on purpose. Unanchored, this clause said yes to
7700
+ // `//host/share/AppData/Local/Temp/x.md`, and the caller's answer to yes is to stat the path --
7701
+ // which on Windows opens an SMB session to whatever host a command named. A temp directory is
7702
+ // somewhere on a local volume; nothing that starts with two separators is one.
7703
+ /^[a-z]:\/(?:[^/]+\/)*AppData\/Local\/Temp\//i.test(norm) || norm.startsWith("/c/Users/") && norm.includes("/AppData/Local/Temp/") || isUnderSystemTemp(fp);
7798
7704
  }
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;
7705
+ function isOrchestratorStateFile(filePath) {
7706
+ const basename3 = (filePath.includes("/") ? filePath.split("/").at(-1) : filePath.split("\\").at(-1)) ?? filePath;
7707
+ return /^\.improve-state-/.test(basename3);
7826
7708
  }
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("-"));
7709
+ function extractCatSourceFile(cmd) {
7710
+ 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);
7711
+ return m?.[1] ?? null;
7832
7712
  }
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;
7713
+ function classifyFileExtensions(filePath) {
7714
+ const basename3 = (filePath.includes("/") ? filePath.split("/").at(-1) : filePath.split("\\").at(-1)) ?? filePath;
7715
+ const isEnvFile = /^\.env(\.\w+)?$/i.test(basename3);
7716
+ 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);
7717
+ if (!hasKnownExt && !isEnvFile) return null;
7718
+ const isSql = /\.sql$/i.test(filePath);
7719
+ const isDoc = /\.(?:md|mdx|rst|txt)$/i.test(filePath);
7720
+ const isEnv = isEnvFile || /\.env$/i.test(filePath);
7721
+ const isConfig = /\.(?:json|yaml|yml|toml|conf|cfg|ini|properties)$/i.test(filePath);
7722
+ return { isDoc, isEnv, isConfig, isSql };
7842
7723
  }
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 };
7724
+ function classifyDocConfig(filePath) {
7725
+ const isDoc = /\.(?:md|mdx|rst|txt)$/i.test(filePath);
7726
+ const isConfig = /\.(?:json|yaml|yml|toml|conf|cfg|ini|properties)$/i.test(filePath);
7727
+ const isSql = /\.sql$/i.test(filePath);
7728
+ return { isDoc, isConfig, isSql };
7957
7729
  }
7958
7730
  function classifyCatPath(filePath, cmd0) {
7959
7731
  if (isTempPath(filePath)) return null;
@@ -8824,21 +8596,504 @@ function buildRecallHint(cmd, outputId) {
8824
8596
  if (isTscCommand(cmd)) {
8825
8597
  return "Output from a prior `" + cmdPreview + "` run is cached. Use `token-goat bash-output " + outputId + ' --grep "error TS"` to filter TypeScript errors, or `--grep "Cannot find"` for missing module errors.';
8826
8598
  }
8827
- if (isDevServerCommand(cmd)) {
8828
- return "Dev server output cached (`" + cmdPreview + "`). Use `token-goat bash-output " + outputId + ' --tail 20` to see the latest output, or `--grep "error\\|warn"` to filter issues.';
8599
+ if (isDevServerCommand(cmd)) {
8600
+ return "Dev server output cached (`" + cmdPreview + "`). Use `token-goat bash-output " + outputId + ' --tail 20` to see the latest output, or `--grep "error\\|warn"` to filter issues.';
8601
+ }
8602
+ return "Output from a prior `" + cmdPreview + "` run is cached. Use `token-goat bash-output " + outputId + "` (or `--tail 50`, `--grep ERROR`) to re-inspect it without re-running.";
8603
+ }
8604
+ function shellQuoteSingle(s) {
8605
+ return `'${s.replace(/'/g, `'\\''`)}'`;
8606
+ }
8607
+ function isCompressibleSingleCommand(cmd) {
8608
+ if (!cmd || cmd.length > 65536) return false;
8609
+ if (["$(", "`"].some((op) => cmd.includes(op))) return false;
8610
+ if (hasUnquotedOperator(cmd, ["&&", "||", "|", ";", "<", ">"])) return false;
8611
+ if (hasBareBackgroundOrNewline(cmd)) return false;
8612
+ return true;
8613
+ }
8614
+
8615
+ // src/hooks_bash_commands.ts
8616
+ var CD_PREFIX_RE = /^(?:cd\s+(?:"[^"]*"|'[^']*'|\S+)[ \t]*(?:&&|;|\r?\n)\s*)+/;
8617
+ function stripCdPrefix(cmd) {
8618
+ const stripped = cmd.replace(CD_PREFIX_RE, "");
8619
+ return stripped.trim() || cmd;
8620
+ }
8621
+ function stripTrailingStderrRedirect(cmd) {
8622
+ const stripped = cmd.replace(/\s*2>&1\s*$/, "");
8623
+ return stripped.trim() || cmd;
8624
+ }
8625
+ function hasTestRunScopeOrBudget(cmd) {
8626
+ const hasTimeout = /(?:^|\s)(?:timeout(?:\.exe)?\s+\S+|--(?:test)?timeout(?:=|\s)\S+)/i.test(cmd);
8627
+ 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);
8628
+ return hasTimeout || hasSelector;
8629
+ }
8630
+ function isDirectTestRunnerCommand(cmd) {
8631
+ return /^(?:pytest|(?:npx\s+)?(?:jest|vitest)|go\s+test|cargo\s+test)\b/i.test(cmd);
8632
+ }
8633
+ function extractCdPrefixDirs(rawCmd) {
8634
+ const prefixMatch = rawCmd.match(CD_PREFIX_RE);
8635
+ if (prefixMatch === null) return [];
8636
+ const dirs = [];
8637
+ const segmentPattern = /cd\s+(?:"([^"]*)"|'([^']*)'|(\S+))[ \t]*(?:&&|;|\r?\n)/g;
8638
+ let match;
8639
+ while ((match = segmentPattern.exec(prefixMatch[0])) !== null) {
8640
+ const dir = match[1] ?? match[2] ?? match[3];
8641
+ if (dir !== void 0) dirs.push(dir);
8642
+ }
8643
+ return dirs;
8644
+ }
8645
+ function resolveCdHintPath(rawCmd, filePath, cwd) {
8646
+ if (extractCdPrefixDirs(rawCmd).length === 0) return filePath;
8647
+ return resolveIndexPath(filePath, cdPrefixCwd(rawCmd, cwd));
8648
+ }
8649
+ function cdPrefixCwd(rawCmd, cwd) {
8650
+ let dir = cwd;
8651
+ for (const target of extractCdPrefixDirs(rawCmd)) dir = resolveIndexPath(target, dir);
8652
+ return dir;
8653
+ }
8654
+ function stripOutputPipeline(cmd) {
8655
+ let inSingle = false;
8656
+ let inDouble = false;
8657
+ let cut = cmd.length;
8658
+ let backslashes = 0;
8659
+ for (let i = 0; i < cmd.length; i++) {
8660
+ const ch = cmd[i];
8661
+ if (ch === "\\") {
8662
+ backslashes++;
8663
+ continue;
8664
+ }
8665
+ const escaped = backslashes % 2 === 1;
8666
+ backslashes = 0;
8667
+ if (ch === "'" && !inDouble) {
8668
+ if (!escaped) inSingle = !inSingle;
8669
+ } else if (ch === '"' && !inSingle) {
8670
+ if (!escaped) inDouble = !inDouble;
8671
+ } else if (ch === "|" && !inSingle && !inDouble) {
8672
+ if (cmd[i + 1] === "|") {
8673
+ i++;
8674
+ continue;
8675
+ }
8676
+ cut = i;
8677
+ break;
8678
+ }
8679
+ }
8680
+ let base = cmd.slice(0, cut);
8681
+ let prev;
8682
+ do {
8683
+ prev = base;
8684
+ const masked = base.replace(/"((?:[^"\\]|\\.)*)"/g, (_m, inner) => '"' + " ".repeat(inner.length) + '"').replace(/'([^']*)'/g, (_m, inner) => "'" + " ".repeat(inner.length) + "'");
8685
+ const newMasked = masked.replace(/\s*(?:[0-9]*>&[0-9]+|[0-9&]*>>?\s*(?:"[^"]*"|'[^']*'|[^\s|]+))\s*$/, "");
8686
+ if (newMasked.length < masked.length) {
8687
+ base = base.slice(0, newMasked.length);
8688
+ }
8689
+ } while (base !== prev);
8690
+ return base.trim();
8691
+ }
8692
+ function pipelineDivergenceNote(cmd, entryCommand) {
8693
+ if (entryCommand === cmd) return "";
8694
+ const preview = entryCommand.length > 60 ? entryCommand.slice(0, 57) + "..." : entryCommand;
8695
+ return " (cached from a differently-piped run, `" + preview + "` \u2014 verify it covers what you need before trusting it)";
8696
+ }
8697
+ function extractCommand(event) {
8698
+ const cmd = event.toolInput["command"];
8699
+ return typeof cmd === "string" && cmd.trim() !== "" ? cmd.trim() : void 0;
8700
+ }
8701
+ var HEAD_MOVING_GIT_RE = /^\s*git\s+(?:checkout|switch|pull|merge|rebase|reset|cherry-pick)\b/i;
8702
+ var PATH_SCOPED_CHECKOUT_RE = /^\s*git\s+checkout\s+(?:\S.*?\s)?--(?:\s|$)/i;
8703
+ function isHeadMovingGitCommand(cmd) {
8704
+ if (!HEAD_MOVING_GIT_RE.test(cmd)) return false;
8705
+ if (PATH_SCOPED_CHECKOUT_RE.test(cmd)) return false;
8706
+ return true;
8707
+ }
8708
+ var ORIG_HEAD_ELIGIBLE_GIT_RE = /^\s*git\s+(?:pull|merge|rebase)\b/i;
8709
+ var ORIG_HEAD_REFLOG_MSG_RE = /^(merge\s|rebase\s\(|pull\s)/i;
8710
+ var GIT_RESTORE_RE = /^\s*git\s+restore\b/i;
8711
+ var GIT_RESTORE_STAGED_ONLY_RE = /(?:^|\s)(?:--staged|-S)(?:\s|$)/;
8712
+ var GIT_STASH_APPLY_RE = /^\s*git\s+stash\s+(?:pop|apply)\b/i;
8713
+ var PATCH_APPLY_SEGMENT_RE = /^\s*(?:\S*[/\\])?(?:git\s+apply|patch)\b/i;
8714
+ var PATCH_APPLY_INSPECT_ONLY_RE = /(?:^|\s)--(?:check|stat|numstat|summary|dry-run)(?:\s|=|$)/i;
8715
+ function isFormatterWriteSegment(segment) {
8716
+ return /(?:^|\s|[/\\])(?:prettier|eslint)\b/i.test(segment) && /(?:^|\s)--(?:write|fix)(?:\s|$)/i.test(segment);
8717
+ }
8718
+ function safeShlexSplit(segment) {
8719
+ try {
8720
+ return shlexSplit(segment);
8721
+ } catch {
8722
+ return null;
8723
+ }
8724
+ }
8725
+ function segmentCommandIs(segment, name2) {
8726
+ return new RegExp("^\\s*(?:\\S*[/\\\\])?" + name2 + "(?:\\.exe)?\\b", "i").test(segment);
8727
+ }
8728
+ function extractSedInPlaceFiles(segment) {
8729
+ if (!segmentCommandIs(segment, "sed")) return [];
8730
+ const tokens = safeShlexSplit(segment);
8731
+ if (tokens === null) return [];
8732
+ const rest = tokens.slice(1);
8733
+ const inPlaceIdx = rest.findIndex((t) => t === "--in-place" || t.startsWith("--in-place=") || /^-[a-zA-Z]*i/.test(t));
8734
+ if (inPlaceIdx === -1) return [];
8735
+ const files = [];
8736
+ let scriptTaken = rest.some((t) => t === "-e" || t === "-f" || t.startsWith("--expression") || t.startsWith("--file"));
8737
+ for (let i = 0; i < rest.length; i++) {
8738
+ const t = rest[i];
8739
+ if (i === inPlaceIdx) {
8740
+ if (t === "-i" && rest[i + 1] === "") i++;
8741
+ continue;
8742
+ }
8743
+ if (t === "-e" || t === "-f" || t === "--expression" || t === "--file") {
8744
+ i++;
8745
+ continue;
8746
+ }
8747
+ if (t.length > 1 && t.startsWith("-")) continue;
8748
+ if (!scriptTaken) {
8749
+ scriptTaken = true;
8750
+ continue;
8751
+ }
8752
+ files.push(t);
8753
+ }
8754
+ return files;
8755
+ }
8756
+ function extractTeeFiles(segment) {
8757
+ if (!segmentCommandIs(segment, "tee")) return [];
8758
+ const tokens = safeShlexSplit(segment);
8759
+ if (tokens === null) return [];
8760
+ return tokens.slice(1).filter((t) => t.length > 0 && !t.startsWith("-"));
8761
+ }
8762
+ function extractRedirectTargets(segment) {
8763
+ const targets = [];
8764
+ const re = /(?:^|\s)[0-9]?>>?\s*("[^"]*"|'[^']*'|[^\s|&<>]+)/g;
8765
+ let m;
8766
+ while ((m = re.exec(segment)) !== null) {
8767
+ const raw = m[1];
8768
+ targets.push(raw.length >= 2 && (raw[0] === '"' || raw[0] === "'") ? raw.slice(1, -1) : raw);
8769
+ }
8770
+ return targets;
8771
+ }
8772
+ function extractGitRestorePathspecs(cmd) {
8773
+ const m = /^\s*git\s+restore\b(.*)$/i.exec(cmd);
8774
+ if (m === null) return [];
8775
+ const tokens = safeShlexSplit(m[1] ?? "");
8776
+ if (tokens === null) return [];
8777
+ const dashDash = tokens.indexOf("--");
8778
+ if (dashDash !== -1) return tokens.slice(dashDash + 1);
8779
+ const specs = [];
8780
+ let skipNext = false;
8781
+ for (const t of tokens) {
8782
+ if (skipNext) {
8783
+ skipNext = false;
8784
+ continue;
8785
+ }
8786
+ if (t === "-s" || t === "--source") {
8787
+ skipNext = true;
8788
+ continue;
8789
+ }
8790
+ if (t.startsWith("-")) continue;
8791
+ specs.push(t);
8792
+ }
8793
+ return specs;
8794
+ }
8795
+ function gitRepoRoot(gitDir) {
8796
+ const toplevel = runGit(["rev-parse", "--show-toplevel"], { cwd: gitDir, timeoutMs: 5e3 });
8797
+ return toplevel.exitCode === 0 && toplevel.stdout.trim() !== "" ? toplevel.stdout.trim() : gitDir;
8798
+ }
8799
+ function expandGitPathspecs(specs, gitDir) {
8800
+ if (specs.length === 0) return [];
8801
+ const listed = runGit(["ls-files", "--full-name", "--", ...specs], { cwd: gitDir, timeoutMs: 5e3 });
8802
+ if (listed.exitCode !== 0) return [];
8803
+ const repoRoot = gitRepoRoot(gitDir);
8804
+ return listed.stdout.split("\n").map((l) => l.trim()).filter((l) => l.length > 0).map((rel) => resolveIndexPath(rel, repoRoot));
8805
+ }
8806
+ function workingTreeStatusPaths(gitDir) {
8807
+ const status = runGit(["status", "--porcelain"], { cwd: gitDir, timeoutMs: 5e3 });
8808
+ if (status.exitCode !== 0) return [];
8809
+ const repoRoot = gitRepoRoot(gitDir);
8810
+ const out = [];
8811
+ for (const line of status.stdout.split("\n")) {
8812
+ if (line.length < 4) continue;
8813
+ let rel = line.slice(3).trim();
8814
+ if (rel.length === 0) continue;
8815
+ const arrow = rel.lastIndexOf(" -> ");
8816
+ if (arrow !== -1) rel = rel.slice(arrow + 4);
8817
+ if (rel.startsWith('"')) {
8818
+ try {
8819
+ rel = JSON.parse(rel);
8820
+ } catch {
8821
+ continue;
8822
+ }
8823
+ }
8824
+ out.push(resolveIndexPath(rel, repoRoot));
8825
+ }
8826
+ return out;
8827
+ }
8828
+ function enqueueRewrittenPath(absPath) {
8829
+ if (/(?:^|[/\\])(?:NUL|nul)$/.test(absPath) || absPath.replace(/\\/g, "/").endsWith("/dev/null")) return;
8830
+ try {
8831
+ if (!statSync5(absPath).isFile()) return;
8832
+ } catch {
8833
+ return;
8834
+ }
8835
+ enqueueDirtyPathSafe(absPath, { alreadyResolved: true });
8836
+ }
8837
+ function enqueueNonHeadMovingRewrites(cmd, rawCmd, cwd) {
8838
+ const atCwd = (f) => resolveIndexPath(resolveCdHintPath(rawCmd, f, cwd), cwd);
8839
+ const paths = [];
8840
+ let needsStatusSweep = GIT_STASH_APPLY_RE.test(cmd);
8841
+ if (GIT_RESTORE_RE.test(cmd) && !GIT_RESTORE_STAGED_ONLY_RE.test(cmd)) {
8842
+ paths.push(...expandGitPathspecs(extractGitRestorePathspecs(cmd), cwd));
8843
+ }
8844
+ for (const segment of splitShellSegments(cmd)) {
8845
+ if (PATCH_APPLY_SEGMENT_RE.test(segment) && !PATCH_APPLY_INSPECT_ONLY_RE.test(segment)) needsStatusSweep = true;
8846
+ if (isFormatterWriteSegment(segment)) needsStatusSweep = true;
8847
+ for (const f of extractSedInPlaceFiles(segment)) paths.push(atCwd(f));
8848
+ for (const f of extractTeeFiles(segment)) paths.push(atCwd(f));
8849
+ for (const f of extractRedirectTargets(segment)) paths.push(atCwd(f));
8850
+ }
8851
+ if (needsStatusSweep) paths.push(...workingTreeStatusPaths(cwd));
8852
+ for (const p of paths) enqueueRewrittenPath(p);
8853
+ }
8854
+ var PIPELINE_PASSTHROUGH_HEADS = /* @__PURE__ */ new Set(["head", "tail", "cat", "tee", "less", "more"]);
8855
+ 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)$/;
8856
+ function isCiBuildTestSegment(cleaned, cwd) {
8857
+ if (isTestRunnerCommand(cleaned) || isBuildCommand(cleaned) || isTscCommand(cleaned)) return true;
8858
+ if (/^\s*(npm|pnpm|yarn|bun)\s+(run\s+)?(test|build|lint|typecheck|check|ci|guards)\b/i.test(cleaned)) return true;
8859
+ if (/^\s*(cargo|go|dotnet|make|gradle|mvn|pytest|vitest|jest|eslint|ruff)\b/i.test(cleaned)) return true;
8860
+ const detected = detectFromCommand(cleaned, cwd ?? void 0);
8861
+ if (detected !== null && CI_FILTER_NAMES.test(detected.filter.name)) return true;
8862
+ return false;
8863
+ }
8864
+ function pipelineShapeFilter(cmd, cwd) {
8865
+ if (hasUnquotedOperator(cmd, ["&&", "||", ";"])) {
8866
+ const forSplit2 = cmd.replace(/\s2>(?:&1|\/dev\/null)/g, "");
8867
+ if (hasBareBackgroundOrNewline(forSplit2)) return null;
8868
+ const segments2 = splitShellSegments(forSplit2);
8869
+ if (segments2.length >= 2) {
8870
+ let recognizedCiCount = 0;
8871
+ for (const segment of segments2) {
8872
+ const cleaned = stripOutputPipeline(segment.trim());
8873
+ if (cleaned.length === 0) continue;
8874
+ if (isCiBuildTestSegment(cleaned, cwd)) {
8875
+ recognizedCiCount++;
8876
+ }
8877
+ }
8878
+ if (recognizedCiCount > 0) {
8879
+ return filterByName("generic-ci");
8880
+ }
8881
+ }
8882
+ return null;
8883
+ }
8884
+ const forSplit = cmd.replace(/\s2>(?:&1|\/dev\/null)/g, "");
8885
+ if (hasBareBackgroundOrNewline(forSplit)) return null;
8886
+ const segments = splitShellSegments(forSplit);
8887
+ if (segments.length < 2) return null;
8888
+ for (const segment of segments.slice(1)) {
8889
+ const head = safeShlexSplit(segment)?.[0];
8890
+ if (head === void 0) return null;
8891
+ if (!PIPELINE_PASSTHROUGH_HEADS.has(head.replace(/^.*[/\\]/, ""))) return null;
8892
+ }
8893
+ const detected = detectFromCommand(stripOutputPipeline(cmd), cwd ?? void 0);
8894
+ return detected === null ? null : detected.filter;
8895
+ }
8896
+ function pureFileReadPath(cmd) {
8897
+ const single = extractCatFile(cmd)?.filePath ?? extractHeadFile(cmd)?.filePath ?? extractTailFile(cmd)?.filePath ?? extractLineRangeRead(cmd)?.filePath;
8898
+ if (single !== void 0) return single;
8899
+ return singleFileCompoundReadPath(cmd);
8900
+ }
8901
+ function singleFileCompoundReadPath(cmd) {
8902
+ const reads = extractLineRangeReadsCompound(cmd);
8903
+ if (reads === null || reads.length !== 1) return null;
8904
+ return reads[0]?.filePath ?? null;
8905
+ }
8906
+ function deliveredLineNumbers(cmd, lineCount) {
8907
+ const ranged = extractLineRangeRead(cmd);
8908
+ if (ranged !== null) {
8909
+ const nums = [];
8910
+ for (const [lo, hi] of ranged.ranges) for (let n = lo; n <= hi; n++) nums.push(n);
8911
+ if (nums.length === 0) return null;
8912
+ while (nums.length < lineCount) nums.push((nums[nums.length - 1] ?? 0) + 1);
8913
+ return nums;
8914
+ }
8915
+ if (extractCatFile(cmd) !== null || extractHeadFile(cmd) !== null) return Array.from({ length: lineCount }, (_, i) => i + 1);
8916
+ if (singleFileCompoundReadPath(cmd) !== null) return Array.from({ length: lineCount }, () => null);
8917
+ return null;
8918
+ }
8919
+ 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;
8920
+ function isWholeFileDump(cmd, extractedPath) {
8921
+ const m = WHOLE_FILE_DUMP_RE.exec(cmd);
8922
+ if (m === null) return false;
8923
+ const shaped = m[1] ?? m[2] ?? m[3];
8924
+ return shaped !== void 0 && shaped === extractedPath;
8925
+ }
8926
+ function unwrapCompressCommand(executed) {
8927
+ const t = executed.trim();
8928
+ if (!/^(?:token-goat|tg)\s+compress\b/.test(t)) return null;
8929
+ let argv;
8930
+ try {
8931
+ argv = shlexSplit(t);
8932
+ } catch {
8933
+ return null;
8934
+ }
8935
+ for (let i = 0; i + 1 < argv.length; i++) {
8936
+ const tok = argv[i];
8937
+ if (tok === "-c" || tok === "--cmd") return argv[i + 1] ?? null;
8938
+ if (tok === "--cmd-b64") {
8939
+ try {
8940
+ return Buffer.from(argv[i + 1] ?? "", "base64").toString("utf8");
8941
+ } catch {
8942
+ return null;
8943
+ }
8944
+ }
8945
+ }
8946
+ return null;
8947
+ }
8948
+ function detectUnbalancedShellSyntax(cmd) {
8949
+ let inSingle = false;
8950
+ let inDouble = false;
8951
+ let i = 0;
8952
+ while (i < cmd.length) {
8953
+ const ch = cmd[i];
8954
+ if (inSingle) {
8955
+ if (ch === "'") {
8956
+ inSingle = false;
8957
+ }
8958
+ i++;
8959
+ continue;
8960
+ }
8961
+ if (inDouble) {
8962
+ if (ch === "\\" && i + 1 < cmd.length) {
8963
+ i += 2;
8964
+ continue;
8965
+ }
8966
+ if (ch === '"') {
8967
+ inDouble = false;
8968
+ }
8969
+ i++;
8970
+ continue;
8971
+ }
8972
+ if (ch === "\\" && i + 1 < cmd.length) {
8973
+ i += 2;
8974
+ continue;
8975
+ }
8976
+ if (ch === "$" && cmd[i + 1] === "(" && cmd[i + 2] === "(") {
8977
+ let depth = 2;
8978
+ let j = i + 3;
8979
+ while (j < cmd.length && depth > 0) {
8980
+ if (cmd[j] === "(") depth++;
8981
+ else if (cmd[j] === ")") depth--;
8982
+ j++;
8983
+ }
8984
+ i = j;
8985
+ continue;
8986
+ }
8987
+ if (ch === "$" && cmd[i + 1] === "'") {
8988
+ let j = i + 2;
8989
+ let closed = false;
8990
+ while (j < cmd.length) {
8991
+ if (cmd[j] === "\\") {
8992
+ j += 2;
8993
+ continue;
8994
+ }
8995
+ if (cmd[j] === "'") {
8996
+ j++;
8997
+ closed = true;
8998
+ break;
8999
+ }
9000
+ j++;
9001
+ }
9002
+ if (!closed) {
9003
+ inSingle = true;
9004
+ break;
9005
+ }
9006
+ i = j;
9007
+ continue;
9008
+ }
9009
+ if (ch === "#" && (i === 0 || /\s/.test(cmd[i - 1] ?? ""))) {
9010
+ const nl2 = cmd.indexOf("\n", i);
9011
+ i = nl2 === -1 ? cmd.length : nl2 + 1;
9012
+ continue;
9013
+ }
9014
+ if (ch === "'") {
9015
+ inSingle = true;
9016
+ i++;
9017
+ continue;
9018
+ }
9019
+ if (ch === '"') {
9020
+ inDouble = true;
9021
+ i++;
9022
+ continue;
9023
+ }
9024
+ if (ch === "<" && i + 1 < cmd.length && cmd[i + 1] === "<") {
9025
+ if (i + 2 < cmd.length && cmd[i + 2] === "<") {
9026
+ i += 3;
9027
+ continue;
9028
+ }
9029
+ let j = i + 2;
9030
+ let hasIndentModifier = false;
9031
+ if (j < cmd.length && (cmd[j] === "-" || cmd[j] === "~")) {
9032
+ hasIndentModifier = true;
9033
+ j++;
9034
+ }
9035
+ while (j < cmd.length && cmd[j] === " ") {
9036
+ j++;
9037
+ }
9038
+ if (j < cmd.length) {
9039
+ let delimStart = j;
9040
+ if (cmd[j] === '"' || cmd[j] === "'") {
9041
+ delimStart = j + 1;
9042
+ const quoteChar = cmd[j];
9043
+ let delimEnd = delimStart;
9044
+ while (delimEnd < cmd.length && cmd[delimEnd] !== quoteChar) {
9045
+ delimEnd++;
9046
+ }
9047
+ if (delimEnd >= cmd.length) {
9048
+ return "an unclosed quote in a heredoc opener";
9049
+ }
9050
+ j = delimEnd + 1;
9051
+ } else {
9052
+ let delimEnd = delimStart;
9053
+ while (delimEnd < cmd.length && /\w/.test(cmd.charAt(delimEnd))) {
9054
+ delimEnd++;
9055
+ }
9056
+ j = delimEnd;
9057
+ }
9058
+ const delimiter = cmd.slice(delimStart, j).replace(/["']/g, "");
9059
+ if (delimiter) {
9060
+ let scanPos = j;
9061
+ let terminatorEnd = -1;
9062
+ while (scanPos <= cmd.length) {
9063
+ const nl2 = cmd.indexOf("\n", scanPos);
9064
+ const lineEnd = nl2 === -1 ? cmd.length : nl2;
9065
+ let line = cmd.slice(scanPos, lineEnd);
9066
+ if (line.endsWith("\r")) {
9067
+ line = line.slice(0, -1);
9068
+ }
9069
+ const isMatch = hasIndentModifier ? line.trim() === delimiter : line === delimiter;
9070
+ if (isMatch) {
9071
+ terminatorEnd = nl2 === -1 ? cmd.length : nl2 + 1;
9072
+ break;
9073
+ }
9074
+ if (nl2 === -1) break;
9075
+ scanPos = nl2 + 1;
9076
+ }
9077
+ if (terminatorEnd === -1) {
9078
+ return `an unterminated heredoc (${delimiter} never appears on its own line)`;
9079
+ }
9080
+ i = terminatorEnd;
9081
+ continue;
9082
+ }
9083
+ }
9084
+ }
9085
+ i++;
9086
+ }
9087
+ if (inSingle) {
9088
+ return "an unclosed single quote";
8829
9089
  }
8830
- return "Output from a prior `" + cmdPreview + "` run is cached. Use `token-goat bash-output " + outputId + "` (or `--tail 50`, `--grep ERROR`) to re-inspect it without re-running.";
8831
- }
8832
- function shellQuoteSingle(s) {
8833
- return `'${s.replace(/'/g, `'\\''`)}'`;
8834
- }
8835
- function isCompressibleSingleCommand(cmd) {
8836
- if (!cmd || cmd.length > 65536) return false;
8837
- if (["$(", "`"].some((op) => cmd.includes(op))) return false;
8838
- if (hasUnquotedOperator(cmd, ["&&", "||", "|", ";", "<", ">"])) return false;
8839
- if (hasBareBackgroundOrNewline(cmd)) return false;
8840
- return true;
9090
+ if (inDouble) {
9091
+ return "an unclosed double quote";
9092
+ }
9093
+ return null;
8841
9094
  }
9095
+
9096
+ // src/hooks_bash.ts
8842
9097
  function maybeCompressRewrite(event, rawCmd, cmd) {
8843
9098
  if (process.env["TOKEN_GOAT_BASH_COMPRESS"] === "0") return null;
8844
9099
  if (event.raw["_tg_harness"] === "vscode") return null;
@@ -8864,96 +9119,6 @@ function maybeCompressRewrite(event, rawCmd, cmd) {
8864
9119
  const wrapped = `token-goat compress -f ${filterName} --timeout ${cfg.timeout_seconds} -c ${shellQuoteSingle(rawCmd)}`;
8865
9120
  return { hookType: "rewriteInput", updatedInput: { ...event.toolInput, command: wrapped } };
8866
9121
  }
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;
8901
- }
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");
8918
- }
8919
- }
8920
- return null;
8921
- }
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;
8930
- }
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;
8943
- }
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;
8952
- }
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;
8956
- }
8957
9122
  function elideServedShellLines(cmd, output, priorIds, unknownLineNumbers = false) {
8958
9123
  if (priorIds.length === 0) return null;
8959
9124
  const lines = output.split("\n");
@@ -9006,19 +9171,12 @@ function foldShellReadBodies(cmd, output, fileKey, cwd) {
9006
9171
  if (folded === null) return null;
9007
9172
  return { text: folded.numbered.join("\n"), folds: folded.folds };
9008
9173
  }
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
9174
  function foldShellReadStructure(cmd, filePath, output, fileKey, cwd) {
9017
9175
  if (!isWholeFileDump(cmd, filePath)) return null;
9018
9176
  const originalBytes = Buffer.byteLength(output, "utf-8");
9019
9177
  let onDisk;
9020
9178
  try {
9021
- onDisk = statSync4(fileKey).size;
9179
+ onDisk = statSync6(fileKey).size;
9022
9180
  } catch {
9023
9181
  return null;
9024
9182
  }
@@ -9216,168 +9374,6 @@ function maybeFoldCurlHtml(cmd, output, id) {
9216
9374
  }
9217
9375
  return emitRewrite(notice + fenced, "curl HTML body cleaned", { kind: "bash_compress:curl-html", originalBytes: deliveredOutputBytes(originalBytes) });
9218
9376
  }
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
9377
  function preBashHandlerInner(event) {
9382
9378
  const rawCmd = extractCommand(event);
9383
9379
  if (rawCmd === void 0) return passOutput();
@@ -9408,7 +9404,7 @@ function preBashHandlerInner(event) {
9408
9404
  }
9409
9405
  const toolResults = extractToolResultsFile(cmd);
9410
9406
  if (toolResults !== null) {
9411
- const { path: outPath } = toolResults;
9407
+ const outPath = displaySafePath(toolResults.path);
9412
9408
  recordStat("session_hint", 0, 0);
9413
9409
  return contextOutput(
9414
9410
  "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.'
@@ -9659,7 +9655,7 @@ function preBashHandlerInner(event) {
9659
9655
  const prevResolvedPath = prevPath !== null && commandPathIsTouchable(prevPath, event) ? resolveIndexPath(prevPath, preHookCwd ?? process.cwd()) : null;
9660
9656
  if (prevPath !== null && prevResolvedPath !== null && !existsSync(prevResolvedPath)) {
9661
9657
  clearCurlDownload(curlDl.url);
9662
- } else if (prevPath !== null && prevResolvedPath !== null && statSync4(prevResolvedPath).size >= loadConfig().hints.bash_dedup_min_bytes) {
9658
+ } else if (prevPath !== null && prevResolvedPath !== null && statSync6(prevResolvedPath).size >= loadConfig().hints.bash_dedup_min_bytes) {
9663
9659
  recordStat("session_hint", 0, 0);
9664
9660
  return denyOutput(
9665
9661
  "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.'
@@ -10151,7 +10147,7 @@ registerHook("post_tool_use", postTaskOutputHandler, { toolName: "TaskOutput" })
10151
10147
 
10152
10148
  // src/hooks_tool_failure.ts
10153
10149
  init_define_import_meta_env();
10154
- import { existsSync as existsSync2, readFileSync as readFileSync3, statSync as statSync5, writeFileSync as writeFileSync2 } from "node:fs";
10150
+ import { existsSync as existsSync2, readFileSync as readFileSync3, statSync as statSync7, writeFileSync as writeFileSync2 } from "node:fs";
10155
10151
  import { dirname as dirname2, relative } from "node:path";
10156
10152
  var FAILURE_SUFFIX = ".tool-failures.json";
10157
10153
  var MAX_TRACKED_FAILURES = 64;
@@ -10220,7 +10216,7 @@ function diagnoseEditFailure(event, errorText) {
10220
10216
  const absPath = normalizePath(filePath);
10221
10217
  if (!existsSync2(absPath)) return null;
10222
10218
  try {
10223
- const stat = statSync5(absPath);
10219
+ const stat = statSync7(absPath);
10224
10220
  if (stat.size > MAX_EDIT_DIAGNOSE_BYTES) return null;
10225
10221
  const fileContent = readFileSync3(absPath, "utf8");
10226
10222
  const relDisplay = displaySafeText(relative(process.cwd(), absPath).replace(/\\/g, "/") || absPath);
@@ -11419,6 +11415,10 @@ async function relayInProcess(eventName, rawPayload) {
11419
11415
  if (!process.env["CLAUDE_CODE_SESSION_ID"] && event.sessionId) {
11420
11416
  process.env["CLAUDE_CODE_SESSION_ID"] = event.sessionId;
11421
11417
  }
11418
+ const rawTranscriptPath = event.raw["transcript_path"];
11419
+ if (typeof rawTranscriptPath === "string" && rawTranscriptPath !== "") {
11420
+ setTranscriptPath(rawTranscriptPath, event.sessionId ?? "");
11421
+ }
11422
11422
  if (!process.env["TRACEPARENT"] && event.traceparent) {
11423
11423
  process.env["TRACEPARENT"] = event.traceparent;
11424
11424
  }