token-goat 2.9.12 → 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.
- package/README.md +20 -1
- package/dist/token-goat-chunk-2ESBO4IN.mjs +209 -0
- package/dist/token-goat-chunk-3BTK54F3.mjs +1733 -0
- package/dist/{token-goat-chunk-6NIGPPN6.mjs → token-goat-chunk-3XQPEJMV.mjs} +1 -1
- package/dist/token-goat-chunk-4SDX3QP3.mjs +122 -0
- package/dist/token-goat-chunk-7OGKZ7AP.mjs +142 -0
- package/dist/token-goat-chunk-AH6QILZM.mjs +26 -0
- package/dist/token-goat-chunk-AMYCQJX4.mjs +1548 -0
- package/dist/{token-goat-chunk-OTW7LC4Y.mjs → token-goat-chunk-ASVVF4JV.mjs} +13 -10
- package/dist/{token-goat-chunk-GCXX67HM.mjs → token-goat-chunk-DAMXYVIW.mjs} +12477 -12156
- package/dist/token-goat-chunk-DBNY4RLN.mjs +308 -0
- package/dist/token-goat-chunk-EG3663UT.mjs +226 -0
- package/dist/token-goat-chunk-EZNVAIR3.mjs +21 -0
- package/dist/token-goat-chunk-GMOUBOX4.mjs +386 -0
- package/dist/{token-goat-chunk-6F5TLJC7.mjs → token-goat-chunk-HF6H7RNK.mjs} +1 -1
- package/dist/{token-goat-chunk-GM6QZWCF.mjs → token-goat-chunk-IT6O3PNN.mjs} +2660 -6877
- package/dist/token-goat-chunk-NEI4NC54.mjs +424 -0
- package/dist/token-goat-chunk-O5WAMISC.mjs +167 -0
- package/dist/{token-goat-chunk-LHLQFGWQ.mjs → token-goat-chunk-OMNQUUIT.mjs} +9 -27
- package/dist/{token-goat-chunk-SF2CKCFQ.mjs → token-goat-chunk-PGGDW7DZ.mjs} +30 -13
- package/dist/token-goat-chunk-POBYR64E.mjs +1632 -0
- package/dist/token-goat-chunk-PRJVGIC5.mjs +58 -0
- package/dist/{token-goat-chunk-4CK445AW.mjs → token-goat-chunk-RUDOKYPJ.mjs} +10074 -9765
- package/dist/token-goat-chunk-SAQ5PG4L.mjs +2634 -0
- package/dist/token-goat-chunk-T2IWWTHB.mjs +3155 -0
- package/dist/{token-goat-chunk-TOGYS5A7.mjs → token-goat-chunk-VZYD4OZB.mjs} +1469 -5752
- package/dist/token-goat-chunk-XEH6KBWW.mjs +23 -0
- package/dist/token-goat-chunk-XQF5J25J.mjs +33 -0
- package/dist/token-goat-chunk-XTQAOTSO.mjs +89 -0
- package/dist/{token-goat-chunk-THLHC6QJ.mjs → token-goat-chunk-XVZ4MNQC.mjs} +614 -603
- package/dist/token-goat-chunk-Y4AFKTHK.mjs +22 -0
- package/dist/token-goat-chunk-YHGTGG6K.mjs +396 -0
- package/dist/{token-goat-chunk-SBYRP3X4.mjs → token-goat-chunk-YTQHZJXW.mjs} +61 -27
- package/dist/token-goat-chunk-Z6UXPYJA.mjs +62 -0
- package/dist/token-goat-chunk-ZD4EM4LR.mjs +30 -0
- package/dist/token-goat-chunk-ZFM4PWXL.mjs +585 -0
- package/dist/{token-goat-chunk-A6QTLAWO.mjs → token-goat-chunk-ZYNNQ36L.mjs} +128 -328
- package/dist/token-goat-hook.mjs +12 -6
- package/dist/token-goat.core.mjs +22 -7
- package/docs/cli.md +12 -8
- package/docs/security.md +1 -1
- package/package.json +4 -2
- package/dist/token-goat-chunk-HKFOH6JH.mjs +0 -28
- package/dist/token-goat-chunk-LOCOX2ML.mjs +0 -3564
- package/dist/token-goat-chunk-VRWX6QYW.mjs +0 -24
|
@@ -4,31 +4,25 @@ import {
|
|
|
4
4
|
canRunWrappedShell,
|
|
5
5
|
clipToDeliveryCap,
|
|
6
6
|
deliveredOutputBytes
|
|
7
|
-
} from "./token-goat-chunk-
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
summarizeResidentContext,
|
|
24
|
-
taskListPruneHint
|
|
25
|
-
} from "./token-goat-chunk-A6QTLAWO.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,74 +100,96 @@ 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
|
-
storeBlob,
|
|
125
112
|
storeOutput,
|
|
126
|
-
|
|
113
|
+
summarizeResidentContext,
|
|
127
114
|
takePendingLargeFileHint,
|
|
115
|
+
taskListPruneHint,
|
|
128
116
|
visionTokens,
|
|
129
117
|
visionTokensSaved,
|
|
130
118
|
vscodePathAllowed,
|
|
131
119
|
wasCliReadThisSession,
|
|
132
120
|
wasFileReadThisSession,
|
|
133
121
|
wasHintShown
|
|
134
|
-
} from "./token-goat-chunk-
|
|
122
|
+
} from "./token-goat-chunk-IT6O3PNN.mjs";
|
|
135
123
|
import {
|
|
124
|
+
BASH_OUTPUT_SUBDIR,
|
|
136
125
|
BODY_FIRST_TOOL_RESPONSE_KEYS,
|
|
137
126
|
OUTPUT_FIRST_TOOL_RESPONSE_KEYS,
|
|
127
|
+
commandHash,
|
|
138
128
|
compressOutput,
|
|
139
129
|
contextOutput,
|
|
130
|
+
countSymbols,
|
|
140
131
|
dedupeConsecutive,
|
|
141
132
|
denyOutput,
|
|
142
133
|
detectFromCommand,
|
|
143
|
-
detectHarness,
|
|
144
|
-
detectLanguage,
|
|
145
134
|
emitRewrite,
|
|
146
135
|
emitRewriteIfChanged,
|
|
147
|
-
envBool,
|
|
148
|
-
envInt,
|
|
149
136
|
estimateTokens,
|
|
150
137
|
extractToolResponseField,
|
|
151
138
|
extractToolResultText,
|
|
152
139
|
filterByName,
|
|
153
|
-
|
|
140
|
+
getBashOutput,
|
|
154
141
|
getCwd,
|
|
155
|
-
getDb,
|
|
156
142
|
getFilePath,
|
|
157
|
-
|
|
143
|
+
getMonitoringRecallHint,
|
|
158
144
|
getToolInput,
|
|
159
145
|
getToolName,
|
|
146
|
+
getTranscriptPath,
|
|
160
147
|
hasBareBackgroundOrNewline,
|
|
161
148
|
hasUnquotedOperator,
|
|
149
|
+
indexRecallEntry,
|
|
150
|
+
isBashEntryStale,
|
|
151
|
+
isBuildCommand,
|
|
162
152
|
isMcpErrorResponse,
|
|
163
153
|
isRewriteWorthwhile,
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
154
|
+
isScopedGitStatusOrDiffStatCommand,
|
|
155
|
+
isTestRunnerCommand,
|
|
156
|
+
isTextualContentBlock,
|
|
167
157
|
makeDedupHintHandlers,
|
|
158
|
+
mcpContentBlocks,
|
|
168
159
|
normalizePayload,
|
|
169
160
|
passOutput,
|
|
170
161
|
readCopilotHooksOwners,
|
|
171
|
-
recordStat,
|
|
172
|
-
redactSecrets,
|
|
173
162
|
registerHook,
|
|
174
163
|
resolveMinNetSavingsBytes,
|
|
175
164
|
runHook,
|
|
176
|
-
savedTokensFromBytes,
|
|
177
165
|
serializeOutput,
|
|
178
166
|
sessionStateKey,
|
|
179
167
|
shlexSplit,
|
|
180
|
-
shortFingerprint,
|
|
181
168
|
splitOwnTrailingNotices,
|
|
169
|
+
storeBashOutput,
|
|
170
|
+
summarizeOutputDelta,
|
|
171
|
+
withPreservedBlocks
|
|
172
|
+
} from "./token-goat-chunk-VZYD4OZB.mjs";
|
|
173
|
+
import {
|
|
174
|
+
detectLanguage,
|
|
175
|
+
getDb,
|
|
176
|
+
languageHasFlag,
|
|
177
|
+
recordStat,
|
|
178
|
+
redactSecrets,
|
|
179
|
+
savedTokensFromBytes,
|
|
180
|
+
storeBlob,
|
|
182
181
|
stripAnsiEscapes
|
|
183
|
-
} from "./token-goat-chunk-
|
|
182
|
+
} from "./token-goat-chunk-T2IWWTHB.mjs";
|
|
183
|
+
import {
|
|
184
|
+
detectHarness,
|
|
185
|
+
envBool,
|
|
186
|
+
envInt,
|
|
187
|
+
findProject,
|
|
188
|
+
getHarnessName,
|
|
189
|
+
isUnderSystemTemp,
|
|
190
|
+
loadConfig,
|
|
191
|
+
shortFingerprint
|
|
192
|
+
} from "./token-goat-chunk-SAQ5PG4L.mjs";
|
|
184
193
|
import {
|
|
185
194
|
ENV_KEYS,
|
|
186
195
|
IDENTICAL_READ_MIN_BODY_BYTES,
|
|
@@ -207,7 +216,7 @@ import {
|
|
|
207
216
|
runGit,
|
|
208
217
|
toDisplayPath,
|
|
209
218
|
toKB
|
|
210
|
-
} from "./token-goat-chunk-
|
|
219
|
+
} from "./token-goat-chunk-AMYCQJX4.mjs";
|
|
211
220
|
import {
|
|
212
221
|
init_define_import_meta_env
|
|
213
222
|
} from "./token-goat-chunk-A37V4PBF.mjs";
|
|
@@ -740,7 +749,7 @@ function mergeManifestFiles(parent, siblingFiles) {
|
|
|
740
749
|
}
|
|
741
750
|
return Array.from(byPath.values());
|
|
742
751
|
}
|
|
743
|
-
function buildManifest(sessionId, cwd) {
|
|
752
|
+
function buildManifest(sessionId, cwd, transcriptPath) {
|
|
744
753
|
const ownFiles = [...getSessionFiles().values()];
|
|
745
754
|
const siblingFiles = sessionId !== void 0 ? listSiblingSessionStates(sessionId).flatMap((s) => s.files) : [];
|
|
746
755
|
const files = siblingFiles.length > 0 ? mergeManifestFiles(ownFiles, siblingFiles) : ownFiles;
|
|
@@ -772,7 +781,7 @@ function buildManifest(sessionId, cwd) {
|
|
|
772
781
|
);
|
|
773
782
|
lines.push(...buildSafeToDiscardSection(files));
|
|
774
783
|
lines.push(...buildMemEpochSection());
|
|
775
|
-
return capManifestChars(lines.join("\n"), sessionId, cwd);
|
|
784
|
+
return capManifestChars(lines.join("\n"), sessionId, cwd, transcriptPath);
|
|
776
785
|
}
|
|
777
786
|
function gitDirtySignals(cwd) {
|
|
778
787
|
const timeoutMs = loadConfig().hints.git_hint_max_ms;
|
|
@@ -790,11 +799,11 @@ function gitDirtySignals(cwd) {
|
|
|
790
799
|
}
|
|
791
800
|
return { hasPendingDiff, hasUncommittedChanges };
|
|
792
801
|
}
|
|
793
|
-
function adaptiveCharBonus(sessionId, cwd) {
|
|
802
|
+
function adaptiveCharBonus(sessionId, cwd, transcriptPath) {
|
|
794
803
|
if (!cwd) return 0;
|
|
795
804
|
const cache = loadSessionCache(sessionId ?? "") ?? {};
|
|
796
805
|
const ageSecs = cache.created_ts !== void 0 ? Math.max(0, Date.now() / 1e3 - cache.created_ts) : 0;
|
|
797
|
-
const contextPressure = getContextPressure(cache);
|
|
806
|
+
const contextPressure = getContextPressure(cache, transcriptPath);
|
|
798
807
|
const { hasPendingDiff, hasUncommittedChanges } = gitDirtySignals(cwd);
|
|
799
808
|
if (!hasPendingDiff && !hasUncommittedChanges) return 0;
|
|
800
809
|
const baseline = computeAdaptiveBudget(cache, ageSecs, { contextPressure });
|
|
@@ -802,11 +811,11 @@ function adaptiveCharBonus(sessionId, cwd) {
|
|
|
802
811
|
const deltaTokens = Math.max(0, withGitSignal - baseline);
|
|
803
812
|
return deltaTokens * 3;
|
|
804
813
|
}
|
|
805
|
-
function capManifestChars(manifest, sessionId, cwd) {
|
|
814
|
+
function capManifestChars(manifest, sessionId, cwd, transcriptPath) {
|
|
806
815
|
const cap = loadConfig().compact_assist.max_manifest_chars;
|
|
807
816
|
if (cap <= 0) return manifest;
|
|
808
817
|
if (manifest.length <= cap) return manifest;
|
|
809
|
-
const effectiveCap = cap + adaptiveCharBonus(sessionId, cwd);
|
|
818
|
+
const effectiveCap = cap + adaptiveCharBonus(sessionId, cwd, transcriptPath);
|
|
810
819
|
if (manifest.length <= effectiveCap) return manifest;
|
|
811
820
|
const omitted = manifest.length - effectiveCap;
|
|
812
821
|
return manifest.slice(0, effectiveCap) + `
|
|
@@ -879,7 +888,7 @@ function summaryBudgetDirective(budgetChars) {
|
|
|
879
888
|
function preCompactHandler(event) {
|
|
880
889
|
const out = loadConfig().compact_assist.enabled ? contextOutput(`${MANIFEST_PREAMBLE}${summaryBudgetDirective(loadConfig().compact_assist.summary_budget_chars)}
|
|
881
890
|
|
|
882
|
-
${buildManifest(event.sessionId, getCwd(event))}`) : passOutput();
|
|
891
|
+
${buildManifest(event.sessionId, getCwd(event), getTranscriptPath(event))}`) : passOutput();
|
|
883
892
|
markCompacted();
|
|
884
893
|
return out;
|
|
885
894
|
}
|
|
@@ -927,7 +936,7 @@ init_define_import_meta_env();
|
|
|
927
936
|
var TRACKED_SKILL = "token-goat";
|
|
928
937
|
var MAX_COMMANDS_SHOWN = 8;
|
|
929
938
|
async function currentCommandNames() {
|
|
930
|
-
const { buildProgram } = await import("./token-goat-chunk-
|
|
939
|
+
const { buildProgram } = await import("./token-goat-chunk-O5WAMISC.mjs");
|
|
931
940
|
return flattenCommandNames(buildCommandManifest(buildProgram()));
|
|
932
941
|
}
|
|
933
942
|
async function recordSkillVersionSnapshot(sessionId, skillName) {
|
|
@@ -7638,115 +7647,21 @@ registerHook("post_tool_use", postSkillHandler, { toolName: "Skill" });
|
|
|
7638
7647
|
|
|
7639
7648
|
// src/hooks_bash.ts
|
|
7640
7649
|
init_define_import_meta_env();
|
|
7641
|
-
import { statSync as
|
|
7642
|
-
|
|
7643
|
-
|
|
7644
|
-
|
|
7645
|
-
|
|
7646
|
-
|
|
7647
|
-
|
|
7648
|
-
|
|
7649
|
-
|
|
7650
|
-
}
|
|
7651
|
-
function hasTestRunScopeOrBudget(cmd) {
|
|
7652
|
-
const hasTimeout = /(?:^|\s)(?:timeout(?:\.exe)?\s+\S+|--(?:test)?timeout(?:=|\s)\S+)/i.test(cmd);
|
|
7653
|
-
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);
|
|
7654
|
-
return hasTimeout || hasSelector;
|
|
7655
|
-
}
|
|
7656
|
-
function isDirectTestRunnerCommand(cmd) {
|
|
7657
|
-
return /^(?:pytest|(?:npx\s+)?(?:jest|vitest)|go\s+test|cargo\s+test)\b/i.test(cmd);
|
|
7658
|
-
}
|
|
7659
|
-
function extractCdPrefixDirs(rawCmd) {
|
|
7660
|
-
const prefixMatch = rawCmd.match(CD_PREFIX_RE);
|
|
7661
|
-
if (prefixMatch === null) return [];
|
|
7662
|
-
const dirs = [];
|
|
7663
|
-
const segmentPattern = /cd\s+(?:"([^"]*)"|'([^']*)'|(\S+))[ \t]*(?:&&|;|\r?\n)/g;
|
|
7664
|
-
let match;
|
|
7665
|
-
while ((match = segmentPattern.exec(prefixMatch[0])) !== null) {
|
|
7666
|
-
const dir = match[1] ?? match[2] ?? match[3];
|
|
7667
|
-
if (dir !== void 0) dirs.push(dir);
|
|
7668
|
-
}
|
|
7669
|
-
return dirs;
|
|
7670
|
-
}
|
|
7671
|
-
function resolveCdHintPath(rawCmd, filePath, cwd) {
|
|
7672
|
-
if (extractCdPrefixDirs(rawCmd).length === 0) return filePath;
|
|
7673
|
-
return resolveIndexPath(filePath, cdPrefixCwd(rawCmd, cwd));
|
|
7674
|
-
}
|
|
7675
|
-
function cdPrefixCwd(rawCmd, cwd) {
|
|
7676
|
-
let dir = cwd;
|
|
7677
|
-
for (const target of extractCdPrefixDirs(rawCmd)) dir = resolveIndexPath(target, dir);
|
|
7678
|
-
return dir;
|
|
7679
|
-
}
|
|
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";
|
|
7680
7659
|
function surgicalHintFor(hintPath, isEnv, isConfig, isDoc) {
|
|
7681
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.';
|
|
7682
7661
|
}
|
|
7683
7662
|
function surgicalHintForConfigDoc(filePath, isConfig, isDoc, isSql) {
|
|
7684
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.';
|
|
7685
7664
|
}
|
|
7686
|
-
function stripOutputPipeline(cmd) {
|
|
7687
|
-
let inSingle = false;
|
|
7688
|
-
let inDouble = false;
|
|
7689
|
-
let cut = cmd.length;
|
|
7690
|
-
let backslashes = 0;
|
|
7691
|
-
for (let i = 0; i < cmd.length; i++) {
|
|
7692
|
-
const ch = cmd[i];
|
|
7693
|
-
if (ch === "\\") {
|
|
7694
|
-
backslashes++;
|
|
7695
|
-
continue;
|
|
7696
|
-
}
|
|
7697
|
-
const escaped = backslashes % 2 === 1;
|
|
7698
|
-
backslashes = 0;
|
|
7699
|
-
if (ch === "'" && !inDouble) {
|
|
7700
|
-
if (!escaped) inSingle = !inSingle;
|
|
7701
|
-
} else if (ch === '"' && !inSingle) {
|
|
7702
|
-
if (!escaped) inDouble = !inDouble;
|
|
7703
|
-
} else if (ch === "|" && !inSingle && !inDouble) {
|
|
7704
|
-
if (cmd[i + 1] === "|") {
|
|
7705
|
-
i++;
|
|
7706
|
-
continue;
|
|
7707
|
-
}
|
|
7708
|
-
cut = i;
|
|
7709
|
-
break;
|
|
7710
|
-
}
|
|
7711
|
-
}
|
|
7712
|
-
let base = cmd.slice(0, cut);
|
|
7713
|
-
let prev;
|
|
7714
|
-
do {
|
|
7715
|
-
prev = base;
|
|
7716
|
-
const masked = base.replace(/"((?:[^"\\]|\\.)*)"/g, (_m, inner) => '"' + " ".repeat(inner.length) + '"').replace(/'([^']*)'/g, (_m, inner) => "'" + " ".repeat(inner.length) + "'");
|
|
7717
|
-
const newMasked = masked.replace(/\s*(?:[0-9]*>&[0-9]+|[0-9&]*>>?\s*(?:"[^"]*"|'[^']*'|[^\s|]+))\s*$/, "");
|
|
7718
|
-
if (newMasked.length < masked.length) {
|
|
7719
|
-
base = base.slice(0, newMasked.length);
|
|
7720
|
-
}
|
|
7721
|
-
} while (base !== prev);
|
|
7722
|
-
return base.trim();
|
|
7723
|
-
}
|
|
7724
|
-
function pipelineDivergenceNote(cmd, entryCommand) {
|
|
7725
|
-
if (entryCommand === cmd) return "";
|
|
7726
|
-
const preview = entryCommand.length > 60 ? entryCommand.slice(0, 57) + "..." : entryCommand;
|
|
7727
|
-
return " (cached from a differently-piped run, `" + preview + "` \u2014 verify it covers what you need before trusting it)";
|
|
7728
|
-
}
|
|
7729
|
-
function extractCommand(event) {
|
|
7730
|
-
const cmd = event.toolInput["command"];
|
|
7731
|
-
return typeof cmd === "string" && cmd.trim() !== "" ? cmd.trim() : void 0;
|
|
7732
|
-
}
|
|
7733
|
-
var HEAD_MOVING_GIT_RE = /^\s*git\s+(?:checkout|switch|pull|merge|rebase|reset|cherry-pick)\b/i;
|
|
7734
|
-
var PATH_SCOPED_CHECKOUT_RE = /^\s*git\s+checkout\s+(?:.*\s)?--(?:\s|$)/i;
|
|
7735
|
-
function isHeadMovingGitCommand(cmd) {
|
|
7736
|
-
if (!HEAD_MOVING_GIT_RE.test(cmd)) return false;
|
|
7737
|
-
if (PATH_SCOPED_CHECKOUT_RE.test(cmd)) return false;
|
|
7738
|
-
return true;
|
|
7739
|
-
}
|
|
7740
|
-
var ORIG_HEAD_ELIGIBLE_GIT_RE = /^\s*git\s+(?:pull|merge|rebase)\b/i;
|
|
7741
|
-
var ORIG_HEAD_REFLOG_MSG_RE = /^(merge\s|rebase\s\(|pull\s)/i;
|
|
7742
|
-
var GIT_RESTORE_RE = /^\s*git\s+restore\b/i;
|
|
7743
|
-
var GIT_RESTORE_STAGED_ONLY_RE = /(?:^|\s)(?:--staged|-S)(?:\s|$)/;
|
|
7744
|
-
var GIT_STASH_APPLY_RE = /^\s*git\s+stash\s+(?:pop|apply)\b/i;
|
|
7745
|
-
var PATCH_APPLY_SEGMENT_RE = /^\s*(?:\S*[/\\])?(?:git\s+apply|patch)\b/i;
|
|
7746
|
-
var PATCH_APPLY_INSPECT_ONLY_RE = /(?:^|\s)--(?:check|stat|numstat|summary|dry-run)(?:\s|=|$)/i;
|
|
7747
|
-
function isFormatterWriteSegment(segment) {
|
|
7748
|
-
return /(?:^|\s|[/\\])(?:prettier|eslint)\b/i.test(segment) && /(?:^|\s)--(?:write|fix)(?:\s|$)/i.test(segment);
|
|
7749
|
-
}
|
|
7750
7665
|
function splitShellSegments(cmd) {
|
|
7751
7666
|
const segments = [];
|
|
7752
7667
|
let cur = "";
|
|
@@ -7779,174 +7694,38 @@ function splitShellSegments(cmd) {
|
|
|
7779
7694
|
segments.push(cur);
|
|
7780
7695
|
return segments.map((s) => s.trim()).filter((s) => s.length > 0);
|
|
7781
7696
|
}
|
|
7782
|
-
function
|
|
7783
|
-
|
|
7784
|
-
|
|
7785
|
-
|
|
7786
|
-
|
|
7787
|
-
|
|
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);
|
|
7788
7704
|
}
|
|
7789
|
-
function
|
|
7790
|
-
|
|
7705
|
+
function isOrchestratorStateFile(filePath) {
|
|
7706
|
+
const basename3 = (filePath.includes("/") ? filePath.split("/").at(-1) : filePath.split("\\").at(-1)) ?? filePath;
|
|
7707
|
+
return /^\.improve-state-/.test(basename3);
|
|
7791
7708
|
}
|
|
7792
|
-
function
|
|
7793
|
-
|
|
7794
|
-
|
|
7795
|
-
if (tokens === null) return [];
|
|
7796
|
-
const rest = tokens.slice(1);
|
|
7797
|
-
const inPlaceIdx = rest.findIndex((t) => t === "--in-place" || t.startsWith("--in-place=") || /^-[a-zA-Z]*i/.test(t));
|
|
7798
|
-
if (inPlaceIdx === -1) return [];
|
|
7799
|
-
const files = [];
|
|
7800
|
-
let scriptTaken = rest.some((t) => t === "-e" || t === "-f" || t.startsWith("--expression") || t.startsWith("--file"));
|
|
7801
|
-
for (let i = 0; i < rest.length; i++) {
|
|
7802
|
-
const t = rest[i];
|
|
7803
|
-
if (i === inPlaceIdx) {
|
|
7804
|
-
if (t === "-i" && rest[i + 1] === "") i++;
|
|
7805
|
-
continue;
|
|
7806
|
-
}
|
|
7807
|
-
if (t === "-e" || t === "-f" || t === "--expression" || t === "--file") {
|
|
7808
|
-
i++;
|
|
7809
|
-
continue;
|
|
7810
|
-
}
|
|
7811
|
-
if (t.length > 1 && t.startsWith("-")) continue;
|
|
7812
|
-
if (!scriptTaken) {
|
|
7813
|
-
scriptTaken = true;
|
|
7814
|
-
continue;
|
|
7815
|
-
}
|
|
7816
|
-
files.push(t);
|
|
7817
|
-
}
|
|
7818
|
-
return files;
|
|
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;
|
|
7819
7712
|
}
|
|
7820
|
-
function
|
|
7821
|
-
|
|
7822
|
-
const
|
|
7823
|
-
|
|
7824
|
-
|
|
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 };
|
|
7825
7723
|
}
|
|
7826
|
-
function
|
|
7827
|
-
const
|
|
7828
|
-
const
|
|
7829
|
-
|
|
7830
|
-
|
|
7831
|
-
const raw = m[1];
|
|
7832
|
-
targets.push(raw.length >= 2 && (raw[0] === '"' || raw[0] === "'") ? raw.slice(1, -1) : raw);
|
|
7833
|
-
}
|
|
7834
|
-
return targets;
|
|
7835
|
-
}
|
|
7836
|
-
function extractGitRestorePathspecs(cmd) {
|
|
7837
|
-
const m = /^\s*git\s+restore\b(.*)$/i.exec(cmd);
|
|
7838
|
-
if (m === null) return [];
|
|
7839
|
-
const tokens = safeShlexSplit(m[1] ?? "");
|
|
7840
|
-
if (tokens === null) return [];
|
|
7841
|
-
const dashDash = tokens.indexOf("--");
|
|
7842
|
-
if (dashDash !== -1) return tokens.slice(dashDash + 1);
|
|
7843
|
-
const specs = [];
|
|
7844
|
-
let skipNext = false;
|
|
7845
|
-
for (const t of tokens) {
|
|
7846
|
-
if (skipNext) {
|
|
7847
|
-
skipNext = false;
|
|
7848
|
-
continue;
|
|
7849
|
-
}
|
|
7850
|
-
if (t === "-s" || t === "--source") {
|
|
7851
|
-
skipNext = true;
|
|
7852
|
-
continue;
|
|
7853
|
-
}
|
|
7854
|
-
if (t.startsWith("-")) continue;
|
|
7855
|
-
specs.push(t);
|
|
7856
|
-
}
|
|
7857
|
-
return specs;
|
|
7858
|
-
}
|
|
7859
|
-
function gitRepoRoot(gitDir) {
|
|
7860
|
-
const toplevel = runGit(["rev-parse", "--show-toplevel"], { cwd: gitDir, timeoutMs: 5e3 });
|
|
7861
|
-
return toplevel.exitCode === 0 && toplevel.stdout.trim() !== "" ? toplevel.stdout.trim() : gitDir;
|
|
7862
|
-
}
|
|
7863
|
-
function expandGitPathspecs(specs, gitDir) {
|
|
7864
|
-
if (specs.length === 0) return [];
|
|
7865
|
-
const listed = runGit(["ls-files", "--full-name", "--", ...specs], { cwd: gitDir, timeoutMs: 5e3 });
|
|
7866
|
-
if (listed.exitCode !== 0) return [];
|
|
7867
|
-
const repoRoot = gitRepoRoot(gitDir);
|
|
7868
|
-
return listed.stdout.split("\n").map((l) => l.trim()).filter((l) => l.length > 0).map((rel) => resolveIndexPath(rel, repoRoot));
|
|
7869
|
-
}
|
|
7870
|
-
function workingTreeStatusPaths(gitDir) {
|
|
7871
|
-
const status = runGit(["status", "--porcelain"], { cwd: gitDir, timeoutMs: 5e3 });
|
|
7872
|
-
if (status.exitCode !== 0) return [];
|
|
7873
|
-
const repoRoot = gitRepoRoot(gitDir);
|
|
7874
|
-
const out = [];
|
|
7875
|
-
for (const line of status.stdout.split("\n")) {
|
|
7876
|
-
if (line.length < 4) continue;
|
|
7877
|
-
let rel = line.slice(3).trim();
|
|
7878
|
-
if (rel.length === 0) continue;
|
|
7879
|
-
const arrow = rel.lastIndexOf(" -> ");
|
|
7880
|
-
if (arrow !== -1) rel = rel.slice(arrow + 4);
|
|
7881
|
-
if (rel.startsWith('"')) {
|
|
7882
|
-
try {
|
|
7883
|
-
rel = JSON.parse(rel);
|
|
7884
|
-
} catch {
|
|
7885
|
-
continue;
|
|
7886
|
-
}
|
|
7887
|
-
}
|
|
7888
|
-
out.push(resolveIndexPath(rel, repoRoot));
|
|
7889
|
-
}
|
|
7890
|
-
return out;
|
|
7891
|
-
}
|
|
7892
|
-
function enqueueRewrittenPath(absPath) {
|
|
7893
|
-
if (/(?:^|[/\\])(?:NUL|nul)$/.test(absPath) || absPath.replace(/\\/g, "/").endsWith("/dev/null")) return;
|
|
7894
|
-
try {
|
|
7895
|
-
if (!statSync4(absPath).isFile()) return;
|
|
7896
|
-
} catch {
|
|
7897
|
-
return;
|
|
7898
|
-
}
|
|
7899
|
-
enqueueDirtyPathSafe(absPath, { alreadyResolved: true });
|
|
7900
|
-
}
|
|
7901
|
-
function enqueueNonHeadMovingRewrites(cmd, rawCmd, cwd) {
|
|
7902
|
-
const atCwd = (f) => resolveIndexPath(resolveCdHintPath(rawCmd, f, cwd), cwd);
|
|
7903
|
-
const paths = [];
|
|
7904
|
-
let needsStatusSweep = GIT_STASH_APPLY_RE.test(cmd);
|
|
7905
|
-
if (GIT_RESTORE_RE.test(cmd) && !GIT_RESTORE_STAGED_ONLY_RE.test(cmd)) {
|
|
7906
|
-
paths.push(...expandGitPathspecs(extractGitRestorePathspecs(cmd), cwd));
|
|
7907
|
-
}
|
|
7908
|
-
for (const segment of splitShellSegments(cmd)) {
|
|
7909
|
-
if (PATCH_APPLY_SEGMENT_RE.test(segment) && !PATCH_APPLY_INSPECT_ONLY_RE.test(segment)) needsStatusSweep = true;
|
|
7910
|
-
if (isFormatterWriteSegment(segment)) needsStatusSweep = true;
|
|
7911
|
-
for (const f of extractSedInPlaceFiles(segment)) paths.push(atCwd(f));
|
|
7912
|
-
for (const f of extractTeeFiles(segment)) paths.push(atCwd(f));
|
|
7913
|
-
for (const f of extractRedirectTargets(segment)) paths.push(atCwd(f));
|
|
7914
|
-
}
|
|
7915
|
-
if (needsStatusSweep) paths.push(...workingTreeStatusPaths(cwd));
|
|
7916
|
-
for (const p of paths) enqueueRewrittenPath(p);
|
|
7917
|
-
}
|
|
7918
|
-
function isTempPath(fp) {
|
|
7919
|
-
const norm = fp.replace(/\\/g, "/");
|
|
7920
|
-
return /^\/tmp\//i.test(norm) || /\/var\/folders\//i.test(norm) || // Anchored to a local drive root on purpose. Unanchored, this clause said yes to
|
|
7921
|
-
// `//host/share/AppData/Local/Temp/x.md`, and the caller's answer to yes is to stat the path --
|
|
7922
|
-
// which on Windows opens an SMB session to whatever host a command named. A temp directory is
|
|
7923
|
-
// somewhere on a local volume; nothing that starts with two separators is one.
|
|
7924
|
-
/^[a-z]:\/(?:[^/]+\/)*AppData\/Local\/Temp\//i.test(norm) || norm.startsWith("/c/Users/") && norm.includes("/AppData/Local/Temp/") || isUnderSystemTemp(fp);
|
|
7925
|
-
}
|
|
7926
|
-
function isOrchestratorStateFile(filePath) {
|
|
7927
|
-
const basename3 = (filePath.includes("/") ? filePath.split("/").at(-1) : filePath.split("\\").at(-1)) ?? filePath;
|
|
7928
|
-
return /^\.improve-state-/.test(basename3);
|
|
7929
|
-
}
|
|
7930
|
-
function extractCatSourceFile(cmd) {
|
|
7931
|
-
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);
|
|
7932
|
-
return m?.[1] ?? null;
|
|
7933
|
-
}
|
|
7934
|
-
function classifyFileExtensions(filePath) {
|
|
7935
|
-
const basename3 = (filePath.includes("/") ? filePath.split("/").at(-1) : filePath.split("\\").at(-1)) ?? filePath;
|
|
7936
|
-
const isEnvFile = /^\.env(\.\w+)?$/i.test(basename3);
|
|
7937
|
-
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);
|
|
7938
|
-
if (!hasKnownExt && !isEnvFile) return null;
|
|
7939
|
-
const isSql = /\.sql$/i.test(filePath);
|
|
7940
|
-
const isDoc = /\.(?:md|mdx|rst|txt)$/i.test(filePath);
|
|
7941
|
-
const isEnv = isEnvFile || /\.env$/i.test(filePath);
|
|
7942
|
-
const isConfig = /\.(?:json|yaml|yml|toml|conf|cfg|ini|properties)$/i.test(filePath);
|
|
7943
|
-
return { isDoc, isEnv, isConfig, isSql };
|
|
7944
|
-
}
|
|
7945
|
-
function classifyDocConfig(filePath) {
|
|
7946
|
-
const isDoc = /\.(?:md|mdx|rst|txt)$/i.test(filePath);
|
|
7947
|
-
const isConfig = /\.(?:json|yaml|yml|toml|conf|cfg|ini|properties)$/i.test(filePath);
|
|
7948
|
-
const isSql = /\.sql$/i.test(filePath);
|
|
7949
|
-
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 };
|
|
7950
7729
|
}
|
|
7951
7730
|
function classifyCatPath(filePath, cmd0) {
|
|
7952
7731
|
if (isTempPath(filePath)) return null;
|
|
@@ -8817,21 +8596,504 @@ function buildRecallHint(cmd, outputId) {
|
|
|
8817
8596
|
if (isTscCommand(cmd)) {
|
|
8818
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.';
|
|
8819
8598
|
}
|
|
8820
|
-
if (isDevServerCommand(cmd)) {
|
|
8821
|
-
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";
|
|
8822
9089
|
}
|
|
8823
|
-
|
|
8824
|
-
|
|
8825
|
-
|
|
8826
|
-
return
|
|
8827
|
-
}
|
|
8828
|
-
function isCompressibleSingleCommand(cmd) {
|
|
8829
|
-
if (!cmd || cmd.length > 65536) return false;
|
|
8830
|
-
if (["$(", "`"].some((op) => cmd.includes(op))) return false;
|
|
8831
|
-
if (hasUnquotedOperator(cmd, ["&&", "||", "|", ";", "<", ">"])) return false;
|
|
8832
|
-
if (hasBareBackgroundOrNewline(cmd)) return false;
|
|
8833
|
-
return true;
|
|
9090
|
+
if (inDouble) {
|
|
9091
|
+
return "an unclosed double quote";
|
|
9092
|
+
}
|
|
9093
|
+
return null;
|
|
8834
9094
|
}
|
|
9095
|
+
|
|
9096
|
+
// src/hooks_bash.ts
|
|
8835
9097
|
function maybeCompressRewrite(event, rawCmd, cmd) {
|
|
8836
9098
|
if (process.env["TOKEN_GOAT_BASH_COMPRESS"] === "0") return null;
|
|
8837
9099
|
if (event.raw["_tg_harness"] === "vscode") return null;
|
|
@@ -8857,96 +9119,6 @@ function maybeCompressRewrite(event, rawCmd, cmd) {
|
|
|
8857
9119
|
const wrapped = `token-goat compress -f ${filterName} --timeout ${cfg.timeout_seconds} -c ${shellQuoteSingle(rawCmd)}`;
|
|
8858
9120
|
return { hookType: "rewriteInput", updatedInput: { ...event.toolInput, command: wrapped } };
|
|
8859
9121
|
}
|
|
8860
|
-
var PIPELINE_PASSTHROUGH_HEADS = /* @__PURE__ */ new Set(["head", "tail", "cat", "tee", "less", "more"]);
|
|
8861
|
-
var CI_BUILD_TEST_FILTER_NAMES = /* @__PURE__ */ new Set([
|
|
8862
|
-
"generic-ci",
|
|
8863
|
-
"jest",
|
|
8864
|
-
"vitest",
|
|
8865
|
-
"pytest",
|
|
8866
|
-
"go_test",
|
|
8867
|
-
"cargo_test",
|
|
8868
|
-
"cargo",
|
|
8869
|
-
"go",
|
|
8870
|
-
"make",
|
|
8871
|
-
"cmake",
|
|
8872
|
-
"gradle",
|
|
8873
|
-
"maven",
|
|
8874
|
-
"dotnet",
|
|
8875
|
-
"turbo",
|
|
8876
|
-
"nx",
|
|
8877
|
-
"lerna",
|
|
8878
|
-
"webpack",
|
|
8879
|
-
"eslint",
|
|
8880
|
-
"ruff",
|
|
8881
|
-
"clippy",
|
|
8882
|
-
"flake8",
|
|
8883
|
-
"mypy",
|
|
8884
|
-
"prettier",
|
|
8885
|
-
"tsc"
|
|
8886
|
-
]);
|
|
8887
|
-
function isCiBuildTestSegment(cleaned, cwd) {
|
|
8888
|
-
if (isTestRunnerCommand(cleaned) || isBuildCommand(cleaned) || isTscCommand(cleaned)) return true;
|
|
8889
|
-
if (/^\s*(npm|pnpm|yarn|bun)\s+(run\s+)?(test|build|lint|typecheck|check|ci|guards)\b/i.test(cleaned)) return true;
|
|
8890
|
-
if (/^\s*(cargo|go|dotnet|make|gradle|mvn|pytest|vitest|jest|eslint|ruff)\b/i.test(cleaned)) return true;
|
|
8891
|
-
const detected = detectFromCommand(cleaned, cwd ?? void 0);
|
|
8892
|
-
if (detected !== null && CI_BUILD_TEST_FILTER_NAMES.has(detected.filter.name)) return true;
|
|
8893
|
-
return false;
|
|
8894
|
-
}
|
|
8895
|
-
function pipelineShapeFilter(cmd, cwd) {
|
|
8896
|
-
if (hasUnquotedOperator(cmd, ["&&", "||", ";"])) {
|
|
8897
|
-
const forSplit2 = cmd.replace(/\s2>(?:&1|\/dev\/null)/g, "");
|
|
8898
|
-
if (hasBareBackgroundOrNewline(forSplit2)) return null;
|
|
8899
|
-
const segments2 = splitShellSegments(forSplit2);
|
|
8900
|
-
if (segments2.length >= 2) {
|
|
8901
|
-
let recognizedCiCount = 0;
|
|
8902
|
-
for (const segment of segments2) {
|
|
8903
|
-
const cleaned = stripOutputPipeline(segment.trim());
|
|
8904
|
-
if (cleaned.length === 0) continue;
|
|
8905
|
-
if (isCiBuildTestSegment(cleaned, cwd)) {
|
|
8906
|
-
recognizedCiCount++;
|
|
8907
|
-
}
|
|
8908
|
-
}
|
|
8909
|
-
if (recognizedCiCount > 0) {
|
|
8910
|
-
return filterByName("generic-ci");
|
|
8911
|
-
}
|
|
8912
|
-
}
|
|
8913
|
-
return null;
|
|
8914
|
-
}
|
|
8915
|
-
const forSplit = cmd.replace(/\s2>(?:&1|\/dev\/null)/g, "");
|
|
8916
|
-
if (hasBareBackgroundOrNewline(forSplit)) return null;
|
|
8917
|
-
const segments = splitShellSegments(forSplit);
|
|
8918
|
-
if (segments.length < 2) return null;
|
|
8919
|
-
for (const segment of segments.slice(1)) {
|
|
8920
|
-
const head = safeShlexSplit(segment)?.[0];
|
|
8921
|
-
if (head === void 0) return null;
|
|
8922
|
-
if (!PIPELINE_PASSTHROUGH_HEADS.has(head.replace(/^.*[/\\]/, ""))) return null;
|
|
8923
|
-
}
|
|
8924
|
-
const detected = detectFromCommand(stripOutputPipeline(cmd), cwd ?? void 0);
|
|
8925
|
-
return detected === null ? null : detected.filter;
|
|
8926
|
-
}
|
|
8927
|
-
function pureFileReadPath(cmd) {
|
|
8928
|
-
const single = extractCatFile(cmd)?.filePath ?? extractHeadFile(cmd)?.filePath ?? extractTailFile(cmd)?.filePath ?? extractLineRangeRead(cmd)?.filePath;
|
|
8929
|
-
if (single !== void 0) return single;
|
|
8930
|
-
return singleFileCompoundReadPath(cmd);
|
|
8931
|
-
}
|
|
8932
|
-
function singleFileCompoundReadPath(cmd) {
|
|
8933
|
-
const reads = extractLineRangeReadsCompound(cmd);
|
|
8934
|
-
if (reads === null || reads.length !== 1) return null;
|
|
8935
|
-
return reads[0]?.filePath ?? null;
|
|
8936
|
-
}
|
|
8937
|
-
function deliveredLineNumbers(cmd, lineCount) {
|
|
8938
|
-
const ranged = extractLineRangeRead(cmd);
|
|
8939
|
-
if (ranged !== null) {
|
|
8940
|
-
const nums = [];
|
|
8941
|
-
for (const [lo, hi] of ranged.ranges) for (let n = lo; n <= hi; n++) nums.push(n);
|
|
8942
|
-
if (nums.length === 0) return null;
|
|
8943
|
-
while (nums.length < lineCount) nums.push((nums[nums.length - 1] ?? 0) + 1);
|
|
8944
|
-
return nums;
|
|
8945
|
-
}
|
|
8946
|
-
if (extractCatFile(cmd) !== null || extractHeadFile(cmd) !== null) return Array.from({ length: lineCount }, (_, i) => i + 1);
|
|
8947
|
-
if (singleFileCompoundReadPath(cmd) !== null) return Array.from({ length: lineCount }, () => null);
|
|
8948
|
-
return null;
|
|
8949
|
-
}
|
|
8950
9122
|
function elideServedShellLines(cmd, output, priorIds, unknownLineNumbers = false) {
|
|
8951
9123
|
if (priorIds.length === 0) return null;
|
|
8952
9124
|
const lines = output.split("\n");
|
|
@@ -8999,19 +9171,12 @@ function foldShellReadBodies(cmd, output, fileKey, cwd) {
|
|
|
8999
9171
|
if (folded === null) return null;
|
|
9000
9172
|
return { text: folded.numbered.join("\n"), folds: folded.folds };
|
|
9001
9173
|
}
|
|
9002
|
-
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;
|
|
9003
|
-
function isWholeFileDump(cmd, extractedPath) {
|
|
9004
|
-
const m = WHOLE_FILE_DUMP_RE.exec(cmd);
|
|
9005
|
-
if (m === null) return false;
|
|
9006
|
-
const shaped = m[1] ?? m[2] ?? m[3];
|
|
9007
|
-
return shaped !== void 0 && shaped === extractedPath;
|
|
9008
|
-
}
|
|
9009
9174
|
function foldShellReadStructure(cmd, filePath, output, fileKey, cwd) {
|
|
9010
9175
|
if (!isWholeFileDump(cmd, filePath)) return null;
|
|
9011
9176
|
const originalBytes = Buffer.byteLength(output, "utf-8");
|
|
9012
9177
|
let onDisk;
|
|
9013
9178
|
try {
|
|
9014
|
-
onDisk =
|
|
9179
|
+
onDisk = statSync6(fileKey).size;
|
|
9015
9180
|
} catch {
|
|
9016
9181
|
return null;
|
|
9017
9182
|
}
|
|
@@ -9209,168 +9374,6 @@ function maybeFoldCurlHtml(cmd, output, id) {
|
|
|
9209
9374
|
}
|
|
9210
9375
|
return emitRewrite(notice + fenced, "curl HTML body cleaned", { kind: "bash_compress:curl-html", originalBytes: deliveredOutputBytes(originalBytes) });
|
|
9211
9376
|
}
|
|
9212
|
-
function unwrapCompressCommand(executed) {
|
|
9213
|
-
const t = executed.trim();
|
|
9214
|
-
if (!/^(?:token-goat|tg)\s+compress\b/.test(t)) return null;
|
|
9215
|
-
let argv;
|
|
9216
|
-
try {
|
|
9217
|
-
argv = shlexSplit(t);
|
|
9218
|
-
} catch {
|
|
9219
|
-
return null;
|
|
9220
|
-
}
|
|
9221
|
-
for (let i = 0; i + 1 < argv.length; i++) {
|
|
9222
|
-
const tok = argv[i];
|
|
9223
|
-
if (tok === "-c" || tok === "--cmd") return argv[i + 1] ?? null;
|
|
9224
|
-
}
|
|
9225
|
-
return null;
|
|
9226
|
-
}
|
|
9227
|
-
function detectUnbalancedShellSyntax(cmd) {
|
|
9228
|
-
let inSingle = false;
|
|
9229
|
-
let inDouble = false;
|
|
9230
|
-
let i = 0;
|
|
9231
|
-
while (i < cmd.length) {
|
|
9232
|
-
const ch = cmd[i];
|
|
9233
|
-
if (inSingle) {
|
|
9234
|
-
if (ch === "'") {
|
|
9235
|
-
inSingle = false;
|
|
9236
|
-
}
|
|
9237
|
-
i++;
|
|
9238
|
-
continue;
|
|
9239
|
-
}
|
|
9240
|
-
if (inDouble) {
|
|
9241
|
-
if (ch === "\\" && i + 1 < cmd.length) {
|
|
9242
|
-
i += 2;
|
|
9243
|
-
continue;
|
|
9244
|
-
}
|
|
9245
|
-
if (ch === '"') {
|
|
9246
|
-
inDouble = false;
|
|
9247
|
-
}
|
|
9248
|
-
i++;
|
|
9249
|
-
continue;
|
|
9250
|
-
}
|
|
9251
|
-
if (ch === "\\" && i + 1 < cmd.length) {
|
|
9252
|
-
i += 2;
|
|
9253
|
-
continue;
|
|
9254
|
-
}
|
|
9255
|
-
if (ch === "$" && cmd[i + 1] === "(" && cmd[i + 2] === "(") {
|
|
9256
|
-
let depth = 2;
|
|
9257
|
-
let j = i + 3;
|
|
9258
|
-
while (j < cmd.length && depth > 0) {
|
|
9259
|
-
if (cmd[j] === "(") depth++;
|
|
9260
|
-
else if (cmd[j] === ")") depth--;
|
|
9261
|
-
j++;
|
|
9262
|
-
}
|
|
9263
|
-
i = j;
|
|
9264
|
-
continue;
|
|
9265
|
-
}
|
|
9266
|
-
if (ch === "$" && cmd[i + 1] === "'") {
|
|
9267
|
-
let j = i + 2;
|
|
9268
|
-
let closed = false;
|
|
9269
|
-
while (j < cmd.length) {
|
|
9270
|
-
if (cmd[j] === "\\") {
|
|
9271
|
-
j += 2;
|
|
9272
|
-
continue;
|
|
9273
|
-
}
|
|
9274
|
-
if (cmd[j] === "'") {
|
|
9275
|
-
j++;
|
|
9276
|
-
closed = true;
|
|
9277
|
-
break;
|
|
9278
|
-
}
|
|
9279
|
-
j++;
|
|
9280
|
-
}
|
|
9281
|
-
if (!closed) {
|
|
9282
|
-
inSingle = true;
|
|
9283
|
-
break;
|
|
9284
|
-
}
|
|
9285
|
-
i = j;
|
|
9286
|
-
continue;
|
|
9287
|
-
}
|
|
9288
|
-
if (ch === "#" && (i === 0 || /\s/.test(cmd[i - 1] ?? ""))) {
|
|
9289
|
-
const nl2 = cmd.indexOf("\n", i);
|
|
9290
|
-
i = nl2 === -1 ? cmd.length : nl2 + 1;
|
|
9291
|
-
continue;
|
|
9292
|
-
}
|
|
9293
|
-
if (ch === "'") {
|
|
9294
|
-
inSingle = true;
|
|
9295
|
-
i++;
|
|
9296
|
-
continue;
|
|
9297
|
-
}
|
|
9298
|
-
if (ch === '"') {
|
|
9299
|
-
inDouble = true;
|
|
9300
|
-
i++;
|
|
9301
|
-
continue;
|
|
9302
|
-
}
|
|
9303
|
-
if (ch === "<" && i + 1 < cmd.length && cmd[i + 1] === "<") {
|
|
9304
|
-
if (i + 2 < cmd.length && cmd[i + 2] === "<") {
|
|
9305
|
-
i += 3;
|
|
9306
|
-
continue;
|
|
9307
|
-
}
|
|
9308
|
-
let j = i + 2;
|
|
9309
|
-
let hasIndentModifier = false;
|
|
9310
|
-
if (j < cmd.length && (cmd[j] === "-" || cmd[j] === "~")) {
|
|
9311
|
-
hasIndentModifier = true;
|
|
9312
|
-
j++;
|
|
9313
|
-
}
|
|
9314
|
-
while (j < cmd.length && cmd[j] === " ") {
|
|
9315
|
-
j++;
|
|
9316
|
-
}
|
|
9317
|
-
if (j < cmd.length) {
|
|
9318
|
-
let delimStart = j;
|
|
9319
|
-
if (cmd[j] === '"' || cmd[j] === "'") {
|
|
9320
|
-
delimStart = j + 1;
|
|
9321
|
-
const quoteChar = cmd[j];
|
|
9322
|
-
let delimEnd = delimStart;
|
|
9323
|
-
while (delimEnd < cmd.length && cmd[delimEnd] !== quoteChar) {
|
|
9324
|
-
delimEnd++;
|
|
9325
|
-
}
|
|
9326
|
-
if (delimEnd >= cmd.length) {
|
|
9327
|
-
return "an unclosed quote in a heredoc opener";
|
|
9328
|
-
}
|
|
9329
|
-
j = delimEnd + 1;
|
|
9330
|
-
} else {
|
|
9331
|
-
let delimEnd = delimStart;
|
|
9332
|
-
while (delimEnd < cmd.length && /\w/.test(cmd.charAt(delimEnd))) {
|
|
9333
|
-
delimEnd++;
|
|
9334
|
-
}
|
|
9335
|
-
j = delimEnd;
|
|
9336
|
-
}
|
|
9337
|
-
const delimiter = cmd.slice(delimStart, j).replace(/["']/g, "");
|
|
9338
|
-
if (delimiter) {
|
|
9339
|
-
let scanPos = j;
|
|
9340
|
-
let terminatorEnd = -1;
|
|
9341
|
-
while (scanPos <= cmd.length) {
|
|
9342
|
-
const nl2 = cmd.indexOf("\n", scanPos);
|
|
9343
|
-
const lineEnd = nl2 === -1 ? cmd.length : nl2;
|
|
9344
|
-
let line = cmd.slice(scanPos, lineEnd);
|
|
9345
|
-
if (line.endsWith("\r")) {
|
|
9346
|
-
line = line.slice(0, -1);
|
|
9347
|
-
}
|
|
9348
|
-
const isMatch = hasIndentModifier ? line.trim() === delimiter : line === delimiter;
|
|
9349
|
-
if (isMatch) {
|
|
9350
|
-
terminatorEnd = nl2 === -1 ? cmd.length : nl2 + 1;
|
|
9351
|
-
break;
|
|
9352
|
-
}
|
|
9353
|
-
if (nl2 === -1) break;
|
|
9354
|
-
scanPos = nl2 + 1;
|
|
9355
|
-
}
|
|
9356
|
-
if (terminatorEnd === -1) {
|
|
9357
|
-
return `an unterminated heredoc (${delimiter} never appears on its own line)`;
|
|
9358
|
-
}
|
|
9359
|
-
i = terminatorEnd;
|
|
9360
|
-
continue;
|
|
9361
|
-
}
|
|
9362
|
-
}
|
|
9363
|
-
}
|
|
9364
|
-
i++;
|
|
9365
|
-
}
|
|
9366
|
-
if (inSingle) {
|
|
9367
|
-
return "an unclosed single quote";
|
|
9368
|
-
}
|
|
9369
|
-
if (inDouble) {
|
|
9370
|
-
return "an unclosed double quote";
|
|
9371
|
-
}
|
|
9372
|
-
return null;
|
|
9373
|
-
}
|
|
9374
9377
|
function preBashHandlerInner(event) {
|
|
9375
9378
|
const rawCmd = extractCommand(event);
|
|
9376
9379
|
if (rawCmd === void 0) return passOutput();
|
|
@@ -9401,7 +9404,7 @@ function preBashHandlerInner(event) {
|
|
|
9401
9404
|
}
|
|
9402
9405
|
const toolResults = extractToolResultsFile(cmd);
|
|
9403
9406
|
if (toolResults !== null) {
|
|
9404
|
-
const
|
|
9407
|
+
const outPath = displaySafePath(toolResults.path);
|
|
9405
9408
|
recordStat("session_hint", 0, 0);
|
|
9406
9409
|
return contextOutput(
|
|
9407
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.'
|
|
@@ -9652,7 +9655,7 @@ function preBashHandlerInner(event) {
|
|
|
9652
9655
|
const prevResolvedPath = prevPath !== null && commandPathIsTouchable(prevPath, event) ? resolveIndexPath(prevPath, preHookCwd ?? process.cwd()) : null;
|
|
9653
9656
|
if (prevPath !== null && prevResolvedPath !== null && !existsSync(prevResolvedPath)) {
|
|
9654
9657
|
clearCurlDownload(curlDl.url);
|
|
9655
|
-
} else if (prevPath !== null && prevResolvedPath !== null &&
|
|
9658
|
+
} else if (prevPath !== null && prevResolvedPath !== null && statSync6(prevResolvedPath).size >= loadConfig().hints.bash_dedup_min_bytes) {
|
|
9656
9659
|
recordStat("session_hint", 0, 0);
|
|
9657
9660
|
return denyOutput(
|
|
9658
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.'
|
|
@@ -10144,7 +10147,7 @@ registerHook("post_tool_use", postTaskOutputHandler, { toolName: "TaskOutput" })
|
|
|
10144
10147
|
|
|
10145
10148
|
// src/hooks_tool_failure.ts
|
|
10146
10149
|
init_define_import_meta_env();
|
|
10147
|
-
import { existsSync as existsSync2, readFileSync as readFileSync3, statSync as
|
|
10150
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3, statSync as statSync7, writeFileSync as writeFileSync2 } from "node:fs";
|
|
10148
10151
|
import { dirname as dirname2, relative } from "node:path";
|
|
10149
10152
|
var FAILURE_SUFFIX = ".tool-failures.json";
|
|
10150
10153
|
var MAX_TRACKED_FAILURES = 64;
|
|
@@ -10213,7 +10216,7 @@ function diagnoseEditFailure(event, errorText) {
|
|
|
10213
10216
|
const absPath = normalizePath(filePath);
|
|
10214
10217
|
if (!existsSync2(absPath)) return null;
|
|
10215
10218
|
try {
|
|
10216
|
-
const stat =
|
|
10219
|
+
const stat = statSync7(absPath);
|
|
10217
10220
|
if (stat.size > MAX_EDIT_DIAGNOSE_BYTES) return null;
|
|
10218
10221
|
const fileContent = readFileSync3(absPath, "utf8");
|
|
10219
10222
|
const relDisplay = displaySafeText(relative(process.cwd(), absPath).replace(/\\/g, "/") || absPath);
|
|
@@ -10745,10 +10748,14 @@ function postMcpHandler(event) {
|
|
|
10745
10748
|
if (!resultText) return passOutput();
|
|
10746
10749
|
const injectionMatches = scanAndRecord(resultText);
|
|
10747
10750
|
const redactedResult = redactSecrets(resultText);
|
|
10748
|
-
const
|
|
10749
|
-
|
|
10750
|
-
|
|
10751
|
-
|
|
10751
|
+
const preserved = (mcpContentBlocks(event.raw) ?? []).filter((b) => !isTextualContentBlock(b));
|
|
10752
|
+
const preserve = (output) => withPreservedBlocks(output, preserved);
|
|
10753
|
+
const fenced = () => preserve(
|
|
10754
|
+
emitRewriteIfChanged(
|
|
10755
|
+
resultText,
|
|
10756
|
+
fenceWithMatches(redactedResult.text, injectionMatches, UNTRUSTED_TOOL_TAG),
|
|
10757
|
+
"mcp"
|
|
10758
|
+
)
|
|
10752
10759
|
);
|
|
10753
10760
|
const passOrFence = () => fenced();
|
|
10754
10761
|
if (!event.sessionId) return passOrFence();
|
|
@@ -10776,10 +10783,12 @@ function postMcpHandler(event) {
|
|
|
10776
10783
|
minNetSavingsBytes: resolveMinNetSavingsBytes()
|
|
10777
10784
|
});
|
|
10778
10785
|
if (worthwhile) {
|
|
10779
|
-
return
|
|
10780
|
-
|
|
10781
|
-
|
|
10782
|
-
|
|
10786
|
+
return preserve(
|
|
10787
|
+
emitRewrite(
|
|
10788
|
+
`${notice}${fenceWithMatches(redactedBody, injectionMatches, UNTRUSTED_TOOL_TAG)}`,
|
|
10789
|
+
"mcp",
|
|
10790
|
+
{ kind: "mcp:compress", originalBytes }
|
|
10791
|
+
)
|
|
10783
10792
|
);
|
|
10784
10793
|
}
|
|
10785
10794
|
}
|
|
@@ -10877,6 +10886,7 @@ import { createHash } from "node:crypto";
|
|
|
10877
10886
|
function getResponseContentBlocks(raw) {
|
|
10878
10887
|
const tr = raw["tool_response"];
|
|
10879
10888
|
if (!tr || typeof tr !== "object") return null;
|
|
10889
|
+
if (Array.isArray(tr)) return tr;
|
|
10880
10890
|
const content = tr["content"];
|
|
10881
10891
|
return Array.isArray(content) ? content : null;
|
|
10882
10892
|
}
|
|
@@ -10915,10 +10925,7 @@ async function shrinkImageBlock(block) {
|
|
|
10915
10925
|
changed: true,
|
|
10916
10926
|
// Decoded image bytes, not base64 characters. Both branches of this function file under the same `image_shrink` kind, and the shrink branch below credits `result.originalBytes - result.shrunkBytes` -- decoded bytes, the convention documented at image_shrink.ts's own recordStat call. Base64 inflates by 4/3, so crediting `originalDataUrl.length` here booked roughly 33% more for a repeat screenshot than an identical shrink of the same image, and the ledger summed the two units into one row. The gate above deliberately still measures the data URL: it decides whether the rewrite pays off on the wire, where base64 characters are what is actually sent.
|
|
10917
10927
|
savedBytes: buffer.length - Buffer.byteLength(SCREENSHOT_REPEAT_NOTICE, "utf-8"),
|
|
10918
|
-
// The whole image is withheld here rather than resized, so the visual tokens saved are its
|
|
10919
|
-
// entire billed cost less the notice standing in for it. Billed cost, not raw patch count:
|
|
10920
|
-
// an oversized screenshot would have been capped by the API's own downscale, so pricing the
|
|
10921
|
-
// untouched dimensions would credit a bill that was never going to be sent.
|
|
10928
|
+
// The whole image is withheld here rather than resized, so the visual tokens saved are its entire billed cost less the notice standing in for it. Billed cost, not raw patch count: an oversized screenshot would have been capped by the API's own downscale, so pricing the untouched dimensions would credit a bill that was never going to be sent.
|
|
10922
10929
|
savedTokens: Math.max(0, visionTokens(meta?.width ?? 0, meta?.height ?? 0, tier) - savedTokensFromBytes(Buffer.byteLength(SCREENSHOT_REPEAT_NOTICE, "utf-8")))
|
|
10923
10930
|
};
|
|
10924
10931
|
}
|
|
@@ -11408,6 +11415,10 @@ async function relayInProcess(eventName, rawPayload) {
|
|
|
11408
11415
|
if (!process.env["CLAUDE_CODE_SESSION_ID"] && event.sessionId) {
|
|
11409
11416
|
process.env["CLAUDE_CODE_SESSION_ID"] = event.sessionId;
|
|
11410
11417
|
}
|
|
11418
|
+
const rawTranscriptPath = event.raw["transcript_path"];
|
|
11419
|
+
if (typeof rawTranscriptPath === "string" && rawTranscriptPath !== "") {
|
|
11420
|
+
setTranscriptPath(rawTranscriptPath, event.sessionId ?? "");
|
|
11421
|
+
}
|
|
11411
11422
|
if (!process.env["TRACEPARENT"] && event.traceparent) {
|
|
11412
11423
|
process.env["TRACEPARENT"] = event.traceparent;
|
|
11413
11424
|
}
|