token-goat 2.9.18 → 2.9.20
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 +5 -1
- package/dist/token-goat-chunk-2CLWWRFR.mjs +26 -0
- package/dist/{token-goat-chunk-LTYWLKAG.mjs → token-goat-chunk-2Q3N5KUQ.mjs} +1283 -1012
- package/dist/{token-goat-chunk-6LGVCCMY.mjs → token-goat-chunk-3OGZOFLS.mjs} +26 -16
- package/dist/{token-goat-chunk-QML7ITHJ.mjs → token-goat-chunk-3VTJC7KU.mjs} +693 -438
- package/dist/{token-goat-chunk-QIDLJGXZ.mjs → token-goat-chunk-6LJNM4ZZ.mjs} +29 -5
- package/dist/{token-goat-chunk-CA6V36KW.mjs → token-goat-chunk-74ADIZZX.mjs} +68 -50
- package/dist/{token-goat-chunk-52IMXJ2L.mjs → token-goat-chunk-ASTBLE2R.mjs} +4 -4
- package/dist/token-goat-chunk-AU2K2QHR.mjs +20 -0
- package/dist/token-goat-chunk-BQIC3AVL.mjs +292 -0
- package/dist/token-goat-chunk-CBXLE54Q.mjs +30 -0
- package/dist/{token-goat-chunk-LMFO66YD.mjs → token-goat-chunk-DA43OE7L.mjs} +1 -1
- package/dist/{token-goat-chunk-ARNRFR6M.mjs → token-goat-chunk-DNAF26L4.mjs} +1 -1
- package/dist/{token-goat-chunk-BFEWOMA3.mjs → token-goat-chunk-DPHJU3P7.mjs} +1186 -4573
- package/dist/{token-goat-chunk-OWSJJY5R.mjs → token-goat-chunk-DW4VKU7D.mjs} +5 -5
- package/dist/token-goat-chunk-DYUBZZNP.mjs +198 -0
- package/dist/{token-goat-chunk-W3E65QOW.mjs → token-goat-chunk-ERGHDBC6.mjs} +310 -47
- package/dist/token-goat-chunk-EUZKG7ZO.mjs +304 -0
- package/dist/{token-goat-chunk-5MMJAJF3.mjs → token-goat-chunk-FWYZVS3E.mjs} +324 -31
- package/dist/token-goat-chunk-G6SYQQO7.mjs +41 -0
- package/dist/{token-goat-chunk-YQZE5NS6.mjs → token-goat-chunk-GIPMOGOI.mjs} +6 -6
- package/dist/{token-goat-chunk-BWCRC23L.mjs → token-goat-chunk-H4VC5TR6.mjs} +29 -20
- package/dist/{token-goat-chunk-V3ZWBEHM.mjs → token-goat-chunk-I5PQRHFE.mjs} +103 -24
- package/dist/token-goat-chunk-ITWYDOKP.mjs +341 -0
- package/dist/{token-goat-chunk-I67CPTDD.mjs → token-goat-chunk-K3ZNBN4U.mjs} +19 -14
- package/dist/{token-goat-chunk-HPOFR6QX.mjs → token-goat-chunk-K5XPIUVB.mjs} +222 -61
- package/dist/token-goat-chunk-KT75DZK4.mjs +1989 -0
- package/dist/{token-goat-chunk-H3FONFBO.mjs → token-goat-chunk-MNJWG2ZO.mjs} +3 -3
- package/dist/{token-goat-chunk-GRI6HWX3.mjs → token-goat-chunk-MUJV7BR3.mjs} +1 -1
- package/dist/{token-goat-chunk-ERTXEKB6.mjs → token-goat-chunk-NMTKNYGF.mjs} +2 -0
- package/dist/{token-goat-chunk-LA3O4JGD.mjs → token-goat-chunk-ODF7H7WS.mjs} +3 -3
- package/dist/{token-goat-chunk-K4DG6EEF.mjs → token-goat-chunk-ONMU7BON.mjs} +7 -8
- package/dist/{token-goat-chunk-4EXFN2AW.mjs → token-goat-chunk-OZRSREKR.mjs} +2 -2
- package/dist/{token-goat-chunk-EPXNOKIV.mjs → token-goat-chunk-P4XBHZXU.mjs} +2 -2
- package/dist/{token-goat-chunk-CTOYJ232.mjs → token-goat-chunk-PAVWB4L4.mjs} +173 -1840
- package/dist/{token-goat-chunk-UENEL6KX.mjs → token-goat-chunk-PTW22MRC.mjs} +2 -2
- package/dist/token-goat-chunk-SL454EN5.mjs +1816 -0
- package/dist/token-goat-chunk-SP5QR2HN.mjs +1379 -0
- package/dist/{token-goat-chunk-7VLWNQDQ.mjs → token-goat-chunk-T46UKRFM.mjs} +1 -1
- package/dist/{token-goat-chunk-L7VCX33I.mjs → token-goat-chunk-T63VHOL3.mjs} +40 -10
- package/dist/token-goat-chunk-VSJGPEBB.mjs +248 -0
- package/dist/{token-goat-chunk-HIZXQVNU.mjs → token-goat-chunk-VYXGHGUY.mjs} +4 -4
- package/dist/token-goat-chunk-W6QYCWAT.mjs +128 -0
- package/dist/{token-goat-chunk-WFX2V3OF.mjs → token-goat-chunk-Z4ALQADF.mjs} +579 -422
- package/dist/token-goat-chunk-ZHFM4DHJ.mjs +324 -0
- package/dist/token-goat-hook.mjs +18 -12
- package/dist/token-goat.core.mjs +29 -20
- package/docs/C4_RUNTIME_ARCHITECTURE.md +1 -1
- package/docs/cli.md +15 -13
- package/docs/install.md +1 -1
- package/docs/security.md +2 -2
- package/package.json +3 -2
- package/dist/token-goat-chunk-2KTZ6J7T.mjs +0 -173
- package/dist/token-goat-chunk-34OUJ2IE.mjs +0 -35
- package/dist/token-goat-chunk-RHHFEXKF.mjs +0 -93
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { createRequire as __cjsRequire } from 'node:module';
|
|
2
2
|
const require = __cjsRequire(import.meta.url);
|
|
3
3
|
import {
|
|
4
|
+
MAX_CAPTURE_BYTES,
|
|
4
5
|
canRunWrappedShell,
|
|
5
6
|
clipToDeliveryCap,
|
|
6
7
|
deliveredOutputBytes
|
|
7
|
-
} from "./token-goat-chunk-
|
|
8
|
+
} from "./token-goat-chunk-ITWYDOKP.mjs";
|
|
8
9
|
import {
|
|
9
10
|
DEFAULT_RECONCILE_BUDGET_MS,
|
|
10
11
|
HOOK_EVENTS,
|
|
12
|
+
MCP_TOOL_PATTERN,
|
|
13
|
+
RECALL_COMMAND,
|
|
11
14
|
buildCommandManifest,
|
|
12
15
|
checkSymbolBodySize,
|
|
13
16
|
diagnoseSqlFailure,
|
|
@@ -17,32 +20,37 @@ import {
|
|
|
17
20
|
readStdinJson,
|
|
18
21
|
reconcileProject,
|
|
19
22
|
storeWebOutput
|
|
20
|
-
} from "./token-goat-chunk-
|
|
23
|
+
} from "./token-goat-chunk-I5PQRHFE.mjs";
|
|
24
|
+
import {
|
|
25
|
+
projectTranscriptsDir
|
|
26
|
+
} from "./token-goat-chunk-BQIC3AVL.mjs";
|
|
27
|
+
import {
|
|
28
|
+
BUDGET_ESCALATION_MARKER,
|
|
29
|
+
MANIFEST_PREAMBLE,
|
|
30
|
+
MANIFEST_RECOVERY_PREAMBLE,
|
|
31
|
+
buildManifest,
|
|
32
|
+
manifestPrintedPaths,
|
|
33
|
+
summaryBudgetDirective
|
|
34
|
+
} from "./token-goat-chunk-VSJGPEBB.mjs";
|
|
21
35
|
import {
|
|
22
36
|
BODY_FIRST_TOOL_RESPONSE_KEYS,
|
|
23
|
-
GENERIC_SERVED_OUTPUT_KEY,
|
|
24
37
|
OUTLINE_MAX_REPLACEMENT_RATIO,
|
|
25
38
|
OUTLINE_MIN_HEADINGS,
|
|
26
39
|
OUTPUT_FIRST_TOOL_RESPONSE_KEYS,
|
|
27
|
-
WEB_FETCH_KEY_SEP,
|
|
28
|
-
accumulateResidentLines,
|
|
29
40
|
appendDirtyPath,
|
|
30
41
|
applyHintTracking,
|
|
31
42
|
buildDeltaCapsule,
|
|
32
43
|
buildProjectMap,
|
|
33
44
|
classifyBashHint,
|
|
34
45
|
classifyEditHint,
|
|
35
|
-
clearCurlDownload,
|
|
36
46
|
compactPathFor,
|
|
37
|
-
computeAdaptiveBudget,
|
|
38
47
|
contextOutput,
|
|
39
48
|
denyOutput,
|
|
40
49
|
emitRewrite,
|
|
41
50
|
emitRewriteIfChanged,
|
|
42
51
|
enqueueDirtyPathSafe,
|
|
43
52
|
ensureWorkerAlive,
|
|
44
|
-
|
|
45
|
-
extractCompactFromMarker,
|
|
53
|
+
escapeHintName,
|
|
46
54
|
extractToolResponseField,
|
|
47
55
|
extractToolResultText,
|
|
48
56
|
fenceUntrusted,
|
|
@@ -53,42 +61,19 @@ import {
|
|
|
53
61
|
foldingEnabled,
|
|
54
62
|
formatProjectMap,
|
|
55
63
|
formatShrinkSummary,
|
|
56
|
-
getBashOutputId,
|
|
57
|
-
getContextPressure,
|
|
58
|
-
getCurlDownloadPath,
|
|
59
64
|
getCwd,
|
|
60
|
-
getFileLineRanges,
|
|
61
65
|
getFilePath,
|
|
62
|
-
getFileServedOutputs,
|
|
63
|
-
getGlobMatchCount,
|
|
64
|
-
getGrepMatchCount,
|
|
65
|
-
getOutstandingAgentSpawns,
|
|
66
|
-
getSessionBashOutputs,
|
|
67
|
-
getSessionBashReruns,
|
|
68
|
-
getSessionFiles,
|
|
69
|
-
getSessionWebFetches,
|
|
70
66
|
getToolInput,
|
|
71
67
|
getToolName,
|
|
72
68
|
getTranscriptPath,
|
|
73
|
-
hasSeenImage,
|
|
74
|
-
hasSessionOutput,
|
|
75
69
|
imageQualifiesForShrink,
|
|
76
|
-
incrementSkillHit,
|
|
77
70
|
indexServedBody,
|
|
78
|
-
installedSkillPath,
|
|
79
71
|
isMcpErrorResponse,
|
|
80
72
|
isStructuralRewriteAccepted,
|
|
81
73
|
isTextualContentBlock,
|
|
82
|
-
|
|
83
|
-
lineMayCarryResidentSignal,
|
|
84
|
-
listSiblingSessionStates,
|
|
85
|
-
loadSessionCache,
|
|
86
|
-
loadSessionState,
|
|
74
|
+
logSuppressedDetection,
|
|
87
75
|
makeDedupHintHandlers,
|
|
88
76
|
markCompactStale,
|
|
89
|
-
markCompacted,
|
|
90
|
-
markFileTruncated,
|
|
91
|
-
markHintShown,
|
|
92
77
|
matchesAllowPattern,
|
|
93
78
|
matchesDenyPattern,
|
|
94
79
|
mcpContentBlocks,
|
|
@@ -101,8 +86,53 @@ import {
|
|
|
101
86
|
planSourceSkeleton,
|
|
102
87
|
preToolPathDeclined,
|
|
103
88
|
probeImageMeta,
|
|
104
|
-
|
|
105
|
-
|
|
89
|
+
recordKnownRootThrottled,
|
|
90
|
+
registerHook,
|
|
91
|
+
resolveLineRegions,
|
|
92
|
+
runHook,
|
|
93
|
+
scanAndRecord,
|
|
94
|
+
serializeOutput,
|
|
95
|
+
servedRunNotice,
|
|
96
|
+
sessionStateKey,
|
|
97
|
+
shrinkImage,
|
|
98
|
+
visionTokens,
|
|
99
|
+
visionTokensSaved,
|
|
100
|
+
vscodePathAllowed,
|
|
101
|
+
withPreservedBlocks
|
|
102
|
+
} from "./token-goat-chunk-DPHJU3P7.mjs";
|
|
103
|
+
import {
|
|
104
|
+
compressOutput,
|
|
105
|
+
detectFromCommand,
|
|
106
|
+
filterByName,
|
|
107
|
+
isRewriteWorthwhile,
|
|
108
|
+
looksLikePytestOutput,
|
|
109
|
+
pytestFilter,
|
|
110
|
+
resolveMinNetSavingsBytes
|
|
111
|
+
} from "./token-goat-chunk-PAVWB4L4.mjs";
|
|
112
|
+
import {
|
|
113
|
+
readCopilotHooksOwners
|
|
114
|
+
} from "./token-goat-chunk-SP5QR2HN.mjs";
|
|
115
|
+
import {
|
|
116
|
+
GENERIC_SERVED_OUTPUT_KEY,
|
|
117
|
+
clearCurlDownload,
|
|
118
|
+
extractCompactFromMarker,
|
|
119
|
+
getBashOutputId,
|
|
120
|
+
getCurlDownloadPath,
|
|
121
|
+
getFileLineRanges,
|
|
122
|
+
getFileServedOutputs,
|
|
123
|
+
getGlobMatchCount,
|
|
124
|
+
getGrepMatchCount,
|
|
125
|
+
getOutstandingAgentSpawns,
|
|
126
|
+
getSessionBashOutputs,
|
|
127
|
+
hasSeenImage,
|
|
128
|
+
hasSessionOutput,
|
|
129
|
+
incrementSkillHit,
|
|
130
|
+
installedSkillPath,
|
|
131
|
+
lastTabContextMatches,
|
|
132
|
+
loadSessionState,
|
|
133
|
+
markCompacted,
|
|
134
|
+
markFileTruncated,
|
|
135
|
+
markHintShown,
|
|
106
136
|
recordBashOutput,
|
|
107
137
|
recordBashRerun,
|
|
108
138
|
recordCliRead,
|
|
@@ -113,45 +143,38 @@ import {
|
|
|
113
143
|
recordFileServedOutput,
|
|
114
144
|
recordGlobQuery,
|
|
115
145
|
recordGrepQuery,
|
|
116
|
-
recordKnownRootThrottled,
|
|
117
146
|
recordOutstandingAgentSpawn,
|
|
118
147
|
recordScheduledPrompt,
|
|
119
148
|
recordSeenImage,
|
|
120
149
|
recordSymbolRead,
|
|
121
150
|
recordWebFetch,
|
|
122
|
-
registerHook,
|
|
123
151
|
removeOutstandingAgentSpawn,
|
|
124
|
-
repeatedSkillBodyHint,
|
|
125
|
-
runHook,
|
|
126
152
|
saveSessionState,
|
|
127
|
-
scanAndRecord,
|
|
128
|
-
serializeOutput,
|
|
129
|
-
servedRunNotice,
|
|
130
153
|
sessionOutputBodyBytes,
|
|
131
154
|
sessionSidecarPath,
|
|
132
|
-
sessionStateKey,
|
|
133
155
|
setLastTabContext,
|
|
134
156
|
setTranscriptPath,
|
|
135
|
-
shrinkImage,
|
|
136
157
|
storeOutput,
|
|
137
|
-
summarizeResidentContext,
|
|
138
158
|
takePendingLargeFileHint,
|
|
139
|
-
taskListPruneHint,
|
|
140
|
-
visionTokens,
|
|
141
|
-
visionTokensSaved,
|
|
142
|
-
vscodePathAllowed,
|
|
143
159
|
wasCliReadThisSession,
|
|
144
160
|
wasFileReadThisSession,
|
|
145
|
-
wasHintShown
|
|
146
|
-
|
|
147
|
-
|
|
161
|
+
wasHintShown
|
|
162
|
+
} from "./token-goat-chunk-KT75DZK4.mjs";
|
|
163
|
+
import {
|
|
164
|
+
accumulateResidentLines,
|
|
165
|
+
claudeConfigDir,
|
|
166
|
+
estimateTokens,
|
|
167
|
+
lineMayCarryResidentSignal,
|
|
168
|
+
readTranscriptTail,
|
|
169
|
+
repeatedSkillBodyHint,
|
|
170
|
+
summarizeResidentContext,
|
|
171
|
+
taskListPruneHint
|
|
172
|
+
} from "./token-goat-chunk-ZHFM4DHJ.mjs";
|
|
148
173
|
import {
|
|
149
174
|
BASH_OUTPUT_SUBDIR,
|
|
175
|
+
bashOutputIdSync,
|
|
150
176
|
commandHash,
|
|
151
|
-
compressOutput,
|
|
152
177
|
dedupeConsecutive,
|
|
153
|
-
detectFromCommand,
|
|
154
|
-
filterByName,
|
|
155
178
|
getBashOutput,
|
|
156
179
|
getMonitoringRecallHint,
|
|
157
180
|
hasBareBackgroundOrNewline,
|
|
@@ -159,44 +182,47 @@ import {
|
|
|
159
182
|
indexRecallEntry,
|
|
160
183
|
isBashEntryStale,
|
|
161
184
|
isBuildCommand,
|
|
162
|
-
|
|
185
|
+
isRedirectAmpersand,
|
|
163
186
|
isScopedGitStatusOrDiffStatCommand,
|
|
164
187
|
isTestRunnerCommand,
|
|
165
|
-
|
|
188
|
+
pathStem,
|
|
166
189
|
shlexSplit,
|
|
167
190
|
splitOwnTrailingNotices,
|
|
168
191
|
storeBashOutput,
|
|
169
192
|
summarizeOutputDelta
|
|
170
|
-
} from "./token-goat-chunk-
|
|
193
|
+
} from "./token-goat-chunk-SL454EN5.mjs";
|
|
171
194
|
import {
|
|
172
195
|
recordStat,
|
|
173
196
|
redactSecrets,
|
|
174
197
|
savedTokensFromBytes,
|
|
175
198
|
storeBlob,
|
|
176
199
|
stripAnsiEscapes
|
|
177
|
-
} from "./token-goat-chunk-
|
|
200
|
+
} from "./token-goat-chunk-K5XPIUVB.mjs";
|
|
178
201
|
import {
|
|
179
202
|
extractMarkdownHeadings,
|
|
180
203
|
formatHeadingTreeParts
|
|
181
|
-
} from "./token-goat-chunk-
|
|
204
|
+
} from "./token-goat-chunk-ERGHDBC6.mjs";
|
|
182
205
|
import {
|
|
183
|
-
countSymbols
|
|
184
|
-
|
|
206
|
+
countSymbols,
|
|
207
|
+
getFileEntry,
|
|
208
|
+
querySymbols
|
|
209
|
+
} from "./token-goat-chunk-K3ZNBN4U.mjs";
|
|
185
210
|
import {
|
|
186
211
|
detectLanguage,
|
|
187
212
|
getDb,
|
|
188
213
|
languageHasFlag
|
|
189
|
-
} from "./token-goat-chunk-
|
|
214
|
+
} from "./token-goat-chunk-T63VHOL3.mjs";
|
|
190
215
|
import {
|
|
191
216
|
detectHarness,
|
|
192
217
|
envBool,
|
|
193
218
|
envInt,
|
|
194
219
|
findProject,
|
|
220
|
+
fingerprintFile,
|
|
195
221
|
getHarnessName,
|
|
196
222
|
isUnderSystemTemp,
|
|
197
223
|
loadConfig,
|
|
198
224
|
shortFingerprint
|
|
199
|
-
} from "./token-goat-chunk-
|
|
225
|
+
} from "./token-goat-chunk-74ADIZZX.mjs";
|
|
200
226
|
import {
|
|
201
227
|
ENV_KEYS,
|
|
202
228
|
IDENTICAL_READ_MIN_BODY_BYTES,
|
|
@@ -216,8 +242,9 @@ import {
|
|
|
216
242
|
isInsideRoot,
|
|
217
243
|
runGit,
|
|
218
244
|
toKB
|
|
219
|
-
} from "./token-goat-chunk-
|
|
245
|
+
} from "./token-goat-chunk-6LJNM4ZZ.mjs";
|
|
220
246
|
import {
|
|
247
|
+
TOOL_RESULTS_ID_CHARS,
|
|
221
248
|
UNTRUSTED_TOOL_TAG,
|
|
222
249
|
displaySafePath,
|
|
223
250
|
displaySafeText,
|
|
@@ -226,7 +253,7 @@ import {
|
|
|
226
253
|
normalizePath,
|
|
227
254
|
resolveIndexPath,
|
|
228
255
|
toDisplayPath
|
|
229
|
-
} from "./token-goat-chunk-
|
|
256
|
+
} from "./token-goat-chunk-NMTKNYGF.mjs";
|
|
230
257
|
import {
|
|
231
258
|
init_define_import_meta_env
|
|
232
259
|
} from "./token-goat-chunk-A37V4PBF.mjs";
|
|
@@ -276,6 +303,52 @@ function stripUnsafeSuggestions(text) {
|
|
|
276
303
|
}
|
|
277
304
|
}
|
|
278
305
|
|
|
306
|
+
// src/pending_context.ts
|
|
307
|
+
init_define_import_meta_env();
|
|
308
|
+
import { readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
309
|
+
import { dirname } from "node:path";
|
|
310
|
+
var PENDING_SUFFIX = ".pending-context.txt";
|
|
311
|
+
var MAX_PENDING_CONTEXT_BYTES = 4096;
|
|
312
|
+
function queuePendingContext(stateKey, text) {
|
|
313
|
+
const trimmed = text.trim();
|
|
314
|
+
if (trimmed === "") return;
|
|
315
|
+
const target = sessionSidecarPath(stateKey, PENDING_SUFFIX);
|
|
316
|
+
if (target === null) return;
|
|
317
|
+
try {
|
|
318
|
+
const existing = readPending(target);
|
|
319
|
+
const merged = existing === null ? trimmed : `${existing}
|
|
320
|
+
${trimmed}`;
|
|
321
|
+
const capped = merged.length <= MAX_PENDING_CONTEXT_BYTES ? merged : merged.slice(merged.length - MAX_PENDING_CONTEXT_BYTES);
|
|
322
|
+
ensureDirSync(dirname(target));
|
|
323
|
+
writeFileSync(target, capped, "utf8");
|
|
324
|
+
} catch {
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
function peekPendingContext(stateKey) {
|
|
328
|
+
const target = sessionSidecarPath(stateKey, PENDING_SUFFIX);
|
|
329
|
+
return target === null ? null : readPending(target);
|
|
330
|
+
}
|
|
331
|
+
function commitPendingContext(stateKey, delivered) {
|
|
332
|
+
if (delivered === null || delivered === "") return;
|
|
333
|
+
const target = sessionSidecarPath(stateKey, PENDING_SUFFIX);
|
|
334
|
+
if (target === null) return;
|
|
335
|
+
const queued = readPending(target);
|
|
336
|
+
if (queued === null) return;
|
|
337
|
+
if (!delivered.includes(queued) && !delivered.includes(stripUnsafeSuggestions(queued))) return;
|
|
338
|
+
try {
|
|
339
|
+
rmSync(target, { force: true });
|
|
340
|
+
} catch {
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
function readPending(target) {
|
|
344
|
+
try {
|
|
345
|
+
const raw = readFileSync(target, "utf8").trim();
|
|
346
|
+
return raw === "" ? null : raw;
|
|
347
|
+
} catch {
|
|
348
|
+
return null;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
279
352
|
// src/vscode_duplicate.ts
|
|
280
353
|
init_define_import_meta_env();
|
|
281
354
|
import * as fs from "node:fs";
|
|
@@ -404,7 +477,7 @@ function grepIntInput(toolInput, key) {
|
|
|
404
477
|
function grepSignature(toolInput) {
|
|
405
478
|
const pattern = toolInput["pattern"];
|
|
406
479
|
if (typeof pattern !== "string" || pattern === "") return null;
|
|
407
|
-
const
|
|
480
|
+
const path6 = typeof toolInput["path"] === "string" ? toolInput["path"] : "";
|
|
408
481
|
const outputMode = typeof toolInput["output_mode"] === "string" ? toolInput["output_mode"] : "files_with_matches";
|
|
409
482
|
const glob = typeof toolInput["glob"] === "string" ? toolInput["glob"] : "";
|
|
410
483
|
const type = typeof toolInput["type"] === "string" ? toolInput["type"] : "";
|
|
@@ -419,7 +492,7 @@ function grepSignature(toolInput) {
|
|
|
419
492
|
const offset = grepIntInput(toolInput, "offset");
|
|
420
493
|
return JSON.stringify([
|
|
421
494
|
pattern,
|
|
422
|
-
|
|
495
|
+
path6,
|
|
423
496
|
outputMode,
|
|
424
497
|
glob,
|
|
425
498
|
type,
|
|
@@ -563,8 +636,8 @@ function isBroadCatchAllGlob(pattern, pathArg) {
|
|
|
563
636
|
function globSignature(toolInput) {
|
|
564
637
|
const pattern = toolInput["pattern"];
|
|
565
638
|
if (typeof pattern !== "string" || pattern === "") return null;
|
|
566
|
-
const
|
|
567
|
-
return JSON.stringify([pattern,
|
|
639
|
+
const path6 = typeof toolInput["path"] === "string" ? toolInput["path"] : "";
|
|
640
|
+
return JSON.stringify([pattern, path6]);
|
|
568
641
|
}
|
|
569
642
|
var { post: postGlobHandler, pre: preGlobDedupHandler } = makeDedupHintHandlers({
|
|
570
643
|
toolName: "Glob",
|
|
@@ -631,7 +704,9 @@ function postEditHandlerInner(event) {
|
|
|
631
704
|
editedSize = statSync2(normalized).size;
|
|
632
705
|
} catch {
|
|
633
706
|
}
|
|
634
|
-
if (meetsSavingsFloor(editedSize)) {
|
|
707
|
+
if (!meetsSavingsFloor(editedSize)) {
|
|
708
|
+
logSuppressedDetection("edit_reread_suggest", event.sessionId, normalized);
|
|
709
|
+
} else {
|
|
635
710
|
const escapedPath = displaySafePath(normalized).replace(/`/g, "\\`").replace(/"/g, '\\"');
|
|
636
711
|
return contextOutput(
|
|
637
712
|
displaySafePath(editedBasename) + ' was edited. Use `token-goat section "' + escapedPath + '::HeadingName"` to re-read a specific section rather than the full file.'
|
|
@@ -650,7 +725,7 @@ registerHook("post_tool_use", postEditHandler, { toolName: "NotebookEdit" });
|
|
|
650
725
|
|
|
651
726
|
// src/hooks_write.ts
|
|
652
727
|
init_define_import_meta_env();
|
|
653
|
-
import { readFileSync, statSync as statSync3 } from "node:fs";
|
|
728
|
+
import { readFileSync as readFileSync2, statSync as statSync3 } from "node:fs";
|
|
654
729
|
var MAX_LINES_FOR_DIFF = 4e3;
|
|
655
730
|
var MAX_OLD_FILE_BYTES_FOR_DIFF = 4 * 1024 * 1024;
|
|
656
731
|
function splitLines(text) {
|
|
@@ -691,7 +766,7 @@ function preWriteRewriteHandler(event) {
|
|
|
691
766
|
if (stat.size > MAX_OLD_FILE_BYTES_FOR_DIFF) return passOutput();
|
|
692
767
|
let oldContent;
|
|
693
768
|
try {
|
|
694
|
-
oldContent =
|
|
769
|
+
oldContent = readFileSync2(filePath, "utf-8");
|
|
695
770
|
} catch {
|
|
696
771
|
return passOutput();
|
|
697
772
|
}
|
|
@@ -717,210 +792,48 @@ registerHook("pre_tool_use", preWriteRewriteHandler, { toolName: "Write" });
|
|
|
717
792
|
|
|
718
793
|
// src/hooks_compact.ts
|
|
719
794
|
init_define_import_meta_env();
|
|
720
|
-
import { spawnSync } from "node:child_process";
|
|
721
795
|
import * as path3 from "node:path";
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
for (const row of rows.slice(0, cap)) lines.push(row);
|
|
729
|
-
if (rows.length > cap) lines.push(`- ...and ${rows.length - cap} more`);
|
|
730
|
-
}
|
|
731
|
-
function renderReadRow(entry) {
|
|
732
|
-
const kb = Math.max(1, toKB(entry.sizeBytes));
|
|
733
|
-
const plural = entry.readCount === 1 ? "read" : "reads";
|
|
734
|
-
const edited = entry.wasEdited ? ", edited" : "";
|
|
735
|
-
return `- ${displaySafePath(entry.path)} (${kb}kb, ${entry.readCount} ${plural}${edited})`;
|
|
736
|
-
}
|
|
737
|
-
function renderSymbolReadRow(entry) {
|
|
738
|
-
return `- ${displaySafePath(entry.path)} (symbols: ${(entry.symbols_read ?? []).map(displaySafeText).join(", ")})`;
|
|
739
|
-
}
|
|
740
|
-
function mergeManifestFiles(parent, siblingFiles) {
|
|
741
|
-
const byPath = /* @__PURE__ */ new Map();
|
|
742
|
-
for (const f of parent) byPath.set(foldPath(f.path), f);
|
|
743
|
-
for (const f of siblingFiles) {
|
|
744
|
-
const key = foldPath(f.path);
|
|
745
|
-
const prev = byPath.get(key);
|
|
746
|
-
if (prev === void 0) {
|
|
747
|
-
byPath.set(key, f);
|
|
748
|
-
continue;
|
|
749
|
-
}
|
|
750
|
-
const mergedSymbols = [.../* @__PURE__ */ new Set([...prev.symbols_read ?? [], ...f.symbols_read ?? []])];
|
|
751
|
-
byPath.set(key, {
|
|
752
|
-
path: prev.path,
|
|
753
|
-
readCount: Math.max(prev.readCount, f.readCount),
|
|
754
|
-
lastReadAt: Math.max(prev.lastReadAt, f.lastReadAt),
|
|
755
|
-
wasEdited: prev.wasEdited || f.wasEdited,
|
|
756
|
-
sizeBytes: f.lastReadAt >= prev.lastReadAt ? f.sizeBytes : prev.sizeBytes,
|
|
757
|
-
...prev.wasTruncated || f.wasTruncated ? { wasTruncated: true } : {},
|
|
758
|
-
...mergedSymbols.length > 0 ? { symbols_read: mergedSymbols } : {}
|
|
759
|
-
});
|
|
760
|
-
}
|
|
761
|
-
return Array.from(byPath.values());
|
|
762
|
-
}
|
|
763
|
-
function buildManifest(sessionId, cwd, transcriptPath) {
|
|
764
|
-
const ownFiles = [...getSessionFiles().values()];
|
|
765
|
-
const siblingFiles = sessionId !== void 0 ? listSiblingSessionStates(sessionId).flatMap((s) => s.files) : [];
|
|
766
|
-
const files = siblingFiles.length > 0 ? mergeManifestFiles(ownFiles, siblingFiles) : ownFiles;
|
|
767
|
-
const editedFiles = files.filter((f) => f.wasEdited);
|
|
768
|
-
const readFiles = files.filter((f) => f.readCount > 0 && !f.wasEdited);
|
|
769
|
-
const symbolOnlyFiles = files.filter((f) => f.readCount === 0 && !f.wasEdited && (f.symbols_read?.length ?? 0) > 0);
|
|
770
|
-
const webFetches = [...getSessionWebFetches().entries()];
|
|
771
|
-
const lines = [];
|
|
772
|
-
lines.push("## Session context");
|
|
773
|
-
lines.push(`Files read: ${readFiles.length}`);
|
|
774
|
-
lines.push(`Files edited: ${editedFiles.length}`);
|
|
775
|
-
appendCappedSection(lines, "### Read files", readFiles.map(renderReadRow), MAX_ROWS);
|
|
776
|
-
appendCappedSection(
|
|
777
|
-
lines,
|
|
778
|
-
"### Edited files",
|
|
779
|
-
editedFiles.map((entry) => `- ${displaySafePath(entry.path)}`),
|
|
780
|
-
MAX_ROWS
|
|
781
|
-
);
|
|
782
|
-
appendCappedSection(lines, "### Surgically read files (symbol/section reads, never read whole)", symbolOnlyFiles.map(renderSymbolReadRow), MAX_ROWS);
|
|
783
|
-
appendCappedSection(
|
|
784
|
-
lines,
|
|
785
|
-
"### Web URLs fetched",
|
|
786
|
-
webFetches.map(([key, cacheId]) => {
|
|
787
|
-
const [url = key, prompt = ""] = key.split(WEB_FETCH_KEY_SEP);
|
|
788
|
-
const promptSuffix = prompt ? `, prompt: ${neutralizeSpokenMarkers(JSON.stringify(prompt))}` : "";
|
|
789
|
-
return `- ${displaySafeText(url)} (cacheId: ${cacheId}${promptSuffix})`;
|
|
790
|
-
}),
|
|
791
|
-
MAX_ROWS
|
|
792
|
-
);
|
|
793
|
-
lines.push(...buildSafeToDiscardSection(files));
|
|
794
|
-
lines.push(...buildMemEpochSection());
|
|
795
|
-
return capManifestChars(lines.join("\n"), sessionId, cwd, transcriptPath);
|
|
796
|
-
}
|
|
797
|
-
function gitDirtySignals(cwd) {
|
|
798
|
-
const timeoutMs = loadConfig().hints.git_hint_max_ms;
|
|
799
|
-
let hasPendingDiff = false;
|
|
800
|
-
try {
|
|
801
|
-
const diffResult = runGit(["diff", "--no-color", "--stat", "HEAD"], { cwd, timeoutMs });
|
|
802
|
-
hasPendingDiff = diffResult.exitCode === 0 && diffResult.stdout.trim() !== "";
|
|
803
|
-
} catch {
|
|
804
|
-
}
|
|
805
|
-
let hasUncommittedChanges = false;
|
|
806
|
-
try {
|
|
807
|
-
const statusResult = runGit(["status", "--porcelain"], { cwd, timeoutMs });
|
|
808
|
-
hasUncommittedChanges = statusResult.exitCode === 0 && statusResult.stdout.trim() !== "";
|
|
809
|
-
} catch {
|
|
810
|
-
}
|
|
811
|
-
return { hasPendingDiff, hasUncommittedChanges };
|
|
812
|
-
}
|
|
813
|
-
function adaptiveCharBonus(sessionId, cwd, transcriptPath) {
|
|
814
|
-
if (!cwd) return 0;
|
|
815
|
-
const cache = loadSessionCache(sessionId ?? "") ?? {};
|
|
816
|
-
const ageSecs = cache.created_ts !== void 0 ? Math.max(0, Date.now() / 1e3 - cache.created_ts) : 0;
|
|
817
|
-
const contextPressure = getContextPressure(cache, transcriptPath);
|
|
818
|
-
const { hasPendingDiff, hasUncommittedChanges } = gitDirtySignals(cwd);
|
|
819
|
-
if (!hasPendingDiff && !hasUncommittedChanges) return 0;
|
|
820
|
-
const baseline = computeAdaptiveBudget(cache, ageSecs, { contextPressure });
|
|
821
|
-
const withGitSignal = computeAdaptiveBudget(cache, ageSecs, { hasPendingDiff, hasUncommittedChanges, contextPressure });
|
|
822
|
-
const deltaTokens = Math.max(0, withGitSignal - baseline);
|
|
823
|
-
return deltaTokens * 3;
|
|
824
|
-
}
|
|
825
|
-
function capManifestChars(manifest, sessionId, cwd, transcriptPath) {
|
|
826
|
-
const cap = loadConfig().compact_assist.max_manifest_chars;
|
|
827
|
-
if (cap <= 0) return manifest;
|
|
828
|
-
if (manifest.length <= cap) return manifest;
|
|
829
|
-
const effectiveCap = cap + adaptiveCharBonus(sessionId, cwd, transcriptPath);
|
|
830
|
-
if (manifest.length <= effectiveCap) return manifest;
|
|
831
|
-
const omitted = manifest.length - effectiveCap;
|
|
832
|
-
return manifest.slice(0, effectiveCap) + `
|
|
833
|
-
...(manifest truncated at ${effectiveCap} chars; ${omitted} chars omitted)`;
|
|
834
|
-
}
|
|
835
|
-
function buildSafeToDiscardSection(files) {
|
|
836
|
-
const rerunHashes = getSessionBashReruns();
|
|
837
|
-
const bashOutputs = getSessionBashOutputs();
|
|
838
|
-
const rerunHashSet = new Set(rerunHashes);
|
|
839
|
-
const rerunRows = [];
|
|
840
|
-
for (const hash of rerunHashes) {
|
|
841
|
-
const id = bashOutputs.find(([h]) => h === hash)?.[1];
|
|
842
|
-
if (id === void 0) continue;
|
|
843
|
-
const entry = getBashOutput(id);
|
|
844
|
-
if (entry === null) continue;
|
|
845
|
-
const flatCommand = neutralizeSpokenMarkers(entry.command.replace(/[\t\r\n]+/g, " "));
|
|
846
|
-
rerunRows.push("- `" + flatCommand + "` \u2014 an older run of this exact command was superseded; recall the surviving copy with `bash-output " + id + " --full`");
|
|
847
|
-
}
|
|
848
|
-
const supersededReadRows = [];
|
|
849
|
-
for (const f of files) {
|
|
850
|
-
if (f.readCount > 1 || f.wasEdited) {
|
|
851
|
-
const reason = f.wasEdited ? "edited after being read" : "re-read " + f.readCount + "x";
|
|
852
|
-
supersededReadRows.push("- " + displaySafePath(f.path) + " (" + reason + " \u2014 only the latest content already in context is current)");
|
|
853
|
-
}
|
|
854
|
-
}
|
|
855
|
-
const cachedOutputRows = [];
|
|
856
|
-
for (const [hash, id] of bashOutputs) {
|
|
857
|
-
if (rerunHashSet.has(hash)) continue;
|
|
858
|
-
const entry = getBashOutput(id);
|
|
859
|
-
if (entry === null) continue;
|
|
860
|
-
const flatCommand = neutralizeSpokenMarkers(entry.command.replace(/[\t\r\n]+/g, " "));
|
|
861
|
-
cachedOutputRows.push("- `" + flatCommand + "` \u2014 recallable via `bash-output " + id + " --full`");
|
|
862
|
-
}
|
|
863
|
-
const total = rerunRows.length + supersededReadRows.length + cachedOutputRows.length;
|
|
864
|
-
if (total === 0) return [];
|
|
865
|
-
const lines = [];
|
|
866
|
-
lines.push("");
|
|
867
|
-
lines.push("### SAFE_TO_DISCARD (" + total + " items \u2014 provably inert; each is recallable, not gone)");
|
|
868
|
-
appendCappedSection(lines, "Superseded reruns (" + rerunRows.length + "):", rerunRows, MAX_ROWS);
|
|
869
|
-
appendCappedSection(lines, "Superseded file reads (" + supersededReadRows.length + "):", supersededReadRows, MAX_ROWS);
|
|
870
|
-
appendCappedSection(lines, "Other cached bash outputs (" + cachedOutputRows.length + "):", cachedOutputRows, MAX_ROWS);
|
|
871
|
-
return lines;
|
|
796
|
+
|
|
797
|
+
// src/harness_channels.ts
|
|
798
|
+
init_define_import_meta_env();
|
|
799
|
+
var PROMPT_SUBMIT_CONTEXT_DROPPED = /* @__PURE__ */ new Set([]);
|
|
800
|
+
function dropsPromptSubmitContext() {
|
|
801
|
+
return PROMPT_SUBMIT_CONTEXT_DROPPED.has(getHarnessName());
|
|
872
802
|
}
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
result = spawnSync("mem", ["epoch"], {
|
|
877
|
-
encoding: "utf-8",
|
|
878
|
-
timeout: MEM_EPOCH_TIMEOUT_MS,
|
|
879
|
-
windowsHide: true
|
|
880
|
-
});
|
|
881
|
-
} catch {
|
|
882
|
-
return [];
|
|
883
|
-
}
|
|
884
|
-
if (result.error !== void 0 || result.status !== 0) return [];
|
|
885
|
-
const stdout = typeof result.stdout === "string" ? result.stdout.trim() : "";
|
|
886
|
-
if (!/^\d+$/.test(stdout)) return [];
|
|
887
|
-
return [
|
|
888
|
-
"",
|
|
889
|
-
"### mem epoch",
|
|
890
|
-
`mem epoch: ${stdout} (no live TGMEM block is tracked in this session -- only the epoch counter is folded in; see buildMemEpochSection's doc comment)`
|
|
891
|
-
];
|
|
892
|
-
}
|
|
893
|
-
var MANIFEST_PREAMBLE = "When summarizing this session, keep the file paths and symbol names below exactly as written -- they are the handles the next turn needs to resume work. Do not paraphrase them into prose.";
|
|
894
|
-
var BUDGET_ESCALATION_MARKER = "TG-BUDGET-ESCALATION:";
|
|
895
|
-
function summaryBudgetDirective(budgetChars) {
|
|
896
|
-
if (budgetChars <= 0) return "";
|
|
897
|
-
return ` Aim for at most ${budgetChars} characters. Prefer a shorter summary that keeps every path, symbol, command and decision over a longer one that reproduces tool output verbatim: cite the recall id (\`token-goat bash-output <id> --full\`) rather than quoting the output again. If this session genuinely cannot be summarized within that target without losing state the next turn needs, exceed it and open the summary with a single line reading \`${BUDGET_ESCALATION_MARKER} <one-sentence reason>\`.`;
|
|
803
|
+
var PRE_COMPACT_CONTEXT_DROPPED = /* @__PURE__ */ new Set(["copilot_cli", "codex"]);
|
|
804
|
+
function dropsPreCompactContext() {
|
|
805
|
+
return PRE_COMPACT_CONTEXT_DROPPED.has(getHarnessName());
|
|
898
806
|
}
|
|
807
|
+
|
|
808
|
+
// src/hooks_compact.ts
|
|
899
809
|
function preCompactHandler(event) {
|
|
900
|
-
const
|
|
810
|
+
const cfg = loadConfig().compact_assist;
|
|
811
|
+
let out = passOutput();
|
|
812
|
+
if (cfg.enabled) {
|
|
813
|
+
const manifest = buildManifest(event.sessionId, getCwd(event), getTranscriptPath(event));
|
|
814
|
+
if (!dropsPreCompactContext()) {
|
|
815
|
+
out = contextOutput(`${MANIFEST_PREAMBLE}${summaryBudgetDirective(cfg.summary_budget_chars)}
|
|
901
816
|
|
|
902
|
-
${
|
|
817
|
+
${manifest}`);
|
|
818
|
+
} else if (event.sessionId) {
|
|
819
|
+
queuePendingContext(sessionStateKey(event), `${MANIFEST_RECOVERY_PREAMBLE}
|
|
820
|
+
|
|
821
|
+
${manifest.slice(0, MAX_PENDING_CONTEXT_BYTES - MANIFEST_RECOVERY_PREAMBLE.length - 2)}`);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
903
824
|
markCompacted();
|
|
904
825
|
return out;
|
|
905
826
|
}
|
|
906
827
|
registerHook("pre_compact", preCompactHandler);
|
|
907
828
|
var MANIFEST_SURVIVAL_SAMPLE = 64;
|
|
908
|
-
function manifestPathSample(
|
|
909
|
-
|
|
910
|
-
const siblingFiles = sessionId !== void 0 ? listSiblingSessionStates(sessionId).flatMap((s) => s.files) : [];
|
|
911
|
-
const files = siblingFiles.length > 0 ? mergeManifestFiles(ownFiles, siblingFiles) : ownFiles;
|
|
912
|
-
const seen = /* @__PURE__ */ new Set();
|
|
913
|
-
for (const entry of files) {
|
|
914
|
-
if (entry.path) seen.add(entry.path);
|
|
915
|
-
if (seen.size >= MANIFEST_SURVIVAL_SAMPLE) break;
|
|
916
|
-
}
|
|
917
|
-
return [...seen];
|
|
829
|
+
function manifestPathSample(event) {
|
|
830
|
+
return manifestPrintedPaths(event.sessionId, MANIFEST_SURVIVAL_SAMPLE, getCwd(event), getTranscriptPath(event));
|
|
918
831
|
}
|
|
919
832
|
function postCompactHandler(event) {
|
|
920
833
|
const raw = event.raw["compact_summary"];
|
|
921
834
|
const summary = typeof raw === "string" ? raw : "";
|
|
922
835
|
const bytes = Buffer.byteLength(summary, "utf-8");
|
|
923
|
-
const sample = manifestPathSample(event
|
|
836
|
+
const sample = typeof raw === "string" ? manifestPathSample(event) : [];
|
|
924
837
|
const cwd = normalizePath(getCwd(event) ?? process.cwd());
|
|
925
838
|
const spell = (p) => foldPath(p.replaceAll("\\", "/"));
|
|
926
839
|
const haystack = spell(summary);
|
|
@@ -953,7 +866,7 @@ init_define_import_meta_env();
|
|
|
953
866
|
var TRACKED_SKILL = "token-goat";
|
|
954
867
|
var MAX_COMMANDS_SHOWN = 8;
|
|
955
868
|
async function currentCommandNames() {
|
|
956
|
-
const { buildProgram } = await import("./token-goat-chunk-
|
|
869
|
+
const { buildProgram } = await import("./token-goat-chunk-H4VC5TR6.mjs");
|
|
957
870
|
return flattenCommandNames(buildCommandManifest(buildProgram()));
|
|
958
871
|
}
|
|
959
872
|
async function recordSkillVersionSnapshot(sessionId, skillName) {
|
|
@@ -1005,55 +918,11 @@ async function checkSkillVersionDrift(sessionId) {
|
|
|
1005
918
|
}
|
|
1006
919
|
}
|
|
1007
920
|
|
|
1008
|
-
// src/pending_context.ts
|
|
1009
|
-
init_define_import_meta_env();
|
|
1010
|
-
import { readFileSync as readFileSync2, rmSync as rmSync2, writeFileSync } from "node:fs";
|
|
1011
|
-
import { dirname } from "node:path";
|
|
1012
|
-
var PENDING_SUFFIX = ".pending-context.txt";
|
|
1013
|
-
var MAX_PENDING_CONTEXT_BYTES = 4096;
|
|
1014
|
-
function queuePendingContext(sessionId, text) {
|
|
1015
|
-
const trimmed = text.trim();
|
|
1016
|
-
if (trimmed === "") return;
|
|
1017
|
-
const target = sessionSidecarPath(sessionId, PENDING_SUFFIX);
|
|
1018
|
-
if (target === null) return;
|
|
1019
|
-
try {
|
|
1020
|
-
const existing = readPending(target);
|
|
1021
|
-
const merged = existing === null ? trimmed : `${existing}
|
|
1022
|
-
${trimmed}`;
|
|
1023
|
-
const capped = merged.length <= MAX_PENDING_CONTEXT_BYTES ? merged : merged.slice(merged.length - MAX_PENDING_CONTEXT_BYTES);
|
|
1024
|
-
ensureDirSync(dirname(target));
|
|
1025
|
-
writeFileSync(target, capped, "utf8");
|
|
1026
|
-
} catch {
|
|
1027
|
-
}
|
|
1028
|
-
}
|
|
1029
|
-
function drainPendingContext(sessionId) {
|
|
1030
|
-
const target = sessionSidecarPath(sessionId, PENDING_SUFFIX);
|
|
1031
|
-
if (target === null) return null;
|
|
1032
|
-
const text = readPending(target);
|
|
1033
|
-
try {
|
|
1034
|
-
rmSync2(target, { force: true });
|
|
1035
|
-
} catch {
|
|
1036
|
-
}
|
|
1037
|
-
return text;
|
|
1038
|
-
}
|
|
1039
|
-
function readPending(target) {
|
|
1040
|
-
try {
|
|
1041
|
-
const raw = readFileSync2(target, "utf8").trim();
|
|
1042
|
-
return raw === "" ? null : raw;
|
|
1043
|
-
} catch {
|
|
1044
|
-
return null;
|
|
1045
|
-
}
|
|
1046
|
-
}
|
|
1047
|
-
|
|
1048
921
|
// src/hooks_session.ts
|
|
1049
|
-
var PROMPT_SUBMIT_CONTEXT_DROPPED = /* @__PURE__ */ new Set([]);
|
|
1050
|
-
function dropsPromptSubmitContext() {
|
|
1051
|
-
return PROMPT_SUBMIT_CONTEXT_DROPPED.has(getHarnessName());
|
|
1052
|
-
}
|
|
1053
922
|
function pendingContextHandler(event) {
|
|
1054
923
|
try {
|
|
1055
|
-
if (!event.sessionId || !dropsPromptSubmitContext()) return passOutput();
|
|
1056
|
-
const pending =
|
|
924
|
+
if (!event.sessionId || !dropsPromptSubmitContext() && !dropsPreCompactContext()) return passOutput();
|
|
925
|
+
const pending = peekPendingContext(sessionStateKey(event));
|
|
1057
926
|
return pending === null ? passOutput() : contextOutput(pending);
|
|
1058
927
|
} catch {
|
|
1059
928
|
return passOutput();
|
|
@@ -1161,7 +1030,7 @@ async function userPromptSubmitHandler(event) {
|
|
|
1161
1030
|
}
|
|
1162
1031
|
const text = lines.join("\n");
|
|
1163
1032
|
if (dropsPromptSubmitContext()) {
|
|
1164
|
-
queuePendingContext(event
|
|
1033
|
+
queuePendingContext(sessionStateKey(event), text);
|
|
1165
1034
|
return passOutput();
|
|
1166
1035
|
}
|
|
1167
1036
|
return contextOutput(text);
|
|
@@ -1259,13 +1128,25 @@ function reconcileNote(cwd, indexed) {
|
|
|
1259
1128
|
if (drifted > 0) clauses.push(`${countNoun(drifted, "file")} that changed outside this session${breakdown}`);
|
|
1260
1129
|
if (upgraded > 0) clauses.push(`${countNoun(upgraded, "file")} unchanged on disk but indexed by an older version of token-goat`);
|
|
1261
1130
|
return `token-goat: reindexing ${clauses.join(" and ")}${truncated}. Symbol lookups may be briefly stale.`;
|
|
1131
|
+
} catch (e) {
|
|
1132
|
+
recordStat("reconcile_note_failed", 0, 0, void 0, extractErrorMessage(e));
|
|
1133
|
+
return null;
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
async function postCompactRecovery(event) {
|
|
1137
|
+
if (event.raw["source"] !== "compact" || event.sessionId === void 0) return null;
|
|
1138
|
+
if (!loadConfig().compact_assist.enabled) return null;
|
|
1139
|
+
try {
|
|
1140
|
+
const { buildResumePacket } = await import("./token-goat-chunk-2CLWWRFR.mjs");
|
|
1141
|
+
return await buildResumePacket(event.sessionId);
|
|
1262
1142
|
} catch {
|
|
1263
1143
|
return null;
|
|
1264
1144
|
}
|
|
1265
1145
|
}
|
|
1266
|
-
function sessionStartHandler(event) {
|
|
1146
|
+
async function sessionStartHandler(event) {
|
|
1147
|
+
const recovery = await postCompactRecovery(event);
|
|
1267
1148
|
try {
|
|
1268
|
-
if (!loadConfig().hints.session_start_reminder) return passOutput();
|
|
1149
|
+
if (!loadConfig().hints.session_start_reminder) return recovery === null ? passOutput() : contextOutput(recovery);
|
|
1269
1150
|
const cwd = getCwd(event);
|
|
1270
1151
|
const indexed = isIndexedProject(cwd);
|
|
1271
1152
|
let context = buildReminder(indexed);
|
|
@@ -1286,9 +1167,12 @@ ${capsule}`;
|
|
|
1286
1167
|
}
|
|
1287
1168
|
} catch {
|
|
1288
1169
|
}
|
|
1170
|
+
if (recovery !== null) context += `
|
|
1171
|
+
|
|
1172
|
+
${recovery}`;
|
|
1289
1173
|
return contextOutput(context);
|
|
1290
1174
|
} catch {
|
|
1291
|
-
return passOutput();
|
|
1175
|
+
return recovery === null ? passOutput() : contextOutput(recovery);
|
|
1292
1176
|
}
|
|
1293
1177
|
}
|
|
1294
1178
|
registerHook("session_start", sessionStartHandler);
|
|
@@ -1870,11 +1754,11 @@ function mapInner(r, f) {
|
|
|
1870
1754
|
function mapOuter(r, f) {
|
|
1871
1755
|
return r.matched ? f(r) : r;
|
|
1872
1756
|
}
|
|
1873
|
-
function ab(pa, pb,
|
|
1874
|
-
return (data, i) => mapOuter(pa(data, i), (ma) => mapInner(pb(data, ma.position), (vb, j) =>
|
|
1757
|
+
function ab(pa, pb, join4) {
|
|
1758
|
+
return (data, i) => mapOuter(pa(data, i), (ma) => mapInner(pb(data, ma.position), (vb, j) => join4(ma.value, vb, data, i, j)));
|
|
1875
1759
|
}
|
|
1876
|
-
function abc(pa, pb, pc,
|
|
1877
|
-
return (data, i) => mapOuter(pa(data, i), (ma) => mapOuter(pb(data, ma.position), (mb) => mapInner(pc(data, mb.position), (vc, j) =>
|
|
1760
|
+
function abc(pa, pb, pc, join4) {
|
|
1761
|
+
return (data, i) => mapOuter(pa(data, i), (ma) => mapOuter(pb(data, ma.position), (mb) => mapInner(pc(data, mb.position), (vc, j) => join4(ma.value, mb.value, vc, data, i, j))));
|
|
1878
1762
|
}
|
|
1879
1763
|
function ahead(p) {
|
|
1880
1764
|
return (data, i) => mapOuter(p(data, i), (m1) => ({
|
|
@@ -5851,8 +5735,8 @@ function trimCharacterEnd(str, char) {
|
|
|
5851
5735
|
function unicodeEscape(str) {
|
|
5852
5736
|
return str.replace(/[\s\S]/g, (c) => "\\u" + c.charCodeAt().toString(16).padStart(4, "0"));
|
|
5853
5737
|
}
|
|
5854
|
-
function get(obj,
|
|
5855
|
-
for (const key of
|
|
5738
|
+
function get(obj, path6) {
|
|
5739
|
+
for (const key of path6) {
|
|
5856
5740
|
if (!obj) {
|
|
5857
5741
|
return void 0;
|
|
5858
5742
|
}
|
|
@@ -7053,8 +6937,8 @@ function withBrackets(str, brackets) {
|
|
|
7053
6937
|
const rbr = typeof brackets[1] === "string" ? brackets[1] : "]";
|
|
7054
6938
|
return lbr + str + rbr;
|
|
7055
6939
|
}
|
|
7056
|
-
function pathRewrite(
|
|
7057
|
-
const modifiedPath = typeof rewriter === "function" ? rewriter(
|
|
6940
|
+
function pathRewrite(path6, rewriter, baseUrl, metadata, elem) {
|
|
6941
|
+
const modifiedPath = typeof rewriter === "function" ? rewriter(path6, metadata, elem) : path6;
|
|
7058
6942
|
return modifiedPath[0] === "/" && baseUrl ? trimCharacterEnd(baseUrl, "/") + modifiedPath : modifiedPath;
|
|
7059
6943
|
}
|
|
7060
6944
|
function formatImage(elem, walk, builder, formatOptions) {
|
|
@@ -7378,9 +7262,9 @@ function handleDeprecatedOptions(options) {
|
|
|
7378
7262
|
options.selectors.push(...tagDefinitions);
|
|
7379
7263
|
options.selectors = mergeDuplicatesPreferLast(options.selectors, ((s) => s.selector));
|
|
7380
7264
|
}
|
|
7381
|
-
function set(obj,
|
|
7382
|
-
const valueKey =
|
|
7383
|
-
for (const key of
|
|
7265
|
+
function set(obj, path6, value) {
|
|
7266
|
+
const valueKey = path6.pop();
|
|
7267
|
+
for (const key of path6) {
|
|
7384
7268
|
let nested = obj[key];
|
|
7385
7269
|
if (!nested) {
|
|
7386
7270
|
nested = {};
|
|
@@ -7670,20 +7554,149 @@ registerHook("post_tool_use", postSkillHandler, { toolName: "Skill" });
|
|
|
7670
7554
|
|
|
7671
7555
|
// src/hooks_bash.ts
|
|
7672
7556
|
init_define_import_meta_env();
|
|
7673
|
-
import { statSync as statSync6, existsSync } from "node:fs";
|
|
7674
7557
|
|
|
7675
|
-
// src/
|
|
7558
|
+
// src/bash_structural_index.ts
|
|
7559
|
+
init_define_import_meta_env();
|
|
7560
|
+
|
|
7561
|
+
// src/index_freshness.ts
|
|
7676
7562
|
init_define_import_meta_env();
|
|
7677
|
-
|
|
7563
|
+
function indexMatchesDisk(resolved) {
|
|
7564
|
+
const entry = getFileEntry(resolved);
|
|
7565
|
+
if (entry === null) return false;
|
|
7566
|
+
if (entry.sha === "") return true;
|
|
7567
|
+
const diskSha = fingerprintFile(resolved);
|
|
7568
|
+
return diskSha !== null && diskSha === entry.sha;
|
|
7569
|
+
}
|
|
7570
|
+
|
|
7571
|
+
// src/bash_structural_index.ts
|
|
7572
|
+
import { statSync as statSync4 } from "node:fs";
|
|
7573
|
+
var ALLOWED_BINARIES = /* @__PURE__ */ new Set(["rg", "grep", "egrep", "fgrep"]);
|
|
7574
|
+
var PY_DEF_RE = /^\^?(?:async\s+)?def\b(?:\s+([A-Za-z_]\w*))?$/;
|
|
7575
|
+
var PY_CLASS_RE = /^\^?class\b(?:\s+([A-Za-z_]\w*))?$/;
|
|
7576
|
+
var IMPORT_RE = /^\^import\b/;
|
|
7577
|
+
var SHELL_SAFE_UNQUOTED_RE = /^[A-Za-z0-9_.:/+,=@^-]+$/;
|
|
7578
|
+
var SHELL_UNSAFE_IN_EITHER_QUOTING_RE = /[$`"\\\r\n]/;
|
|
7579
|
+
function dualShellArg(s) {
|
|
7580
|
+
if (SHELL_SAFE_UNQUOTED_RE.test(s)) return s;
|
|
7581
|
+
if (SHELL_UNSAFE_IN_EITHER_QUOTING_RE.test(s)) return null;
|
|
7582
|
+
return `"${s}"`;
|
|
7583
|
+
}
|
|
7584
|
+
function detectStructuralIndexRewrite(rawCmd, cwd) {
|
|
7585
|
+
try {
|
|
7586
|
+
const detected = detectFromCommand(rawCmd, cwd);
|
|
7587
|
+
if (detected === null || detected.filter.name !== "grep") return null;
|
|
7588
|
+
const argv = detected.argv;
|
|
7589
|
+
if (argv.length !== 3) return null;
|
|
7590
|
+
const [bin, pattern, rawPath] = argv;
|
|
7591
|
+
if (bin === void 0 || pattern === void 0 || rawPath === void 0) return null;
|
|
7592
|
+
if (!ALLOWED_BINARIES.has(pathStem(bin).toLowerCase())) return null;
|
|
7593
|
+
if (/[*?[\]{}]/.test(rawPath)) return null;
|
|
7594
|
+
const resolved = resolveIndexPath(rawPath, cwd);
|
|
7595
|
+
let stat;
|
|
7596
|
+
try {
|
|
7597
|
+
stat = statSync4(resolved);
|
|
7598
|
+
} catch {
|
|
7599
|
+
return null;
|
|
7600
|
+
}
|
|
7601
|
+
if (!stat.isFile()) return null;
|
|
7602
|
+
if (!indexMatchesDisk(resolved)) return null;
|
|
7603
|
+
const pathArg = dualShellArg(rawPath);
|
|
7604
|
+
if (pathArg === null) return null;
|
|
7605
|
+
let target = null;
|
|
7606
|
+
if (DOC_EXT_RE.test(rawPath) && STRUCTURAL_DOC_PATTERN_RE.test(pattern)) {
|
|
7607
|
+
target = { command: `outline ${pathArg}`, kind: "headings" };
|
|
7608
|
+
} else {
|
|
7609
|
+
const lang = detectLanguage(resolved);
|
|
7610
|
+
const trimmedPattern = pattern.trim();
|
|
7611
|
+
if (lang === "python" && languageHasFlag(lang, "grepSource")) {
|
|
7612
|
+
const defMatch = PY_DEF_RE.exec(trimmedPattern);
|
|
7613
|
+
const classMatch = defMatch === null ? PY_CLASS_RE.exec(trimmedPattern) : null;
|
|
7614
|
+
const nameMatch = defMatch ?? classMatch;
|
|
7615
|
+
if (nameMatch !== null) {
|
|
7616
|
+
const name2 = nameMatch[1];
|
|
7617
|
+
if (name2) {
|
|
7618
|
+
const grepArg = dualShellArg("^" + name2);
|
|
7619
|
+
target = grepArg === null ? null : { command: `outline ${pathArg} --grep ${grepArg}`, kind: `symbols named starting with ${name2} (functions, classes or any other kind sharing that name)` };
|
|
7620
|
+
} else {
|
|
7621
|
+
target = { command: `outline ${pathArg}`, kind: "all symbols in the file (not just functions/classes)" };
|
|
7622
|
+
}
|
|
7623
|
+
} else if (IMPORT_RE.test(trimmedPattern)) {
|
|
7624
|
+
target = { command: `imports ${pathArg}`, kind: "imports" };
|
|
7625
|
+
}
|
|
7626
|
+
} else if (lang !== "unknown" && languageHasFlag(lang, "grepSource") && IMPORT_RE.test(trimmedPattern)) {
|
|
7627
|
+
target = { command: `imports ${pathArg}`, kind: "imports" };
|
|
7628
|
+
}
|
|
7629
|
+
}
|
|
7630
|
+
if (target === null) return null;
|
|
7631
|
+
const notice = `[token-goat: rewrote this rg/grep search to an indexed answer (${target.kind}) instead of running it -- re-run your original command yourself for a literal text search]`;
|
|
7632
|
+
const noticeArg = dualShellArg(notice);
|
|
7633
|
+
if (noticeArg === null) return null;
|
|
7634
|
+
return { command: `token-goat --notice ${noticeArg} ${target.command}`, kind: target.kind };
|
|
7635
|
+
} catch {
|
|
7636
|
+
return null;
|
|
7637
|
+
}
|
|
7638
|
+
}
|
|
7639
|
+
|
|
7640
|
+
// src/bash_range_savings.ts
|
|
7641
|
+
init_define_import_meta_env();
|
|
7642
|
+
import { statSync as statSync5, readFileSync as readFileSync3 } from "node:fs";
|
|
7643
|
+
var ALL_SYMBOLS_IN_FILE = -1;
|
|
7644
|
+
var REGION_HEADER_BYTES = 56;
|
|
7645
|
+
function rangeSubstituteFor(hintPath, cwd, ranges) {
|
|
7646
|
+
if (ranges.length === 0) return null;
|
|
7647
|
+
try {
|
|
7648
|
+
const resolved = resolveIndexPath(hintPath, cwd);
|
|
7649
|
+
if (!statSync5(resolved).isFile()) return null;
|
|
7650
|
+
if (!indexMatchesDisk(resolved)) return null;
|
|
7651
|
+
const lines = readFileSync3(resolved, "utf8").split("\n");
|
|
7652
|
+
const symbols = querySymbols({ filePath: resolved, limit: ALL_SYMBOLS_IN_FILE });
|
|
7653
|
+
if (symbols.length === 0) return null;
|
|
7654
|
+
const bytesOf = (start, end) => Buffer.byteLength(lines.slice(start - 1, Math.min(end, lines.length)).join("\n"), "utf8");
|
|
7655
|
+
const requestedLines = /* @__PURE__ */ new Set();
|
|
7656
|
+
const regionBySpan = /* @__PURE__ */ new Map();
|
|
7657
|
+
for (const [start, end] of ranges) {
|
|
7658
|
+
for (let l = start; l <= Math.min(end, lines.length); l++) requestedLines.add(l);
|
|
7659
|
+
for (const r of resolveLineRegions(symbols, lines.length, start, end)) {
|
|
7660
|
+
const named = symbols.find((s) => s.lineStart === r.start && s.lineEnd === r.end);
|
|
7661
|
+
regionBySpan.set(`${r.start}-${r.end}`, {
|
|
7662
|
+
kind: r.kind,
|
|
7663
|
+
name: r.kind === "symbol" && r.end > r.start && named !== void 0 ? named.name : null,
|
|
7664
|
+
start: r.start,
|
|
7665
|
+
end: r.end
|
|
7666
|
+
});
|
|
7667
|
+
}
|
|
7668
|
+
}
|
|
7669
|
+
if (regionBySpan.size === 0) return null;
|
|
7670
|
+
let requestedBytes = 0;
|
|
7671
|
+
for (const l of requestedLines) requestedBytes += Buffer.byteLength(lines[l - 1] ?? "", "utf8") + 1;
|
|
7672
|
+
const regions = [...regionBySpan.values()].sort((a, b) => a.start - b.start || a.end - b.end);
|
|
7673
|
+
const replacementBytes = regions.reduce((t, r) => t + bytesOf(r.start, r.end) + REGION_HEADER_BYTES, 0);
|
|
7674
|
+
const commands = regions.map(
|
|
7675
|
+
(r) => r.name === null ? `token-goat read "${hintPath}:${r.start}-${r.end}"` : `token-goat read "${hintPath}::${r.name}"`
|
|
7676
|
+
);
|
|
7677
|
+
return { requestedBytes, replacementBytes, commands };
|
|
7678
|
+
} catch {
|
|
7679
|
+
return null;
|
|
7680
|
+
}
|
|
7681
|
+
}
|
|
7682
|
+
|
|
7683
|
+
// src/bash_surgical_target.ts
|
|
7684
|
+
init_define_import_meta_env();
|
|
7685
|
+
import { statSync as statSync7 } from "node:fs";
|
|
7678
7686
|
|
|
7679
7687
|
// src/bash_extractors.ts
|
|
7680
7688
|
init_define_import_meta_env();
|
|
7681
|
-
import { statSync as
|
|
7682
|
-
function
|
|
7683
|
-
return
|
|
7689
|
+
import { statSync as statSync6, openSync as openSync2, readSync, closeSync as closeSync2 } from "node:fs";
|
|
7690
|
+
function genericSurgicalFallback(shown) {
|
|
7691
|
+
return '`token-goat outline "' + shown + '"`';
|
|
7692
|
+
}
|
|
7693
|
+
function surgicalHintFor(hintPath, isEnv, isConfig, isDoc, isXml = false, target = null) {
|
|
7694
|
+
const key = target ?? "KEY_NAME";
|
|
7695
|
+
const section = target ?? "SectionHeading";
|
|
7696
|
+
return isXml ? 'Use `token-goat xml-outline "' + hintPath + '"` to inspect structure, or `token-goat xml-query "' + hintPath + '" "<selector>"` to query specific nodes.' : isEnv ? 'Use `token-goat config-get "' + hintPath + '" ' + key + "` to read a specific variable." : isConfig ? target === null ? 'Use `token-goat config-get "' + hintPath + '" KEY_NAME` or `token-goat section "' + hintPath + '::sectionName"` to read a specific value.' : 'Use `token-goat config-get "' + hintPath + '" ' + key + "` to read a specific value, or " + genericSurgicalFallback(hintPath) + " for every key with line ranges." : isDoc ? 'Use `token-goat section "' + hintPath + "::" + section + '"` to read one section' + (target === null ? "." : ", or " + genericSurgicalFallback(hintPath) + " for every heading with line ranges.") : "Use " + genericSurgicalFallback(hintPath) + " to read one function or class.";
|
|
7684
7697
|
}
|
|
7685
7698
|
function surgicalHintForConfigDoc(filePath, isConfig, isDoc, isSql, isXml = false) {
|
|
7686
|
-
return isXml ? 'Use `token-goat xml-outline "' + filePath + '"` to inspect structure, or `token-goat xml-query "' + filePath + '" "<selector>"` to query specific nodes.' : isConfig ? 'Use `token-goat config-get "' + filePath + '" KEY_NAME` or `token-goat section "' + filePath + '::sectionName"` to read a specific value.' : isSql ? 'Use `token-goat section "' + filePath + '::table_name"` to pull one CREATE TABLE / CREATE TYPE block.' : isDoc ? 'Use `token-goat section "' + filePath + '::SectionHeading"` to read one section.' :
|
|
7699
|
+
return isXml ? 'Use `token-goat xml-outline "' + filePath + '"` to inspect structure, or `token-goat xml-query "' + filePath + '" "<selector>"` to query specific nodes.' : isConfig ? 'Use `token-goat config-get "' + filePath + '" KEY_NAME` or `token-goat section "' + filePath + '::sectionName"` to read a specific value.' : isSql ? 'Use `token-goat section "' + filePath + '::table_name"` to pull one CREATE TABLE / CREATE TYPE block.' : isDoc ? 'Use `token-goat section "' + filePath + '::SectionHeading"` to read one section.' : "Use " + genericSurgicalFallback(filePath) + ' or `token-goat skeleton "' + filePath + '"` to see the file structure.';
|
|
7687
7700
|
}
|
|
7688
7701
|
function splitShellSegments(cmd) {
|
|
7689
7702
|
const segments = [];
|
|
@@ -7706,6 +7719,10 @@ function splitShellSegments(cmd) {
|
|
|
7706
7719
|
i++;
|
|
7707
7720
|
continue;
|
|
7708
7721
|
}
|
|
7722
|
+
if (ch === "&" && isRedirectAmpersand(cmd, i)) {
|
|
7723
|
+
cur += ch;
|
|
7724
|
+
continue;
|
|
7725
|
+
}
|
|
7709
7726
|
if (ch === "|" || ch === ";" || ch === "\n" || ch === "&") {
|
|
7710
7727
|
segments.push(cur);
|
|
7711
7728
|
cur = "";
|
|
@@ -7756,6 +7773,7 @@ function classifyCatPath(filePath, cmd0) {
|
|
|
7756
7773
|
return { filePath, ...flags, cmd0 };
|
|
7757
7774
|
}
|
|
7758
7775
|
function extractCatFile(cmd) {
|
|
7776
|
+
if (/-(?:TotalCount|Head|First|Tail)\b/i.test(cmd)) return null;
|
|
7759
7777
|
const m = /^(cat|bat|type|Get-Content|gc)(?:\s+(?:-[a-zA-Z]+|--[a-zA-Z-]+))*\s+(?:"([^"]+)"|'([^']+)'|(\S+?))(?:\s+-[a-zA-Z].*?)?(?:\s+2>(&1|\/dev\/null))?\s*$/i.exec(cmd);
|
|
7760
7778
|
if (!m) return null;
|
|
7761
7779
|
const cmd0 = m[1];
|
|
@@ -7782,7 +7800,7 @@ var PS_FILE_METHOD_RE = /\[(?:System\.)?IO\.File\]::(?:ReadAllText|ReadAllLines|
|
|
|
7782
7800
|
var PS_TEMP_READ_FLOOD_BYTES = 16 * 1024;
|
|
7783
7801
|
function isLargeFileOnDisk(filePath, floor) {
|
|
7784
7802
|
try {
|
|
7785
|
-
return
|
|
7803
|
+
return statSync6(filePath).size >= floor;
|
|
7786
7804
|
} catch {
|
|
7787
7805
|
return false;
|
|
7788
7806
|
}
|
|
@@ -7796,6 +7814,7 @@ function extractPowerShellWrappedGetContent(cmd, event) {
|
|
|
7796
7814
|
if (!w) return null;
|
|
7797
7815
|
const inner = (w[1] ?? w[2] ?? "").trim();
|
|
7798
7816
|
if (!inner) return null;
|
|
7817
|
+
if (/-(?:TotalCount|Head|First|Tail)\b/i.test(inner)) return null;
|
|
7799
7818
|
const m = PS_GETCONTENT_INNER_RE.exec(inner);
|
|
7800
7819
|
if (!m) return null;
|
|
7801
7820
|
const filePath = m[1] ?? m[2] ?? m[3];
|
|
@@ -8215,27 +8234,21 @@ function extractLineRangeReadsCompound(cmd) {
|
|
|
8215
8234
|
}
|
|
8216
8235
|
return [...merged.values()];
|
|
8217
8236
|
}
|
|
8218
|
-
function sedRangeHint(filePath, ranges, tool) {
|
|
8219
|
-
|
|
8220
|
-
|
|
8221
|
-
|
|
8222
|
-
const
|
|
8223
|
-
|
|
8224
|
-
|
|
8225
|
-
|
|
8226
|
-
if (lang === "toml" || lang === "json" || lang === "yaml" || lang === "ini") {
|
|
8227
|
-
return prefix + 'For config, `token-goat config-get "' + filePath + '" <key>` or `token-goat section "' + filePath + '::<block>"` extracts one value; or ' + allReads + " for exactly those lines.";
|
|
8228
|
-
}
|
|
8229
|
-
if (languageHasFlag(lang, "symbolBearing")) {
|
|
8230
|
-
return prefix + 'For a whole function/class, `token-goat symbol <name>` or `token-goat read "' + filePath + '::<Symbol>"` is robust to line shifts; or ' + allReads + " for exactly those lines.";
|
|
8231
|
-
}
|
|
8232
|
-
return prefix + "Use " + allReads + " to read exactly those lines.";
|
|
8237
|
+
function sedRangeHint(filePath, ranges, tool, sub) {
|
|
8238
|
+
return "`" + (tool === "awk" ? "awk" : "sed -n") + "` line-range reads bypass read hooks. " + substituteSentence(filePath, ranges, sub);
|
|
8239
|
+
}
|
|
8240
|
+
function substituteSentence(filePath, ranges, sub) {
|
|
8241
|
+
const spans = ranges.map(([s, e]) => s + "-" + e).join(", ");
|
|
8242
|
+
const reads = sub.commands.map((c) => "`" + c + "`");
|
|
8243
|
+
const allReads = reads.length === 2 ? reads.join(" and ") : reads.length >= 3 ? reads.slice(0, -1).join(", ") + ", and " + reads[reads.length - 1] : reads[0];
|
|
8244
|
+
return "Lines " + spans + " of " + filePath + " cost " + sub.requestedBytes + " bytes; " + allReads + " returns the same content as " + sub.replacementBytes + " bytes, resolved to whole regions and robust to line shifts.";
|
|
8233
8245
|
}
|
|
8234
|
-
function leadingLinesHint(lead, hintPath, start, end,
|
|
8246
|
+
function leadingLinesHint(lead, hintPath, start, end, preHookCwd, substitute) {
|
|
8235
8247
|
const key = resolveIndexPath(hintPath, preHookCwd ?? process.cwd());
|
|
8236
8248
|
const prior = findRangeOverlap(getFileLineRanges(key), start, end);
|
|
8237
8249
|
if (prior !== null) return sedOverlapHint(hintPath, prior, start, end);
|
|
8238
|
-
|
|
8250
|
+
if (substitute === null) return null;
|
|
8251
|
+
return lead + substituteSentence(hintPath, [[start, end]], substitute);
|
|
8239
8252
|
}
|
|
8240
8253
|
function findRangeOverlap(prior, start, end) {
|
|
8241
8254
|
let best = null;
|
|
@@ -8326,6 +8339,25 @@ function extractGetContentSelectFirst(cmd) {
|
|
|
8326
8339
|
const { isDoc, isConfig, isSql, isXml } = classifyDocConfig(filePath);
|
|
8327
8340
|
return { filePath, isDoc, isConfig, isSql, isXml, n };
|
|
8328
8341
|
}
|
|
8342
|
+
function extractGetContentHead(cmd) {
|
|
8343
|
+
const headMatch = /-(?:TotalCount|Head|First)\s+(\d+)/i.exec(cmd);
|
|
8344
|
+
if (!headMatch) return null;
|
|
8345
|
+
const n = parseInt(headMatch[1], 10);
|
|
8346
|
+
if (n <= 10) return null;
|
|
8347
|
+
const getnMatch = /^(Get-Content|gc)\s+/i.exec(cmd);
|
|
8348
|
+
if (!getnMatch) return null;
|
|
8349
|
+
const afterCmd = cmd.slice(getnMatch[0].length).replace(/-Path\s+/i, "");
|
|
8350
|
+
const beforeHead = afterCmd.split(/-(?:TotalCount|Head|First)/i)[0]?.trim() ?? "";
|
|
8351
|
+
const afterHead = afterCmd.split(/-(?:TotalCount|Head|First)\s+\d+/i)[1]?.trim() ?? "";
|
|
8352
|
+
const cleanedBefore = beforeHead.replace(/-(?:Encoding|Delimiter|Wait)\s+\S+/gi, "").replace(/-[a-zA-Z]+/g, "").trim();
|
|
8353
|
+
const cleanedAfter = afterHead.replace(/-(?:Encoding|Delimiter|Wait)\s+\S+/gi, "").replace(/-[a-zA-Z]+/g, "").trim();
|
|
8354
|
+
const filePath = (cleanedBefore || cleanedAfter).replace(/^["']|["']$/g, "");
|
|
8355
|
+
if (!filePath) return null;
|
|
8356
|
+
if (isTempPath(filePath)) return null;
|
|
8357
|
+
if (!/\.(?:ts|tsx|js|jsx|py|go|java|rs|rb|cs|md|mdx|rst|txt|json|yaml|yml|toml|sql|sh|ps1|psm1|xml|dtsx|ampkg|xaml)$/i.test(filePath)) return null;
|
|
8358
|
+
const { isDoc, isConfig, isSql, isXml } = classifyDocConfig(filePath);
|
|
8359
|
+
return { filePath, isDoc, isConfig, isSql, isXml, n };
|
|
8360
|
+
}
|
|
8329
8361
|
function taskOutputIsJsonlTranscript(outPath) {
|
|
8330
8362
|
let fd = null;
|
|
8331
8363
|
try {
|
|
@@ -8379,7 +8411,7 @@ function extractTasksOutput(cmd) {
|
|
|
8379
8411
|
return null;
|
|
8380
8412
|
}
|
|
8381
8413
|
function extractToolResultsFile(cmd) {
|
|
8382
|
-
const toolResultsRe =
|
|
8414
|
+
const toolResultsRe = new RegExp("[/\\\\]tool-results[/\\\\](" + TOOL_RESULTS_ID_CHARS + ")\\.txt$", "i");
|
|
8383
8415
|
const catM = /^cat(?:\s+(?:-[a-zA-Z]+|--[a-zA-Z-]+))*\s+(?:"([^"]+)"|'([^']+)'|(\S+))\s*$/.exec(cmd);
|
|
8384
8416
|
if (catM) {
|
|
8385
8417
|
const fp = catM[1] ?? catM[2] ?? catM[3];
|
|
@@ -8733,7 +8765,38 @@ function extractTerminalXmlParsing(cmd) {
|
|
|
8733
8765
|
return null;
|
|
8734
8766
|
}
|
|
8735
8767
|
|
|
8768
|
+
// src/bash_surgical_target.ts
|
|
8769
|
+
var ALL_SYMBOLS_IN_FILE2 = -1;
|
|
8770
|
+
function quotableName(name2) {
|
|
8771
|
+
const safe = escapeHintName(name2);
|
|
8772
|
+
return safe === "" || safe.includes("::") ? null : safe;
|
|
8773
|
+
}
|
|
8774
|
+
function runnableTargetFor(hintPath, cwd, event) {
|
|
8775
|
+
try {
|
|
8776
|
+
if (!commandPathIsTouchable(hintPath, event)) return null;
|
|
8777
|
+
const resolved = resolveIndexPath(hintPath, cwd);
|
|
8778
|
+
if (!statSync7(resolved).isFile()) return null;
|
|
8779
|
+
if (!indexMatchesDisk(resolved)) return null;
|
|
8780
|
+
const symbols = querySymbols({ filePath: resolved, limit: ALL_SYMBOLS_IN_FILE2 });
|
|
8781
|
+
if (symbols.length === 0) return null;
|
|
8782
|
+
const inLineOrder = [...symbols].sort((a, b) => a.lineStart - b.lineStart);
|
|
8783
|
+
for (const s of inLineOrder) {
|
|
8784
|
+
const safe = quotableName(s.name);
|
|
8785
|
+
if (safe !== null) return safe;
|
|
8786
|
+
}
|
|
8787
|
+
return null;
|
|
8788
|
+
} catch {
|
|
8789
|
+
return null;
|
|
8790
|
+
}
|
|
8791
|
+
}
|
|
8792
|
+
|
|
8793
|
+
// src/hooks_bash.ts
|
|
8794
|
+
import { statSync as statSync9, existsSync, readFileSync as readFileSync4 } from "node:fs";
|
|
8795
|
+
import * as path4 from "node:path";
|
|
8796
|
+
|
|
8736
8797
|
// src/hooks_bash_commands.ts
|
|
8798
|
+
init_define_import_meta_env();
|
|
8799
|
+
import { statSync as statSync8 } from "node:fs";
|
|
8737
8800
|
var CD_PREFIX_RE = /^(?:cd\s+(?:"[^"]*"|'[^']*'|\S+)[ \t]*(?:&&|;|\r?\n)\s*)+/;
|
|
8738
8801
|
function stripCdPrefix(cmd) {
|
|
8739
8802
|
const stripped = cmd.replace(CD_PREFIX_RE, "");
|
|
@@ -8949,7 +9012,7 @@ function workingTreeStatusPaths(gitDir) {
|
|
|
8949
9012
|
function enqueueRewrittenPath(absPath) {
|
|
8950
9013
|
if (/(?:^|[/\\])(?:NUL|nul)$/.test(absPath) || absPath.replace(/\\/g, "/").endsWith("/dev/null")) return;
|
|
8951
9014
|
try {
|
|
8952
|
-
if (!
|
|
9015
|
+
if (!statSync8(absPath).isFile()) return;
|
|
8953
9016
|
} catch {
|
|
8954
9017
|
return;
|
|
8955
9018
|
}
|
|
@@ -8997,7 +9060,8 @@ function pipelineShapeFilter(cmd, cwd) {
|
|
|
8997
9060
|
}
|
|
8998
9061
|
}
|
|
8999
9062
|
if (recognizedCiCount > 0) {
|
|
9000
|
-
|
|
9063
|
+
const ci = filterByName("generic-ci");
|
|
9064
|
+
return ci === null ? null : { filter: ci, argv: [] };
|
|
9001
9065
|
}
|
|
9002
9066
|
}
|
|
9003
9067
|
return null;
|
|
@@ -9012,7 +9076,16 @@ function pipelineShapeFilter(cmd, cwd) {
|
|
|
9012
9076
|
if (!PIPELINE_PASSTHROUGH_HEADS.has(head.replace(/^.*[/\\]/, ""))) return null;
|
|
9013
9077
|
}
|
|
9014
9078
|
const detected = detectFromCommand(stripOutputPipeline(cmd), cwd ?? void 0);
|
|
9015
|
-
return detected === null ? null : detected.filter;
|
|
9079
|
+
return detected === null ? null : { filter: detected.filter, argv: detected.argv };
|
|
9080
|
+
}
|
|
9081
|
+
function isFullRecallCommand(cmd) {
|
|
9082
|
+
const forSplit = cmd.replace(/\s2>(?:&1|\/dev\/null)/g, "");
|
|
9083
|
+
if (hasBareBackgroundOrNewline(forSplit)) return false;
|
|
9084
|
+
const first2 = splitShellSegments(forSplit)[0];
|
|
9085
|
+
if (first2 === void 0 || !segmentCommandIs(first2, "token-goat")) return false;
|
|
9086
|
+
const tokens = safeShlexSplit(stripOutputPipeline(first2));
|
|
9087
|
+
if (tokens === null) return false;
|
|
9088
|
+
return Object.values(RECALL_COMMAND).includes(tokens[1] ?? "") && tokens.includes("--full");
|
|
9016
9089
|
}
|
|
9017
9090
|
function pureFileReadPath(cmd) {
|
|
9018
9091
|
const single = extractCatFile(cmd)?.filePath ?? extractHeadFile(cmd)?.filePath ?? extractTailFile(cmd)?.filePath ?? extractLineRangeRead(cmd)?.filePath;
|
|
@@ -9218,6 +9291,7 @@ function detectUnbalancedShellSyntax(cmd) {
|
|
|
9218
9291
|
function maybeCompressRewrite(event, rawCmd, cmd) {
|
|
9219
9292
|
if (process.env["TOKEN_GOAT_BASH_COMPRESS"] === "0") return null;
|
|
9220
9293
|
if (event.raw["_tg_harness"] === "vscode") return null;
|
|
9294
|
+
if ((event.raw["_tg_harness"] === "codex" || event.raw["_tg_harness"] === "copilot_cli") && process.platform === "win32") return null;
|
|
9221
9295
|
let cfg;
|
|
9222
9296
|
try {
|
|
9223
9297
|
cfg = loadConfig().bash_compress;
|
|
@@ -9286,7 +9360,7 @@ function foldShellReadBodies(cmd, output, fileKey, cwd) {
|
|
|
9286
9360
|
if (no === void 0 || no === null) return null;
|
|
9287
9361
|
rows.push({ no, text: lines[i] ?? "", raw: lines[i] ?? "" });
|
|
9288
9362
|
}
|
|
9289
|
-
const headRead = extractHeadFile(cmd);
|
|
9363
|
+
const headRead = extractHeadFile(cmd) ?? extractGetContentHead(cmd);
|
|
9290
9364
|
const windowed = extractLineRangeRead(cmd) !== null || headRead !== null && lines.length >= headRead.n;
|
|
9291
9365
|
const folded = foldDelivery(rows, fileKey, displaySafePath(toDisplayPath(findProject(cwd ?? process.cwd())?.root, fileKey)), windowed);
|
|
9292
9366
|
if (folded === null) return null;
|
|
@@ -9297,7 +9371,7 @@ function foldShellReadStructure(cmd, filePath, output, fileKey, cwd) {
|
|
|
9297
9371
|
const originalBytes = Buffer.byteLength(output, "utf-8");
|
|
9298
9372
|
let onDisk;
|
|
9299
9373
|
try {
|
|
9300
|
-
onDisk =
|
|
9374
|
+
onDisk = statSync9(fileKey).size;
|
|
9301
9375
|
} catch {
|
|
9302
9376
|
return null;
|
|
9303
9377
|
}
|
|
@@ -9405,6 +9479,7 @@ async function maybeElideServedGenericOutput(cmd, output, exitCode, cwd, cacheMi
|
|
|
9405
9479
|
async function maybeCompressCompoundOutput(cmd, output, exitCode, cwd, cacheMinBytes, isUnwrapped = false) {
|
|
9406
9480
|
if (process.env["TOKEN_GOAT_BASH_COMPRESS"] === "0") return null;
|
|
9407
9481
|
if (!isUnwrapped && isCompressibleSingleCommand(cmd)) return null;
|
|
9482
|
+
if (isFullRecallCommand(cmd)) return null;
|
|
9408
9483
|
if (exitCode !== null && exitCode !== 0) return null;
|
|
9409
9484
|
let cfg;
|
|
9410
9485
|
try {
|
|
@@ -9414,16 +9489,18 @@ async function maybeCompressCompoundOutput(cmd, output, exitCode, cwd, cacheMinB
|
|
|
9414
9489
|
}
|
|
9415
9490
|
if (!cfg.enabled || cfg.disabled_filters.includes("generic")) return null;
|
|
9416
9491
|
if (Buffer.byteLength(output, "utf-8") < cacheMinBytes) return null;
|
|
9417
|
-
const shaped = pipelineShapeFilter(cmd, cwd) ?? (isUnwrapped ? detectFromCommand(cmd, cwd ?? void 0)
|
|
9418
|
-
const
|
|
9492
|
+
const shaped = pipelineShapeFilter(cmd, cwd) ?? (isUnwrapped ? detectFromCommand(cmd, cwd ?? void 0) ?? null : null);
|
|
9493
|
+
const useShaped = shaped !== null && !cfg.disabled_filters.includes(shaped.filter.name);
|
|
9494
|
+
const filter3 = useShaped ? shaped.filter : filterByName("generic");
|
|
9419
9495
|
if (filter3 === null) return null;
|
|
9420
|
-
const
|
|
9496
|
+
const filterArgv = useShaped ? shaped.argv : [];
|
|
9497
|
+
const compressed = compressOutput(filter3, output, "", exitCode ?? 0, filterArgv, {
|
|
9421
9498
|
maxLines: cfg.max_lines,
|
|
9422
9499
|
maxBytes: cfg.max_bytes
|
|
9423
9500
|
});
|
|
9424
9501
|
const minNet = resolveMinNetSavingsBytes();
|
|
9425
9502
|
if (!compressed.worthApplying(minNet)) return null;
|
|
9426
|
-
const id =
|
|
9503
|
+
const id = bashOutputIdSync(cmd, output, cwd);
|
|
9427
9504
|
const { body: untrusted, notices } = splitOwnTrailingNotices(compressed.text);
|
|
9428
9505
|
const marker = compressed.withMarker(minNet).slice(compressed.text.length);
|
|
9429
9506
|
const body = fenceUntrusted(untrusted, UNTRUSTED_TOOL_TAG) + notices + marker + "\n[token-goat] full output: bash-output " + id + " --full";
|
|
@@ -9502,6 +9579,21 @@ function preBashHandlerInner(event) {
|
|
|
9502
9579
|
const cdStripped = cmd !== rawCmd;
|
|
9503
9580
|
const preHookCwd = getCwd(event) ?? null;
|
|
9504
9581
|
const hintCwd = preHookCwd ?? process.cwd();
|
|
9582
|
+
const pathHint = (paths, text) => contextOutput(text, typeof paths === "string" ? [paths] : paths);
|
|
9583
|
+
const pricedSubstitute = (hintPath, start, end) => {
|
|
9584
|
+
const sub = rangeSubstituteFor(hintPath, hintCwd, [[start, end]]);
|
|
9585
|
+
return sub !== null && meetsSavingsFloor(sub.requestedBytes - sub.replacementBytes) ? sub : null;
|
|
9586
|
+
};
|
|
9587
|
+
const declineUnpriced = (paths) => {
|
|
9588
|
+
for (const p of typeof paths === "string" ? [paths] : paths) logSuppressedDetection("bash_redirect", event.sessionId, p);
|
|
9589
|
+
return passOutput();
|
|
9590
|
+
};
|
|
9591
|
+
const recallWorthShowing = (outputId2, entry2) => {
|
|
9592
|
+
if (entry2.sizeBytes < loadConfig().hints.bash_dedup_min_bytes) return false;
|
|
9593
|
+
if (meetsSavingsFloor(entry2.sizeBytes)) return true;
|
|
9594
|
+
logSuppressedDetection("bash_recall", event.sessionId, outputId2);
|
|
9595
|
+
return false;
|
|
9596
|
+
};
|
|
9505
9597
|
const cfg = loadConfig();
|
|
9506
9598
|
if (cfg.hints.warn_unbalanced_shell_quoting) {
|
|
9507
9599
|
const quoteError = detectUnbalancedShellSyntax(cmd);
|
|
@@ -9562,17 +9654,19 @@ function preBashHandlerInner(event) {
|
|
|
9562
9654
|
const { filePath, toolOrScript } = terminalXml;
|
|
9563
9655
|
recordStat("session_hint", 0, 0);
|
|
9564
9656
|
const target = filePath ? displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath) : "<file>";
|
|
9565
|
-
return
|
|
9657
|
+
return pathHint(
|
|
9658
|
+
filePath ? [target] : [],
|
|
9566
9659
|
`token-goat available for this file type, consider 'token-goat xml-query "${target}" "<xpath>"' or 'token-goat xml-outline "${target}"' first instead of terminal XML parsing (${toolOrScript}).`
|
|
9567
9660
|
);
|
|
9568
9661
|
}
|
|
9569
9662
|
const singleLineRangeRead = extractLineRangeRead(cmd);
|
|
9570
9663
|
const sedReads = singleLineRangeRead !== null ? [singleLineRangeRead] : extractLineRangeReadsCompound(cmd);
|
|
9571
9664
|
if (sedReads !== null) {
|
|
9572
|
-
recordStat("session_hint", 0, 0);
|
|
9573
9665
|
const hints = [];
|
|
9666
|
+
const hintPaths = [];
|
|
9574
9667
|
for (const { filePath, ranges, tool } of sedReads) {
|
|
9575
9668
|
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
|
|
9669
|
+
hintPaths.push(hintPath);
|
|
9576
9670
|
const sedDedupKey = resolveIndexPath(hintPath, preHookCwd ?? process.cwd());
|
|
9577
9671
|
const overlapHints = [];
|
|
9578
9672
|
const freshRanges = [];
|
|
@@ -9586,23 +9680,39 @@ function preBashHandlerInner(event) {
|
|
|
9586
9680
|
}
|
|
9587
9681
|
}
|
|
9588
9682
|
hints.push(...overlapHints);
|
|
9589
|
-
|
|
9683
|
+
const sub = freshRanges.length > 0 ? rangeSubstituteFor(hintPath, hintCwd, freshRanges) : null;
|
|
9684
|
+
if (sub !== null && meetsSavingsFloor(sub.requestedBytes - sub.replacementBytes)) {
|
|
9685
|
+
hints.push(sedRangeHint(hintPath, freshRanges, tool, sub));
|
|
9686
|
+
}
|
|
9590
9687
|
}
|
|
9591
|
-
|
|
9688
|
+
if (hints.length === 0) return declineUnpriced(hintPaths);
|
|
9689
|
+
recordStat("session_hint", 0, 0);
|
|
9690
|
+
return pathHint(hintPaths, hints.join(" "));
|
|
9592
9691
|
}
|
|
9593
9692
|
const gcTailResult = extractGetContentTail(cmd);
|
|
9594
9693
|
if (gcTailResult !== null) {
|
|
9595
9694
|
const { filePath, isDoc, isConfig, isSql, isXml } = gcTailResult;
|
|
9596
9695
|
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
|
|
9597
9696
|
recordStat("session_hint", 0, 0);
|
|
9598
|
-
return
|
|
9697
|
+
return pathHint(hintPath, "`Get-Content -Tail` bypasses read hooks. " + surgicalHintForConfigDoc(hintPath, isConfig, isDoc, isSql, isXml));
|
|
9599
9698
|
}
|
|
9600
9699
|
const gcSelectResult = extractGetContentSelectFirst(cmd);
|
|
9601
9700
|
if (gcSelectResult !== null) {
|
|
9602
|
-
const { filePath,
|
|
9701
|
+
const { filePath, n } = gcSelectResult;
|
|
9603
9702
|
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
|
|
9703
|
+
const gcSelectHint = leadingLinesHint("`Select-Object -First` bypasses read hooks. ", hintPath, 1, n, preHookCwd, pricedSubstitute(hintPath, 1, n));
|
|
9704
|
+
if (gcSelectHint === null) return declineUnpriced(hintPath);
|
|
9604
9705
|
recordStat("session_hint", 0, 0);
|
|
9605
|
-
return
|
|
9706
|
+
return pathHint(hintPath, gcSelectHint);
|
|
9707
|
+
}
|
|
9708
|
+
const gcHeadResult = extractGetContentHead(cmd);
|
|
9709
|
+
if (gcHeadResult !== null) {
|
|
9710
|
+
const { filePath, n } = gcHeadResult;
|
|
9711
|
+
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
|
|
9712
|
+
const gcHeadHint = leadingLinesHint("`Get-Content -TotalCount` bypasses read hooks. ", hintPath, 1, n, preHookCwd, pricedSubstitute(hintPath, 1, n));
|
|
9713
|
+
if (gcHeadHint === null) return declineUnpriced(hintPath);
|
|
9714
|
+
recordStat("session_hint", 0, 0);
|
|
9715
|
+
return pathHint(hintPath, gcHeadHint);
|
|
9606
9716
|
}
|
|
9607
9717
|
const catJsonPipe = extractCatJsonPipe(cmd);
|
|
9608
9718
|
if (catJsonPipe !== null) {
|
|
@@ -9610,7 +9720,8 @@ function preBashHandlerInner(event) {
|
|
|
9610
9720
|
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
|
|
9611
9721
|
recordStat("session_hint", 0, 0);
|
|
9612
9722
|
const label = isDirectJq ? "`jq`" : "`cat | jq`";
|
|
9613
|
-
return
|
|
9723
|
+
return pathHint(
|
|
9724
|
+
hintPath,
|
|
9614
9725
|
label + ' loads the whole file. Use `token-goat json-query "' + hintPath + '" "<key>"` or `token-goat config-get "' + hintPath + '" KEY_NAME` to slice one value.'
|
|
9615
9726
|
);
|
|
9616
9727
|
}
|
|
@@ -9619,7 +9730,8 @@ function preBashHandlerInner(event) {
|
|
|
9619
9730
|
recordStat("session_hint", 0, 0);
|
|
9620
9731
|
if (psJsonPipe.filePath) {
|
|
9621
9732
|
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, psJsonPipe.filePath, hintCwd) : psJsonPipe.filePath);
|
|
9622
|
-
return
|
|
9733
|
+
return pathHint(
|
|
9734
|
+
hintPath,
|
|
9623
9735
|
'PowerShell `ConvertFrom-Json` pipeline detected. Use `token-goat json-query "' + hintPath + '" "<selector>"` to extract fields directly without shell conversion scripts.'
|
|
9624
9736
|
);
|
|
9625
9737
|
}
|
|
@@ -9633,12 +9745,13 @@ function preBashHandlerInner(event) {
|
|
|
9633
9745
|
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
|
|
9634
9746
|
recordStat("session_hint", 0, 0);
|
|
9635
9747
|
if (isSql) {
|
|
9636
|
-
return
|
|
9748
|
+
return pathHint(
|
|
9749
|
+
hintPath,
|
|
9637
9750
|
"`" + cmd0 + '` loads the entire file into context. Use `token-goat section "' + hintPath + '::table_name"` to pull one CREATE TABLE / CREATE TYPE block.'
|
|
9638
9751
|
);
|
|
9639
9752
|
}
|
|
9640
|
-
const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc, isXml);
|
|
9641
|
-
return cdStripped || advisoryOnly ?
|
|
9753
|
+
const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc, isXml, runnableTargetFor(hintPath, hintCwd, event));
|
|
9754
|
+
return cdStripped || advisoryOnly ? pathHint(hintPath, "`" + cmd0 + "` loads the entire file into context. " + hint) : denyOutput("`" + cmd0 + "` loads the entire file into context. " + hint);
|
|
9642
9755
|
}
|
|
9643
9756
|
const catMulti = extractCatFilesMulti(cmd);
|
|
9644
9757
|
if (catMulti !== null) {
|
|
@@ -9650,7 +9763,7 @@ function preBashHandlerInner(event) {
|
|
|
9650
9763
|
return " " + hintPath + " -> `" + how + "`";
|
|
9651
9764
|
});
|
|
9652
9765
|
const msg = "`" + cmd0 + "` on multiple files loads them all into context. Read each surgically instead:\n" + perPath.join("\n");
|
|
9653
|
-
return cdStripped ?
|
|
9766
|
+
return cdStripped ? pathHint(catMulti.map((m) => displaySafePath(resolveCdHintPath(rawCmd, m.filePath, hintCwd))), msg) : denyOutput(msg);
|
|
9654
9767
|
}
|
|
9655
9768
|
const psGetContentResult = extractPowerShellWrappedGetContent(cmd, event);
|
|
9656
9769
|
if (psGetContentResult !== null) {
|
|
@@ -9659,10 +9772,10 @@ function preBashHandlerInner(event) {
|
|
|
9659
9772
|
recordStat("session_hint", 0, 0);
|
|
9660
9773
|
const lead = "`Get-Content` via a `powershell -Command` wrapper bypasses read hooks and loads the entire file into context. ";
|
|
9661
9774
|
if (isSql) {
|
|
9662
|
-
return
|
|
9775
|
+
return pathHint(hintPath, lead + 'Use `token-goat section "' + hintPath + '::table_name"` to pull one CREATE TABLE / CREATE TYPE block.');
|
|
9663
9776
|
}
|
|
9664
|
-
const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc, isXml);
|
|
9665
|
-
return cdStripped ?
|
|
9777
|
+
const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc, isXml, runnableTargetFor(hintPath, hintCwd, event));
|
|
9778
|
+
return cdStripped ? pathHint(hintPath, lead + hint) : denyOutput(lead + hint);
|
|
9666
9779
|
}
|
|
9667
9780
|
const wslCatResult = extractWslCatFile(cmd);
|
|
9668
9781
|
if (wslCatResult !== null) {
|
|
@@ -9670,12 +9783,13 @@ function preBashHandlerInner(event) {
|
|
|
9670
9783
|
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
|
|
9671
9784
|
recordStat("session_hint", 0, 0);
|
|
9672
9785
|
if (isSql) {
|
|
9673
|
-
return
|
|
9786
|
+
return pathHint(
|
|
9787
|
+
hintPath,
|
|
9674
9788
|
'`cat` loads the entire file into context. Use `token-goat section "' + hintPath + '::table_name"` to pull one CREATE TABLE / CREATE TYPE block.'
|
|
9675
9789
|
);
|
|
9676
9790
|
}
|
|
9677
|
-
const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc, isXml);
|
|
9678
|
-
return cdStripped ?
|
|
9791
|
+
const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc, isXml, runnableTargetFor(hintPath, hintCwd, event));
|
|
9792
|
+
return cdStripped ? pathHint(hintPath, "`cat` loads the entire file into context. " + hint) : denyOutput("`cat` loads the entire file into context. " + hint);
|
|
9679
9793
|
}
|
|
9680
9794
|
const pyRead = extractPythonFileRead(cmd);
|
|
9681
9795
|
if (pyRead !== null) {
|
|
@@ -9685,33 +9799,37 @@ function preBashHandlerInner(event) {
|
|
|
9685
9799
|
if (isOutputFile) {
|
|
9686
9800
|
if (taskOutputIsJsonlTranscript(filePath)) {
|
|
9687
9801
|
const tHint = 'This `.output` file is a JSONL agent transcript. Use `token-goat bash-output --file "' + hintPath + '" --transcript` to read the assistant text, then narrow with `--grep PATTERN` or `--tail N`, instead of hand-parsing the JSONL.';
|
|
9688
|
-
return cdStripped ?
|
|
9802
|
+
return cdStripped ? pathHint(hintPath, tHint) : denyOutput(tHint);
|
|
9689
9803
|
}
|
|
9690
|
-
return
|
|
9804
|
+
return pathHint(
|
|
9805
|
+
hintPath,
|
|
9691
9806
|
"This `.output` file is a background command's stdout. Use `token-goat bash-output --file \"" + hintPath + '"` to narrow it with `--grep PATTERN`, `--tail N` or `--head N`, instead of reading the whole file.'
|
|
9692
9807
|
);
|
|
9693
9808
|
}
|
|
9694
9809
|
if (isSql) {
|
|
9695
|
-
return
|
|
9810
|
+
return pathHint(
|
|
9811
|
+
hintPath,
|
|
9696
9812
|
'Python `open()` file reads bypass read hooks. Use `token-goat section "' + hintPath + '::table_name"` to pull one CREATE TABLE / CREATE TYPE block.'
|
|
9697
9813
|
);
|
|
9698
9814
|
}
|
|
9699
|
-
const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc, isXml);
|
|
9700
|
-
return cdStripped ?
|
|
9815
|
+
const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc, isXml, runnableTargetFor(hintPath, hintCwd, event));
|
|
9816
|
+
return cdStripped ? pathHint(hintPath, "Python `open()` file reads bypass read hooks. " + hint) : denyOutput("Python `open()` file reads bypass read hooks. " + hint);
|
|
9701
9817
|
}
|
|
9702
9818
|
const tailResult = extractTailFile(cmd);
|
|
9703
9819
|
if (tailResult !== null) {
|
|
9704
9820
|
const { filePath, isDoc, isConfig, isSql, isXml } = tailResult;
|
|
9705
9821
|
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
|
|
9706
9822
|
recordStat("session_hint", 0, 0);
|
|
9707
|
-
return
|
|
9823
|
+
return pathHint(hintPath, "`tail` bypasses read hooks. " + surgicalHintForConfigDoc(hintPath, isConfig, isDoc, isSql, isXml));
|
|
9708
9824
|
}
|
|
9709
9825
|
const headResult = extractHeadFile(cmd);
|
|
9710
9826
|
if (headResult !== null) {
|
|
9711
|
-
const { filePath,
|
|
9827
|
+
const { filePath, n } = headResult;
|
|
9712
9828
|
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
|
|
9829
|
+
const headHint = leadingLinesHint("`head` bypasses read hooks. ", hintPath, 1, n, preHookCwd, pricedSubstitute(hintPath, 1, n));
|
|
9830
|
+
if (headHint === null) return declineUnpriced(hintPath);
|
|
9713
9831
|
recordStat("session_hint", 0, 0);
|
|
9714
|
-
return
|
|
9832
|
+
return pathHint(hintPath, headHint);
|
|
9715
9833
|
}
|
|
9716
9834
|
const nodeRead = extractNodeFileRead(cmd);
|
|
9717
9835
|
if (nodeRead !== null) {
|
|
@@ -9720,11 +9838,11 @@ function preBashHandlerInner(event) {
|
|
|
9720
9838
|
const lead = "Node.js `fs.readFileSync()` bypasses read hooks. ";
|
|
9721
9839
|
if (isSql) {
|
|
9722
9840
|
recordStat("session_hint", 0, 0);
|
|
9723
|
-
return
|
|
9841
|
+
return pathHint(hintPath, lead + 'Use `token-goat section "' + hintPath + '::table_name"` to pull one CREATE TABLE / CREATE TYPE block.');
|
|
9724
9842
|
}
|
|
9725
|
-
const hint =
|
|
9843
|
+
const hint = surgicalHintFor(hintPath, false, isConfig, isDoc, false, runnableTargetFor(hintPath, hintCwd, event));
|
|
9726
9844
|
recordStat("session_hint", 0, 0);
|
|
9727
|
-
return cdStripped ?
|
|
9845
|
+
return cdStripped ? pathHint(hintPath, lead + hint) : denyOutput(lead + hint);
|
|
9728
9846
|
}
|
|
9729
9847
|
const psMethodRead = extractPowerShellFileMethodRead(cmd, event);
|
|
9730
9848
|
if (psMethodRead !== null) {
|
|
@@ -9733,11 +9851,15 @@ function preBashHandlerInner(event) {
|
|
|
9733
9851
|
const lead = "PowerShell `[IO.File]::ReadAllText()` bypasses read hooks. ";
|
|
9734
9852
|
if (isSql) {
|
|
9735
9853
|
recordStat("session_hint", 0, 0);
|
|
9736
|
-
return
|
|
9854
|
+
return pathHint(hintPath, lead + 'Use `token-goat section "' + hintPath + '::table_name"` to pull one CREATE TABLE / CREATE TYPE block.');
|
|
9737
9855
|
}
|
|
9738
|
-
const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc, isXml);
|
|
9856
|
+
const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc, isXml, runnableTargetFor(hintPath, hintCwd, event));
|
|
9739
9857
|
recordStat("session_hint", 0, 0);
|
|
9740
|
-
return cdStripped ?
|
|
9858
|
+
return cdStripped ? pathHint(hintPath, lead + hint) : denyOutput(lead + hint);
|
|
9859
|
+
}
|
|
9860
|
+
const structuralRewrite = detectStructuralIndexRewrite(rawCmd, hintCwd);
|
|
9861
|
+
if (structuralRewrite !== null) {
|
|
9862
|
+
return { hookType: "rewriteInput", updatedInput: { ...event.toolInput, command: structuralRewrite.command } };
|
|
9741
9863
|
}
|
|
9742
9864
|
if (extractGrepPipeChain(cmd)) {
|
|
9743
9865
|
recordStat("session_hint", 0, 0);
|
|
@@ -9750,7 +9872,8 @@ function preBashHandlerInner(event) {
|
|
|
9750
9872
|
const { filePath } = mdHeadingGrep;
|
|
9751
9873
|
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
|
|
9752
9874
|
recordStat("session_hint", 0, 0);
|
|
9753
|
-
return
|
|
9875
|
+
return pathHint(
|
|
9876
|
+
hintPath,
|
|
9754
9877
|
'Use `token-goat outline "' + hintPath + '"` to get all headings with line ranges \u2014 then `token-goat section "' + hintPath + '::Heading"` to read one section.'
|
|
9755
9878
|
);
|
|
9756
9879
|
}
|
|
@@ -9759,7 +9882,8 @@ function preBashHandlerInner(event) {
|
|
|
9759
9882
|
const { identifier } = rgSymbol;
|
|
9760
9883
|
recordStat("session_hint", 0, 0);
|
|
9761
9884
|
return contextOutput(
|
|
9762
|
-
"Use `token-goat symbol " + identifier + "` to jump directly to the definition without scanning the file."
|
|
9885
|
+
"Use `token-goat symbol " + identifier + "` to jump directly to the definition without scanning the file.",
|
|
9886
|
+
[identifier]
|
|
9763
9887
|
);
|
|
9764
9888
|
}
|
|
9765
9889
|
const rgStructural = extractRgStructuralSearch(cmd);
|
|
@@ -9767,7 +9891,8 @@ function preBashHandlerInner(event) {
|
|
|
9767
9891
|
const { filePath } = rgStructural;
|
|
9768
9892
|
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
|
|
9769
9893
|
recordStat("session_hint", 0, 0);
|
|
9770
|
-
return
|
|
9894
|
+
return pathHint(
|
|
9895
|
+
hintPath,
|
|
9771
9896
|
'Searching for code definitions with `rg`/`grep` is slower than surgical reads. Use `token-goat skeleton "' + hintPath + '"` to see all symbols with line numbers, or `token-goat outline "' + hintPath + '"` for symbols with docstrings and line ranges.'
|
|
9772
9897
|
);
|
|
9773
9898
|
}
|
|
@@ -9777,7 +9902,7 @@ function preBashHandlerInner(event) {
|
|
|
9777
9902
|
const monOutputId = getBashOutputId(monCmdHash);
|
|
9778
9903
|
const monEntryRaw = monOutputId !== null ? getBashOutput(monOutputId) : null;
|
|
9779
9904
|
const monEntry = monEntryRaw !== null && !isBashEntryStale(monEntryRaw, cmd, preHookCwd) ? monEntryRaw : null;
|
|
9780
|
-
if (monOutputId !== null && monEntry !== null &&
|
|
9905
|
+
if (monOutputId !== null && monEntry !== null && recallWorthShowing(monOutputId, monEntry)) {
|
|
9781
9906
|
const monBytes = monEntry.sizeBytes;
|
|
9782
9907
|
const catFile = extractCatSourceFile(cmd);
|
|
9783
9908
|
if (catFile !== null) {
|
|
@@ -9799,7 +9924,7 @@ function preBashHandlerInner(event) {
|
|
|
9799
9924
|
const prevResolvedPath = prevPath !== null && commandPathIsTouchable(prevPath, event) ? resolveIndexPath(prevPath, preHookCwd ?? process.cwd()) : null;
|
|
9800
9925
|
if (prevPath !== null && prevResolvedPath !== null && !existsSync(prevResolvedPath)) {
|
|
9801
9926
|
clearCurlDownload(curlDl.url);
|
|
9802
|
-
} else if (prevPath !== null && prevResolvedPath !== null &&
|
|
9927
|
+
} else if (prevPath !== null && prevResolvedPath !== null && statSync9(prevResolvedPath).size >= loadConfig().hints.bash_dedup_min_bytes) {
|
|
9803
9928
|
recordStat("session_hint", 0, 0);
|
|
9804
9929
|
return denyOutput(
|
|
9805
9930
|
"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.'
|
|
@@ -9812,7 +9937,7 @@ function preBashHandlerInner(event) {
|
|
|
9812
9937
|
const curlOutputId = getBashOutputId(curlHash);
|
|
9813
9938
|
const curlEntryRaw = curlOutputId !== null ? getBashOutput(curlOutputId) : null;
|
|
9814
9939
|
const curlEntry = curlEntryRaw !== null && !isBashEntryStale(curlEntryRaw, cmd, preHookCwd) ? curlEntryRaw : null;
|
|
9815
|
-
if (curlOutputId !== null && curlEntry !== null &&
|
|
9940
|
+
if (curlOutputId !== null && curlEntry !== null && recallWorthShowing(curlOutputId, curlEntry)) {
|
|
9816
9941
|
const curlBytes = curlEntry.sizeBytes;
|
|
9817
9942
|
recordStat("bash_compress:recall", curlBytes, savedTokensFromBytes(curlBytes));
|
|
9818
9943
|
const curlPreview = cmd.length > 60 ? cmd.slice(0, 57) + "..." : cmd;
|
|
@@ -9826,7 +9951,7 @@ function preBashHandlerInner(event) {
|
|
|
9826
9951
|
const ghOutputId = getBashOutputId(ghHash);
|
|
9827
9952
|
const ghEntryRaw = ghOutputId !== null ? getBashOutput(ghOutputId) : null;
|
|
9828
9953
|
const ghEntry = ghEntryRaw !== null && !isBashEntryStale(ghEntryRaw, cmd, preHookCwd) ? ghEntryRaw : null;
|
|
9829
|
-
if (ghOutputId !== null && ghEntry !== null &&
|
|
9954
|
+
if (ghOutputId !== null && ghEntry !== null && recallWorthShowing(ghOutputId, ghEntry)) {
|
|
9830
9955
|
const ghBytes = ghEntry.sizeBytes;
|
|
9831
9956
|
recordStat("bash_compress:recall", ghBytes, savedTokensFromBytes(ghBytes));
|
|
9832
9957
|
const ghPreview = cmd.length > 60 ? cmd.slice(0, 57) + "..." : cmd;
|
|
@@ -9840,7 +9965,7 @@ function preBashHandlerInner(event) {
|
|
|
9840
9965
|
const gitScopedOutputId = getBashOutputId(gitScopedHash);
|
|
9841
9966
|
const gitScopedEntryRaw = gitScopedOutputId !== null ? getBashOutput(gitScopedOutputId) : null;
|
|
9842
9967
|
const gitScopedEntry = gitScopedEntryRaw !== null && !isBashEntryStale(gitScopedEntryRaw, cmd, preHookCwd) ? gitScopedEntryRaw : null;
|
|
9843
|
-
if (gitScopedOutputId !== null && gitScopedEntry !== null &&
|
|
9968
|
+
if (gitScopedOutputId !== null && gitScopedEntry !== null && recallWorthShowing(gitScopedOutputId, gitScopedEntry)) {
|
|
9844
9969
|
const gitScopedBytes = gitScopedEntry.sizeBytes;
|
|
9845
9970
|
recordStat("bash_compress:recall", gitScopedBytes, savedTokensFromBytes(gitScopedBytes));
|
|
9846
9971
|
const gitScopedPreview = cmd.length > 60 ? cmd.slice(0, 57) + "..." : cmd;
|
|
@@ -9861,7 +9986,7 @@ function preBashHandlerInner(event) {
|
|
|
9861
9986
|
}
|
|
9862
9987
|
if (notes.length > 0) {
|
|
9863
9988
|
recordStat("session_hint", 0, 0);
|
|
9864
|
-
return
|
|
9989
|
+
return pathHint(tgRead.filePath !== null ? [tgRead.filePath] : [], notes.join(" "));
|
|
9865
9990
|
}
|
|
9866
9991
|
return passOutput();
|
|
9867
9992
|
}
|
|
@@ -9882,7 +10007,7 @@ function preBashHandlerInner(event) {
|
|
|
9882
10007
|
const outputId = getBashOutputId(cmdHash);
|
|
9883
10008
|
const entryRaw = outputId !== null ? getBashOutput(outputId) : null;
|
|
9884
10009
|
const entry = entryRaw !== null && !isBashEntryStale(entryRaw, cmd, preHookCwd) ? entryRaw : null;
|
|
9885
|
-
if (outputId !== null && entry !== null &&
|
|
10010
|
+
if (outputId !== null && entry !== null && recallWorthShowing(outputId, entry)) {
|
|
9886
10011
|
const bytes = entry.sizeBytes;
|
|
9887
10012
|
recordStat("bash_compress:recall", bytes, savedTokensFromBytes(bytes));
|
|
9888
10013
|
return contextOutput(buildRecallHint(cmd, outputId));
|
|
@@ -9893,7 +10018,28 @@ function preBashHandler(event) {
|
|
|
9893
10018
|
return applyHintTracking(event, preBashHandlerInner(event), classifyBashHint);
|
|
9894
10019
|
}
|
|
9895
10020
|
registerHook("pre_tool_use", preBashHandler, { toolName: "Bash" });
|
|
9896
|
-
function
|
|
10021
|
+
function readPersistedBashOutput(resp, cwd, sessionId) {
|
|
10022
|
+
const persistedPath = resp["persistedOutputPath"];
|
|
10023
|
+
if (typeof persistedPath !== "string" || persistedPath === "" || sessionId === "") return null;
|
|
10024
|
+
const root = path4.join(projectTranscriptsDir(cwd ?? process.cwd()), sessionId, "tool-results");
|
|
10025
|
+
if (!isInsideRoot(persistedPath, root)) return null;
|
|
10026
|
+
try {
|
|
10027
|
+
const stat = statSync9(persistedPath);
|
|
10028
|
+
if (!stat.isFile() || stat.size > MAX_CAPTURE_BYTES) return null;
|
|
10029
|
+
const persistedSize = resp["persistedOutputSize"];
|
|
10030
|
+
if (typeof persistedSize === "number" && Math.abs(stat.size - persistedSize) > 8) return null;
|
|
10031
|
+
return readFileSync4(persistedPath, "utf-8");
|
|
10032
|
+
} catch {
|
|
10033
|
+
return null;
|
|
10034
|
+
}
|
|
10035
|
+
}
|
|
10036
|
+
function extractBashOutput(event) {
|
|
10037
|
+
const raw = event.raw;
|
|
10038
|
+
const resp = raw["tool_response"];
|
|
10039
|
+
if (resp !== null && typeof resp === "object") {
|
|
10040
|
+
const persisted = readPersistedBashOutput(resp, getCwd(event) ?? null, event.sessionId);
|
|
10041
|
+
if (persisted !== null) return persisted;
|
|
10042
|
+
}
|
|
9897
10043
|
return extractToolResponseField(raw, OUTPUT_FIRST_TOOL_RESPONSE_KEYS);
|
|
9898
10044
|
}
|
|
9899
10045
|
function extractExitCode(raw) {
|
|
@@ -9959,6 +10105,11 @@ function recordBashFileReadsForSessionCache(cmd, cwd) {
|
|
|
9959
10105
|
recordFileLineRange(resolve2(gcSelect.filePath), 1, gcSelect.n);
|
|
9960
10106
|
return;
|
|
9961
10107
|
}
|
|
10108
|
+
const gcHead = extractGetContentHead(cmd);
|
|
10109
|
+
if (gcHead !== null) {
|
|
10110
|
+
recordFileLineRange(resolve2(gcHead.filePath), 1, gcHead.n);
|
|
10111
|
+
return;
|
|
10112
|
+
}
|
|
9962
10113
|
const head = extractHeadFile(cmd);
|
|
9963
10114
|
if (head !== null) {
|
|
9964
10115
|
recordFileLineRange(resolve2(head.filePath), 1, head.n);
|
|
@@ -10021,7 +10172,7 @@ async function postBashHandler(event) {
|
|
|
10021
10172
|
const isUnwrapped = unwrapCompressCommand(rawCmdRaw) === null;
|
|
10022
10173
|
const rawCmd = unwrapCompressCommand(rawCmdRaw) ?? rawCmdRaw;
|
|
10023
10174
|
const cmd = stripCdPrefix(rawCmd);
|
|
10024
|
-
const output = extractBashOutput(event
|
|
10175
|
+
const output = extractBashOutput(event);
|
|
10025
10176
|
const exitCode = extractExitCode(event.raw);
|
|
10026
10177
|
const cwd = getCwd(event) ?? null;
|
|
10027
10178
|
const cacheMinBytes = loadConfig().bash_compress.cache_min_bytes;
|
|
@@ -10208,9 +10359,11 @@ function postBashOutputHandler(event) {
|
|
|
10208
10359
|
const delta = output.slice(prior.output.length);
|
|
10209
10360
|
storePollSnapshot(event.sessionId, bashId, output);
|
|
10210
10361
|
if (Buffer.byteLength(delta, "utf-8") < minBytes) return passOutput();
|
|
10362
|
+
const compressedDelta = looksLikePytestOutput(delta) ? pytestFilter.apply(delta, "", 0, []) : null;
|
|
10363
|
+
const deltaBody = compressedDelta !== null && compressedDelta.worthApplying(resolveMinNetSavingsBytes()) ? compressedDelta.withMarker(resolveMinNetSavingsBytes()) : delta;
|
|
10211
10364
|
const deltaNotice = `[token-goat: bash_id ${bashId} delta since last poll]
|
|
10212
10365
|
`;
|
|
10213
|
-
const fenced = fenceUntrusted(
|
|
10366
|
+
const fenced = fenceUntrusted(deltaBody, UNTRUSTED_TOOL_TAG);
|
|
10214
10367
|
const body = `${deltaNotice}${fenced}`;
|
|
10215
10368
|
if (!isRewriteWorthwhile({
|
|
10216
10369
|
originalBytes: Buffer.byteLength(output, "utf-8"),
|
|
@@ -10332,7 +10485,7 @@ registerHook("post_tool_use", postTaskOutputHandler, { toolName: "TaskOutput" })
|
|
|
10332
10485
|
|
|
10333
10486
|
// src/hooks_tool_failure.ts
|
|
10334
10487
|
init_define_import_meta_env();
|
|
10335
|
-
import { existsSync as existsSync2, readFileSync as
|
|
10488
|
+
import { existsSync as existsSync2, readFileSync as readFileSync5, statSync as statSync10, writeFileSync as writeFileSync2 } from "node:fs";
|
|
10336
10489
|
import { dirname as dirname2, relative as relative2 } from "node:path";
|
|
10337
10490
|
var FAILURE_SUFFIX = ".tool-failures.json";
|
|
10338
10491
|
var MAX_TRACKED_FAILURES = 64;
|
|
@@ -10360,7 +10513,7 @@ function ledgerPath(sessionId) {
|
|
|
10360
10513
|
}
|
|
10361
10514
|
function readLedger(target) {
|
|
10362
10515
|
try {
|
|
10363
|
-
const parsed = JSON.parse(
|
|
10516
|
+
const parsed = JSON.parse(readFileSync5(target, "utf8"));
|
|
10364
10517
|
if (parsed !== null && typeof parsed === "object") {
|
|
10365
10518
|
const seen = parsed.seen;
|
|
10366
10519
|
const order = parsed.order;
|
|
@@ -10401,9 +10554,9 @@ function diagnoseEditFailure(event, errorText) {
|
|
|
10401
10554
|
const absPath = normalizePath(filePath);
|
|
10402
10555
|
if (!existsSync2(absPath)) return null;
|
|
10403
10556
|
try {
|
|
10404
|
-
const stat =
|
|
10557
|
+
const stat = statSync10(absPath);
|
|
10405
10558
|
if (stat.size > MAX_EDIT_DIAGNOSE_BYTES) return null;
|
|
10406
|
-
const fileContent =
|
|
10559
|
+
const fileContent = readFileSync5(absPath, "utf8");
|
|
10407
10560
|
const relDisplay = displaySafeText(relative2(process.cwd(), absPath).replace(/\\/g, "/") || absPath);
|
|
10408
10561
|
if (isMultiple) {
|
|
10409
10562
|
const matchLines = [];
|
|
@@ -11051,8 +11204,8 @@ The full payload was cached to prevent harness context spill. Slicing commands:
|
|
|
11051
11204
|
}
|
|
11052
11205
|
return passOrFence();
|
|
11053
11206
|
}
|
|
11054
|
-
registerHook("pre_tool_use", preMcpHandler, { toolPattern:
|
|
11055
|
-
registerHook("post_tool_use", postMcpHandler, { toolPattern:
|
|
11207
|
+
registerHook("pre_tool_use", preMcpHandler, { toolPattern: MCP_TOOL_PATTERN });
|
|
11208
|
+
registerHook("post_tool_use", postMcpHandler, { toolPattern: MCP_TOOL_PATTERN });
|
|
11056
11209
|
|
|
11057
11210
|
// src/hooks_websearch.ts
|
|
11058
11211
|
init_define_import_meta_env();
|
|
@@ -11253,8 +11406,7 @@ registerHook("post_tool_use", postBrowserImageHandler, { toolPattern: "^mcp__" }
|
|
|
11253
11406
|
init_define_import_meta_env();
|
|
11254
11407
|
import { createHash as createHash2 } from "node:crypto";
|
|
11255
11408
|
import * as fs2 from "node:fs";
|
|
11256
|
-
import * as
|
|
11257
|
-
import * as path4 from "node:path";
|
|
11409
|
+
import * as path5 from "node:path";
|
|
11258
11410
|
var BRIEFING_TARGET_TOKENS = 550;
|
|
11259
11411
|
function buildSubagentBriefing() {
|
|
11260
11412
|
try {
|
|
@@ -11479,8 +11631,8 @@ function parseAgentDefinition(text, fallbackName) {
|
|
|
11479
11631
|
return { name: name2, restricted: false };
|
|
11480
11632
|
}
|
|
11481
11633
|
function findRestrictedAgentNames(roots) {
|
|
11482
|
-
const projectAgentsRoot =
|
|
11483
|
-
const scanRoots = roots ?? [
|
|
11634
|
+
const projectAgentsRoot = path5.join(process.cwd(), ".claude", "agents");
|
|
11635
|
+
const scanRoots = roots ?? [path5.join(claudeConfigDir(), "agents"), projectAgentsRoot];
|
|
11484
11636
|
const names = /* @__PURE__ */ new Set();
|
|
11485
11637
|
const visited = /* @__PURE__ */ new Set();
|
|
11486
11638
|
let filesSeen = 0;
|
|
@@ -11504,7 +11656,7 @@ function findRestrictedAgentNames(roots) {
|
|
|
11504
11656
|
}
|
|
11505
11657
|
for (const entry of entries) {
|
|
11506
11658
|
if (filesSeen >= ROSTER_WALK_MAX_FILES) return;
|
|
11507
|
-
const full =
|
|
11659
|
+
const full = path5.join(dir, entry.name);
|
|
11508
11660
|
let stat;
|
|
11509
11661
|
try {
|
|
11510
11662
|
stat = fs2.statSync(full);
|
|
@@ -11523,12 +11675,12 @@ function findRestrictedAgentNames(roots) {
|
|
|
11523
11675
|
} catch {
|
|
11524
11676
|
continue;
|
|
11525
11677
|
}
|
|
11526
|
-
const parsed = parseAgentDefinition(text,
|
|
11678
|
+
const parsed = parseAgentDefinition(text, path5.basename(entry.name, path5.extname(entry.name)));
|
|
11527
11679
|
if (parsed !== null && parsed.restricted) names.add(parsed.name);
|
|
11528
11680
|
}
|
|
11529
11681
|
};
|
|
11530
11682
|
for (const root of scanRoots) {
|
|
11531
|
-
const isProjectRoot = roots === void 0 &&
|
|
11683
|
+
const isProjectRoot = roots === void 0 && path5.resolve(root) === path5.resolve(projectAgentsRoot);
|
|
11532
11684
|
if (!isProjectRoot) {
|
|
11533
11685
|
walk(root, 0, null);
|
|
11534
11686
|
continue;
|
|
@@ -11658,11 +11810,11 @@ function safeSuggestions(output) {
|
|
|
11658
11810
|
if (output.hookType === "context") return { hookType: "context", context: stripUnsafeSuggestions(output.context) };
|
|
11659
11811
|
return output;
|
|
11660
11812
|
}
|
|
11661
|
-
async function relayInProcess(eventName, rawPayload) {
|
|
11813
|
+
async function relayInProcess(eventName, rawPayload, harnessWaitMs) {
|
|
11814
|
+
if (!isHookEventName(eventName)) {
|
|
11815
|
+
return "{}";
|
|
11816
|
+
}
|
|
11662
11817
|
try {
|
|
11663
|
-
if (!isHookEventName(eventName)) {
|
|
11664
|
-
return "{}";
|
|
11665
|
-
}
|
|
11666
11818
|
const harness = detectHarness();
|
|
11667
11819
|
const payload = eventName === "pre_tool_use" || eventName === "post_tool_use" ? normalizePayload(rawPayload, harnessForNormalization()) : rawPayload;
|
|
11668
11820
|
const event = buildEvent(eventName, payload);
|
|
@@ -11686,6 +11838,9 @@ async function relayInProcess(eventName, rawPayload) {
|
|
|
11686
11838
|
} catch {
|
|
11687
11839
|
}
|
|
11688
11840
|
const output = safeSuggestions(await runHook(event));
|
|
11841
|
+
if (event.sessionId) {
|
|
11842
|
+
commitPendingContext(stateKey, output.hookType === "context" ? output.context : null);
|
|
11843
|
+
}
|
|
11689
11844
|
try {
|
|
11690
11845
|
saveSessionState(stateKey);
|
|
11691
11846
|
} catch {
|
|
@@ -11693,6 +11848,8 @@ async function relayInProcess(eventName, rawPayload) {
|
|
|
11693
11848
|
return serializeOutput(output, event.eventName, harness, event);
|
|
11694
11849
|
} catch {
|
|
11695
11850
|
return "{}";
|
|
11851
|
+
} finally {
|
|
11852
|
+
recordStat(`hook:${eventName}`, 0, 0, void 0, void 0, void 0, harnessWaitMs ?? performance.now());
|
|
11696
11853
|
}
|
|
11697
11854
|
}
|
|
11698
11855
|
async function relay(eventName) {
|