token-goat 2.9.17 → 2.9.19
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 +2 -0
- package/dist/{token-goat-chunk-HRUGS6CO.mjs → token-goat-chunk-2GTB5RBZ.mjs} +365 -26
- package/dist/{token-goat-chunk-W2GUUQAQ.mjs → token-goat-chunk-3W5P673V.mjs} +1 -1
- package/dist/{token-goat-chunk-5WB7GDH4.mjs → token-goat-chunk-62VJ47WN.mjs} +6 -6
- package/dist/{token-goat-chunk-C26ZBGBC.mjs → token-goat-chunk-7DGTVBWP.mjs} +58 -44
- package/dist/{token-goat-chunk-GPCCYQSS.mjs → token-goat-chunk-AVRD7SBO.mjs} +60 -8
- package/dist/{token-goat-chunk-66224KCZ.mjs → token-goat-chunk-BTKDZW7F.mjs} +1 -1
- package/dist/{token-goat-chunk-LMFO66YD.mjs → token-goat-chunk-DA43OE7L.mjs} +1 -1
- package/dist/{token-goat-chunk-YFGEUIEO.mjs → token-goat-chunk-DA6TIDLI.mjs} +21 -16
- package/dist/{token-goat-chunk-CPHVXKNU.mjs → token-goat-chunk-DHIXE6GV.mjs} +2 -2
- package/dist/{token-goat-chunk-73GXYYZR.mjs → token-goat-chunk-EP7TCWHM.mjs} +304 -46
- package/dist/{token-goat-chunk-EKGVCAES.mjs → token-goat-chunk-EXLLLIC3.mjs} +4 -4
- package/dist/{token-goat-chunk-CICJFVIH.mjs → token-goat-chunk-F24NE3FO.mjs} +3 -3
- package/dist/{token-goat-chunk-THBLW56G.mjs → token-goat-chunk-FJZ5MZK7.mjs} +3 -3
- package/dist/{token-goat-chunk-PKNOHVWP.mjs → token-goat-chunk-FQYMZASI.mjs} +632 -350
- package/dist/{token-goat-chunk-RFFUCQK7.mjs → token-goat-chunk-FX5DHKGF.mjs} +5 -5
- package/dist/{token-goat-chunk-RB24DBBS.mjs → token-goat-chunk-GYC34EGE.mjs} +7 -8
- package/dist/{token-goat-chunk-2M33Q3O4.mjs → token-goat-chunk-HPMMLV37.mjs} +369 -120
- package/dist/{token-goat-chunk-BK3RPPTK.mjs → token-goat-chunk-IBTFGZBX.mjs} +19 -14
- package/dist/{token-goat-chunk-MWSVUKVV.mjs → token-goat-chunk-JNIFSVWM.mjs} +36 -10
- package/dist/{token-goat-chunk-KZPO4DWC.mjs → token-goat-chunk-KWM6KBPA.mjs} +2 -2
- package/dist/{token-goat-chunk-G75WIVFM.mjs → token-goat-chunk-L2UOJBV4.mjs} +21 -20
- package/dist/{token-goat-chunk-2XA5ZW4T.mjs → token-goat-chunk-LACH3WKS.mjs} +4 -4
- package/dist/token-goat-chunk-MU6I4IDI.mjs +337 -0
- package/dist/{token-goat-chunk-SWRKVA3J.mjs → token-goat-chunk-NJKNAC37.mjs} +1 -1
- package/dist/{token-goat-chunk-ERTXEKB6.mjs → token-goat-chunk-NMTKNYGF.mjs} +2 -0
- package/dist/{token-goat-chunk-CKIJWTRQ.mjs → token-goat-chunk-NYFYIRG3.mjs} +12 -12
- package/dist/{token-goat-chunk-4EXFN2AW.mjs → token-goat-chunk-OZRSREKR.mjs} +2 -2
- package/dist/token-goat-chunk-P46RTOAQ.mjs +27 -0
- package/dist/{token-goat-chunk-IDHWKNKZ.mjs → token-goat-chunk-PIAESKVU.mjs} +139 -23
- package/dist/{token-goat-chunk-26YUO7MP.mjs → token-goat-chunk-S7RFIX4Y.mjs} +180 -50
- package/dist/{token-goat-chunk-2VASS5VR.mjs → token-goat-chunk-SOKAPOOC.mjs} +23 -4
- package/dist/token-goat-chunk-VJQBYSXG.mjs +20 -0
- package/dist/{token-goat-chunk-7VQMOBPQ.mjs → token-goat-chunk-WKO5TVNA.mjs} +948 -618
- package/dist/{token-goat-chunk-UBZCON6N.mjs → token-goat-chunk-ZGQXRYKE.mjs} +29 -5
- package/dist/token-goat-hook.mjs +12 -12
- package/dist/token-goat.core.mjs +21 -20
- package/docs/cli.md +4 -3
- package/docs/security.md +1 -1
- package/package.json +1 -1
- package/dist/token-goat-chunk-7EGKXBWT.mjs +0 -173
- package/dist/token-goat-chunk-GLOM5W5C.mjs +0 -93
|
@@ -1,23 +1,27 @@
|
|
|
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-MU6I4IDI.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,
|
|
14
17
|
flattenCommandNames,
|
|
15
18
|
getWebOutput,
|
|
16
19
|
isReconcileClean,
|
|
20
|
+
projectTranscriptsDir,
|
|
17
21
|
readStdinJson,
|
|
18
22
|
reconcileProject,
|
|
19
23
|
storeWebOutput
|
|
20
|
-
} from "./token-goat-chunk-
|
|
24
|
+
} from "./token-goat-chunk-2GTB5RBZ.mjs";
|
|
21
25
|
import {
|
|
22
26
|
BODY_FIRST_TOOL_RESPONSE_KEYS,
|
|
23
27
|
GENERIC_SERVED_OUTPUT_KEY,
|
|
@@ -32,6 +36,7 @@ import {
|
|
|
32
36
|
buildProjectMap,
|
|
33
37
|
classifyBashHint,
|
|
34
38
|
classifyEditHint,
|
|
39
|
+
claudeConfigDir,
|
|
35
40
|
clearCurlDownload,
|
|
36
41
|
compactPathFor,
|
|
37
42
|
computeAdaptiveBudget,
|
|
@@ -41,6 +46,7 @@ import {
|
|
|
41
46
|
emitRewriteIfChanged,
|
|
42
47
|
enqueueDirtyPathSafe,
|
|
43
48
|
ensureWorkerAlive,
|
|
49
|
+
escapeHintName,
|
|
44
50
|
estimateTokens,
|
|
45
51
|
extractCompactFromMarker,
|
|
46
52
|
extractToolResponseField,
|
|
@@ -84,6 +90,7 @@ import {
|
|
|
84
90
|
listSiblingSessionStates,
|
|
85
91
|
loadSessionCache,
|
|
86
92
|
loadSessionState,
|
|
93
|
+
logSuppressedDetection,
|
|
87
94
|
makeDedupHintHandlers,
|
|
88
95
|
markCompactStale,
|
|
89
96
|
markCompacted,
|
|
@@ -122,6 +129,7 @@ import {
|
|
|
122
129
|
registerHook,
|
|
123
130
|
removeOutstandingAgentSpawn,
|
|
124
131
|
repeatedSkillBodyHint,
|
|
132
|
+
resolveLineRegions,
|
|
125
133
|
runHook,
|
|
126
134
|
saveSessionState,
|
|
127
135
|
scanAndRecord,
|
|
@@ -144,9 +152,10 @@ import {
|
|
|
144
152
|
wasFileReadThisSession,
|
|
145
153
|
wasHintShown,
|
|
146
154
|
withPreservedBlocks
|
|
147
|
-
} from "./token-goat-chunk-
|
|
155
|
+
} from "./token-goat-chunk-FQYMZASI.mjs";
|
|
148
156
|
import {
|
|
149
157
|
BASH_OUTPUT_SUBDIR,
|
|
158
|
+
bashOutputIdSync,
|
|
150
159
|
commandHash,
|
|
151
160
|
compressOutput,
|
|
152
161
|
dedupeConsecutive,
|
|
@@ -162,41 +171,47 @@ import {
|
|
|
162
171
|
isRewriteWorthwhile,
|
|
163
172
|
isScopedGitStatusOrDiffStatCommand,
|
|
164
173
|
isTestRunnerCommand,
|
|
174
|
+
looksLikePytestOutput,
|
|
175
|
+
pathStem,
|
|
176
|
+
pytestFilter,
|
|
165
177
|
resolveMinNetSavingsBytes,
|
|
166
178
|
shlexSplit,
|
|
167
179
|
splitOwnTrailingNotices,
|
|
168
180
|
storeBashOutput,
|
|
169
181
|
summarizeOutputDelta
|
|
170
|
-
} from "./token-goat-chunk-
|
|
182
|
+
} from "./token-goat-chunk-7DGTVBWP.mjs";
|
|
171
183
|
import {
|
|
172
184
|
recordStat,
|
|
173
185
|
redactSecrets,
|
|
174
186
|
savedTokensFromBytes,
|
|
175
187
|
storeBlob,
|
|
176
188
|
stripAnsiEscapes
|
|
177
|
-
} from "./token-goat-chunk-
|
|
189
|
+
} from "./token-goat-chunk-S7RFIX4Y.mjs";
|
|
178
190
|
import {
|
|
179
191
|
extractMarkdownHeadings,
|
|
180
192
|
formatHeadingTreeParts
|
|
181
|
-
} from "./token-goat-chunk-
|
|
193
|
+
} from "./token-goat-chunk-EP7TCWHM.mjs";
|
|
182
194
|
import {
|
|
183
|
-
countSymbols
|
|
184
|
-
|
|
195
|
+
countSymbols,
|
|
196
|
+
getFileEntry,
|
|
197
|
+
querySymbols
|
|
198
|
+
} from "./token-goat-chunk-IBTFGZBX.mjs";
|
|
185
199
|
import {
|
|
186
200
|
detectLanguage,
|
|
187
201
|
getDb,
|
|
188
202
|
languageHasFlag
|
|
189
|
-
} from "./token-goat-chunk-
|
|
203
|
+
} from "./token-goat-chunk-JNIFSVWM.mjs";
|
|
190
204
|
import {
|
|
191
205
|
detectHarness,
|
|
192
206
|
envBool,
|
|
193
207
|
envInt,
|
|
194
208
|
findProject,
|
|
209
|
+
fingerprintFile,
|
|
195
210
|
getHarnessName,
|
|
196
211
|
isUnderSystemTemp,
|
|
197
212
|
loadConfig,
|
|
198
213
|
shortFingerprint
|
|
199
|
-
} from "./token-goat-chunk-
|
|
214
|
+
} from "./token-goat-chunk-SOKAPOOC.mjs";
|
|
200
215
|
import {
|
|
201
216
|
ENV_KEYS,
|
|
202
217
|
IDENTICAL_READ_MIN_BODY_BYTES,
|
|
@@ -216,8 +231,9 @@ import {
|
|
|
216
231
|
isInsideRoot,
|
|
217
232
|
runGit,
|
|
218
233
|
toKB
|
|
219
|
-
} from "./token-goat-chunk-
|
|
234
|
+
} from "./token-goat-chunk-ZGQXRYKE.mjs";
|
|
220
235
|
import {
|
|
236
|
+
TOOL_RESULTS_ID_CHARS,
|
|
221
237
|
UNTRUSTED_TOOL_TAG,
|
|
222
238
|
displaySafePath,
|
|
223
239
|
displaySafeText,
|
|
@@ -226,7 +242,7 @@ import {
|
|
|
226
242
|
normalizePath,
|
|
227
243
|
resolveIndexPath,
|
|
228
244
|
toDisplayPath
|
|
229
|
-
} from "./token-goat-chunk-
|
|
245
|
+
} from "./token-goat-chunk-NMTKNYGF.mjs";
|
|
230
246
|
import {
|
|
231
247
|
init_define_import_meta_env
|
|
232
248
|
} from "./token-goat-chunk-A37V4PBF.mjs";
|
|
@@ -404,7 +420,7 @@ function grepIntInput(toolInput, key) {
|
|
|
404
420
|
function grepSignature(toolInput) {
|
|
405
421
|
const pattern = toolInput["pattern"];
|
|
406
422
|
if (typeof pattern !== "string" || pattern === "") return null;
|
|
407
|
-
const
|
|
423
|
+
const path6 = typeof toolInput["path"] === "string" ? toolInput["path"] : "";
|
|
408
424
|
const outputMode = typeof toolInput["output_mode"] === "string" ? toolInput["output_mode"] : "files_with_matches";
|
|
409
425
|
const glob = typeof toolInput["glob"] === "string" ? toolInput["glob"] : "";
|
|
410
426
|
const type = typeof toolInput["type"] === "string" ? toolInput["type"] : "";
|
|
@@ -419,7 +435,7 @@ function grepSignature(toolInput) {
|
|
|
419
435
|
const offset = grepIntInput(toolInput, "offset");
|
|
420
436
|
return JSON.stringify([
|
|
421
437
|
pattern,
|
|
422
|
-
|
|
438
|
+
path6,
|
|
423
439
|
outputMode,
|
|
424
440
|
glob,
|
|
425
441
|
type,
|
|
@@ -563,8 +579,8 @@ function isBroadCatchAllGlob(pattern, pathArg) {
|
|
|
563
579
|
function globSignature(toolInput) {
|
|
564
580
|
const pattern = toolInput["pattern"];
|
|
565
581
|
if (typeof pattern !== "string" || pattern === "") return null;
|
|
566
|
-
const
|
|
567
|
-
return JSON.stringify([pattern,
|
|
582
|
+
const path6 = typeof toolInput["path"] === "string" ? toolInput["path"] : "";
|
|
583
|
+
return JSON.stringify([pattern, path6]);
|
|
568
584
|
}
|
|
569
585
|
var { post: postGlobHandler, pre: preGlobDedupHandler } = makeDedupHintHandlers({
|
|
570
586
|
toolName: "Glob",
|
|
@@ -718,6 +734,7 @@ registerHook("pre_tool_use", preWriteRewriteHandler, { toolName: "Write" });
|
|
|
718
734
|
// src/hooks_compact.ts
|
|
719
735
|
init_define_import_meta_env();
|
|
720
736
|
import { spawnSync } from "node:child_process";
|
|
737
|
+
import * as path3 from "node:path";
|
|
721
738
|
var MEM_EPOCH_TIMEOUT_MS = 800;
|
|
722
739
|
var MAX_ROWS = 40;
|
|
723
740
|
function appendCappedSection(lines, header, rows, cap) {
|
|
@@ -920,8 +937,14 @@ function postCompactHandler(event) {
|
|
|
920
937
|
const summary = typeof raw === "string" ? raw : "";
|
|
921
938
|
const bytes = Buffer.byteLength(summary, "utf-8");
|
|
922
939
|
const sample = manifestPathSample(event.sessionId);
|
|
923
|
-
const
|
|
924
|
-
const
|
|
940
|
+
const cwd = normalizePath(getCwd(event) ?? process.cwd());
|
|
941
|
+
const spell = (p) => foldPath(p.replaceAll("\\", "/"));
|
|
942
|
+
const haystack = spell(summary);
|
|
943
|
+
const survived = sample.filter((p) => {
|
|
944
|
+
const rel = path3.relative(cwd, p);
|
|
945
|
+
const forms = rel !== "" && !rel.startsWith("..") && !path3.isAbsolute(rel) ? [p, rel] : [p];
|
|
946
|
+
return forms.some((f) => haystack.includes(spell(f)));
|
|
947
|
+
}).length;
|
|
925
948
|
const trigger = typeof event.raw["trigger"] === "string" ? event.raw["trigger"] : "unknown";
|
|
926
949
|
const budget = loadConfig().compact_assist.summary_budget_chars;
|
|
927
950
|
const over = budget > 0 && summary.length > budget ? 1 : 0;
|
|
@@ -946,7 +969,7 @@ init_define_import_meta_env();
|
|
|
946
969
|
var TRACKED_SKILL = "token-goat";
|
|
947
970
|
var MAX_COMMANDS_SHOWN = 8;
|
|
948
971
|
async function currentCommandNames() {
|
|
949
|
-
const { buildProgram } = await import("./token-goat-chunk-
|
|
972
|
+
const { buildProgram } = await import("./token-goat-chunk-L2UOJBV4.mjs");
|
|
950
973
|
return flattenCommandNames(buildCommandManifest(buildProgram()));
|
|
951
974
|
}
|
|
952
975
|
async function recordSkillVersionSnapshot(sessionId, skillName) {
|
|
@@ -1252,7 +1275,8 @@ function reconcileNote(cwd, indexed) {
|
|
|
1252
1275
|
if (drifted > 0) clauses.push(`${countNoun(drifted, "file")} that changed outside this session${breakdown}`);
|
|
1253
1276
|
if (upgraded > 0) clauses.push(`${countNoun(upgraded, "file")} unchanged on disk but indexed by an older version of token-goat`);
|
|
1254
1277
|
return `token-goat: reindexing ${clauses.join(" and ")}${truncated}. Symbol lookups may be briefly stale.`;
|
|
1255
|
-
} catch {
|
|
1278
|
+
} catch (e) {
|
|
1279
|
+
recordStat("reconcile_note_failed", 0, 0, void 0, extractErrorMessage(e));
|
|
1256
1280
|
return null;
|
|
1257
1281
|
}
|
|
1258
1282
|
}
|
|
@@ -1863,11 +1887,11 @@ function mapInner(r, f) {
|
|
|
1863
1887
|
function mapOuter(r, f) {
|
|
1864
1888
|
return r.matched ? f(r) : r;
|
|
1865
1889
|
}
|
|
1866
|
-
function ab(pa, pb,
|
|
1867
|
-
return (data, i) => mapOuter(pa(data, i), (ma) => mapInner(pb(data, ma.position), (vb, j) =>
|
|
1890
|
+
function ab(pa, pb, join4) {
|
|
1891
|
+
return (data, i) => mapOuter(pa(data, i), (ma) => mapInner(pb(data, ma.position), (vb, j) => join4(ma.value, vb, data, i, j)));
|
|
1868
1892
|
}
|
|
1869
|
-
function abc(pa, pb, pc,
|
|
1870
|
-
return (data, i) => mapOuter(pa(data, i), (ma) => mapOuter(pb(data, ma.position), (mb) => mapInner(pc(data, mb.position), (vc, j) =>
|
|
1893
|
+
function abc(pa, pb, pc, join4) {
|
|
1894
|
+
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))));
|
|
1871
1895
|
}
|
|
1872
1896
|
function ahead(p) {
|
|
1873
1897
|
return (data, i) => mapOuter(p(data, i), (m1) => ({
|
|
@@ -5844,8 +5868,8 @@ function trimCharacterEnd(str, char) {
|
|
|
5844
5868
|
function unicodeEscape(str) {
|
|
5845
5869
|
return str.replace(/[\s\S]/g, (c) => "\\u" + c.charCodeAt().toString(16).padStart(4, "0"));
|
|
5846
5870
|
}
|
|
5847
|
-
function get(obj,
|
|
5848
|
-
for (const key of
|
|
5871
|
+
function get(obj, path6) {
|
|
5872
|
+
for (const key of path6) {
|
|
5849
5873
|
if (!obj) {
|
|
5850
5874
|
return void 0;
|
|
5851
5875
|
}
|
|
@@ -7046,8 +7070,8 @@ function withBrackets(str, brackets) {
|
|
|
7046
7070
|
const rbr = typeof brackets[1] === "string" ? brackets[1] : "]";
|
|
7047
7071
|
return lbr + str + rbr;
|
|
7048
7072
|
}
|
|
7049
|
-
function pathRewrite(
|
|
7050
|
-
const modifiedPath = typeof rewriter === "function" ? rewriter(
|
|
7073
|
+
function pathRewrite(path6, rewriter, baseUrl, metadata, elem) {
|
|
7074
|
+
const modifiedPath = typeof rewriter === "function" ? rewriter(path6, metadata, elem) : path6;
|
|
7051
7075
|
return modifiedPath[0] === "/" && baseUrl ? trimCharacterEnd(baseUrl, "/") + modifiedPath : modifiedPath;
|
|
7052
7076
|
}
|
|
7053
7077
|
function formatImage(elem, walk, builder, formatOptions) {
|
|
@@ -7371,9 +7395,9 @@ function handleDeprecatedOptions(options) {
|
|
|
7371
7395
|
options.selectors.push(...tagDefinitions);
|
|
7372
7396
|
options.selectors = mergeDuplicatesPreferLast(options.selectors, ((s) => s.selector));
|
|
7373
7397
|
}
|
|
7374
|
-
function set(obj,
|
|
7375
|
-
const valueKey =
|
|
7376
|
-
for (const key of
|
|
7398
|
+
function set(obj, path6, value) {
|
|
7399
|
+
const valueKey = path6.pop();
|
|
7400
|
+
for (const key of path6) {
|
|
7377
7401
|
let nested = obj[key];
|
|
7378
7402
|
if (!nested) {
|
|
7379
7403
|
nested = {};
|
|
@@ -7663,20 +7687,147 @@ registerHook("post_tool_use", postSkillHandler, { toolName: "Skill" });
|
|
|
7663
7687
|
|
|
7664
7688
|
// src/hooks_bash.ts
|
|
7665
7689
|
init_define_import_meta_env();
|
|
7666
|
-
import { statSync as statSync6, existsSync } from "node:fs";
|
|
7667
7690
|
|
|
7668
|
-
// src/
|
|
7691
|
+
// src/bash_structural_index.ts
|
|
7692
|
+
init_define_import_meta_env();
|
|
7693
|
+
import { statSync as statSync4 } from "node:fs";
|
|
7694
|
+
var ALLOWED_BINARIES = /* @__PURE__ */ new Set(["rg", "grep", "egrep", "fgrep"]);
|
|
7695
|
+
var PY_DEF_RE = /^\^?(?:async\s+)?def\b(?:\s+([A-Za-z_]\w*))?$/;
|
|
7696
|
+
var PY_CLASS_RE = /^\^?class\b(?:\s+([A-Za-z_]\w*))?$/;
|
|
7697
|
+
var IMPORT_RE = /^\^import\b/;
|
|
7698
|
+
var SHELL_SAFE_UNQUOTED_RE = /^[A-Za-z0-9_.:/+,=@^-]+$/;
|
|
7699
|
+
var SHELL_UNSAFE_IN_EITHER_QUOTING_RE = /[$`"\\\r\n]/;
|
|
7700
|
+
function dualShellArg(s) {
|
|
7701
|
+
if (SHELL_SAFE_UNQUOTED_RE.test(s)) return s;
|
|
7702
|
+
if (SHELL_UNSAFE_IN_EITHER_QUOTING_RE.test(s)) return null;
|
|
7703
|
+
return `"${s}"`;
|
|
7704
|
+
}
|
|
7705
|
+
function detectStructuralIndexRewrite(rawCmd, cwd) {
|
|
7706
|
+
try {
|
|
7707
|
+
const detected = detectFromCommand(rawCmd, cwd);
|
|
7708
|
+
if (detected === null || detected.filter.name !== "grep") return null;
|
|
7709
|
+
const argv = detected.argv;
|
|
7710
|
+
if (argv.length !== 3) return null;
|
|
7711
|
+
const [bin, pattern, rawPath] = argv;
|
|
7712
|
+
if (bin === void 0 || pattern === void 0 || rawPath === void 0) return null;
|
|
7713
|
+
if (!ALLOWED_BINARIES.has(pathStem(bin).toLowerCase())) return null;
|
|
7714
|
+
if (/[*?[\]{}]/.test(rawPath)) return null;
|
|
7715
|
+
const resolved = resolveIndexPath(rawPath, cwd);
|
|
7716
|
+
let stat;
|
|
7717
|
+
try {
|
|
7718
|
+
stat = statSync4(resolved);
|
|
7719
|
+
} catch {
|
|
7720
|
+
return null;
|
|
7721
|
+
}
|
|
7722
|
+
if (!stat.isFile()) return null;
|
|
7723
|
+
const entry = getFileEntry(resolved);
|
|
7724
|
+
if (entry === null) return null;
|
|
7725
|
+
if (entry.sha !== "") {
|
|
7726
|
+
const diskSha = fingerprintFile(resolved);
|
|
7727
|
+
if (diskSha === null || diskSha !== entry.sha) return null;
|
|
7728
|
+
}
|
|
7729
|
+
const pathArg = dualShellArg(rawPath);
|
|
7730
|
+
if (pathArg === null) return null;
|
|
7731
|
+
let target = null;
|
|
7732
|
+
if (DOC_EXT_RE.test(rawPath) && STRUCTURAL_DOC_PATTERN_RE.test(pattern)) {
|
|
7733
|
+
target = { command: `outline ${pathArg}`, kind: "headings" };
|
|
7734
|
+
} else {
|
|
7735
|
+
const lang = detectLanguage(resolved);
|
|
7736
|
+
const trimmedPattern = pattern.trim();
|
|
7737
|
+
if (lang === "python" && languageHasFlag(lang, "grepSource")) {
|
|
7738
|
+
const defMatch = PY_DEF_RE.exec(trimmedPattern);
|
|
7739
|
+
const classMatch = defMatch === null ? PY_CLASS_RE.exec(trimmedPattern) : null;
|
|
7740
|
+
const nameMatch = defMatch ?? classMatch;
|
|
7741
|
+
if (nameMatch !== null) {
|
|
7742
|
+
const name2 = nameMatch[1];
|
|
7743
|
+
if (name2) {
|
|
7744
|
+
const grepArg = dualShellArg("^" + name2);
|
|
7745
|
+
target = grepArg === null ? null : { command: `outline ${pathArg} --grep ${grepArg}`, kind: `symbols named starting with ${name2} (functions, classes or any other kind sharing that name)` };
|
|
7746
|
+
} else {
|
|
7747
|
+
target = { command: `outline ${pathArg}`, kind: "all symbols in the file (not just functions/classes)" };
|
|
7748
|
+
}
|
|
7749
|
+
} else if (IMPORT_RE.test(trimmedPattern)) {
|
|
7750
|
+
target = { command: `imports ${pathArg}`, kind: "imports" };
|
|
7751
|
+
}
|
|
7752
|
+
} else if (lang !== "unknown" && languageHasFlag(lang, "grepSource") && IMPORT_RE.test(trimmedPattern)) {
|
|
7753
|
+
target = { command: `imports ${pathArg}`, kind: "imports" };
|
|
7754
|
+
}
|
|
7755
|
+
}
|
|
7756
|
+
if (target === null) return null;
|
|
7757
|
+
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]`;
|
|
7758
|
+
const noticeArg = dualShellArg(notice);
|
|
7759
|
+
if (noticeArg === null) return null;
|
|
7760
|
+
return { command: `token-goat --notice ${noticeArg} ${target.command}`, kind: target.kind };
|
|
7761
|
+
} catch {
|
|
7762
|
+
return null;
|
|
7763
|
+
}
|
|
7764
|
+
}
|
|
7765
|
+
|
|
7766
|
+
// src/bash_range_savings.ts
|
|
7669
7767
|
init_define_import_meta_env();
|
|
7670
|
-
import { statSync as statSync5 } from "node:fs";
|
|
7768
|
+
import { statSync as statSync5, readFileSync as readFileSync3 } from "node:fs";
|
|
7769
|
+
var ALL_SYMBOLS_IN_FILE = -1;
|
|
7770
|
+
var REGION_HEADER_BYTES = 56;
|
|
7771
|
+
function rangeSubstituteFor(hintPath, cwd, ranges) {
|
|
7772
|
+
if (ranges.length === 0) return null;
|
|
7773
|
+
try {
|
|
7774
|
+
const resolved = resolveIndexPath(hintPath, cwd);
|
|
7775
|
+
if (!statSync5(resolved).isFile()) return null;
|
|
7776
|
+
const entry = getFileEntry(resolved);
|
|
7777
|
+
if (entry === null) return null;
|
|
7778
|
+
if (entry.sha !== "") {
|
|
7779
|
+
const diskSha = fingerprintFile(resolved);
|
|
7780
|
+
if (diskSha === null || diskSha !== entry.sha) return null;
|
|
7781
|
+
}
|
|
7782
|
+
const lines = readFileSync3(resolved, "utf8").split("\n");
|
|
7783
|
+
const symbols = querySymbols({ filePath: resolved, limit: ALL_SYMBOLS_IN_FILE });
|
|
7784
|
+
if (symbols.length === 0) return null;
|
|
7785
|
+
const bytesOf = (start, end) => Buffer.byteLength(lines.slice(start - 1, Math.min(end, lines.length)).join("\n"), "utf8");
|
|
7786
|
+
const requestedLines = /* @__PURE__ */ new Set();
|
|
7787
|
+
const regionBySpan = /* @__PURE__ */ new Map();
|
|
7788
|
+
for (const [start, end] of ranges) {
|
|
7789
|
+
for (let l = start; l <= Math.min(end, lines.length); l++) requestedLines.add(l);
|
|
7790
|
+
for (const r of resolveLineRegions(symbols, lines.length, start, end)) {
|
|
7791
|
+
const named = symbols.find((s) => s.lineStart === r.start && s.lineEnd === r.end);
|
|
7792
|
+
regionBySpan.set(`${r.start}-${r.end}`, {
|
|
7793
|
+
kind: r.kind,
|
|
7794
|
+
name: r.kind === "symbol" && r.end > r.start && named !== void 0 ? named.name : null,
|
|
7795
|
+
start: r.start,
|
|
7796
|
+
end: r.end
|
|
7797
|
+
});
|
|
7798
|
+
}
|
|
7799
|
+
}
|
|
7800
|
+
if (regionBySpan.size === 0) return null;
|
|
7801
|
+
let requestedBytes = 0;
|
|
7802
|
+
for (const l of requestedLines) requestedBytes += Buffer.byteLength(lines[l - 1] ?? "", "utf8") + 1;
|
|
7803
|
+
const regions = [...regionBySpan.values()].sort((a, b) => a.start - b.start || a.end - b.end);
|
|
7804
|
+
const replacementBytes = regions.reduce((t, r) => t + bytesOf(r.start, r.end) + REGION_HEADER_BYTES, 0);
|
|
7805
|
+
const commands = regions.map(
|
|
7806
|
+
(r) => r.name === null ? `token-goat read "${hintPath}:${r.start}-${r.end}"` : `token-goat read "${hintPath}::${r.name}"`
|
|
7807
|
+
);
|
|
7808
|
+
return { requestedBytes, replacementBytes, commands };
|
|
7809
|
+
} catch {
|
|
7810
|
+
return null;
|
|
7811
|
+
}
|
|
7812
|
+
}
|
|
7813
|
+
|
|
7814
|
+
// src/bash_surgical_target.ts
|
|
7815
|
+
init_define_import_meta_env();
|
|
7816
|
+
import { statSync as statSync7 } from "node:fs";
|
|
7671
7817
|
|
|
7672
7818
|
// src/bash_extractors.ts
|
|
7673
7819
|
init_define_import_meta_env();
|
|
7674
|
-
import { statSync as
|
|
7675
|
-
function
|
|
7676
|
-
return
|
|
7820
|
+
import { statSync as statSync6, openSync as openSync2, readSync, closeSync as closeSync2 } from "node:fs";
|
|
7821
|
+
function genericSurgicalFallback(shown) {
|
|
7822
|
+
return '`token-goat outline "' + shown + '"`';
|
|
7823
|
+
}
|
|
7824
|
+
function surgicalHintFor(hintPath, isEnv, isConfig, isDoc, isXml = false, target = null) {
|
|
7825
|
+
const key = target ?? "KEY_NAME";
|
|
7826
|
+
const section = target ?? "SectionHeading";
|
|
7827
|
+
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.";
|
|
7677
7828
|
}
|
|
7678
7829
|
function surgicalHintForConfigDoc(filePath, isConfig, isDoc, isSql, isXml = false) {
|
|
7679
|
-
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.' :
|
|
7830
|
+
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.';
|
|
7680
7831
|
}
|
|
7681
7832
|
function splitShellSegments(cmd) {
|
|
7682
7833
|
const segments = [];
|
|
@@ -7775,7 +7926,7 @@ var PS_FILE_METHOD_RE = /\[(?:System\.)?IO\.File\]::(?:ReadAllText|ReadAllLines|
|
|
|
7775
7926
|
var PS_TEMP_READ_FLOOD_BYTES = 16 * 1024;
|
|
7776
7927
|
function isLargeFileOnDisk(filePath, floor) {
|
|
7777
7928
|
try {
|
|
7778
|
-
return
|
|
7929
|
+
return statSync6(filePath).size >= floor;
|
|
7779
7930
|
} catch {
|
|
7780
7931
|
return false;
|
|
7781
7932
|
}
|
|
@@ -8208,27 +8359,21 @@ function extractLineRangeReadsCompound(cmd) {
|
|
|
8208
8359
|
}
|
|
8209
8360
|
return [...merged.values()];
|
|
8210
8361
|
}
|
|
8211
|
-
function sedRangeHint(filePath, ranges, tool) {
|
|
8212
|
-
|
|
8213
|
-
|
|
8214
|
-
|
|
8215
|
-
const
|
|
8216
|
-
|
|
8217
|
-
|
|
8218
|
-
|
|
8219
|
-
if (lang === "toml" || lang === "json" || lang === "yaml" || lang === "ini") {
|
|
8220
|
-
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.";
|
|
8221
|
-
}
|
|
8222
|
-
if (languageHasFlag(lang, "symbolBearing")) {
|
|
8223
|
-
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.";
|
|
8224
|
-
}
|
|
8225
|
-
return prefix + "Use " + allReads + " to read exactly those lines.";
|
|
8362
|
+
function sedRangeHint(filePath, ranges, tool, sub) {
|
|
8363
|
+
return "`" + (tool === "awk" ? "awk" : "sed -n") + "` line-range reads bypass read hooks. " + substituteSentence(filePath, ranges, sub);
|
|
8364
|
+
}
|
|
8365
|
+
function substituteSentence(filePath, ranges, sub) {
|
|
8366
|
+
const spans = ranges.map(([s, e]) => s + "-" + e).join(", ");
|
|
8367
|
+
const reads = sub.commands.map((c) => "`" + c + "`");
|
|
8368
|
+
const allReads = reads.length === 2 ? reads.join(" and ") : reads.length >= 3 ? reads.slice(0, -1).join(", ") + ", and " + reads[reads.length - 1] : reads[0];
|
|
8369
|
+
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.";
|
|
8226
8370
|
}
|
|
8227
|
-
function leadingLinesHint(lead, hintPath, start, end,
|
|
8371
|
+
function leadingLinesHint(lead, hintPath, start, end, preHookCwd, substitute) {
|
|
8228
8372
|
const key = resolveIndexPath(hintPath, preHookCwd ?? process.cwd());
|
|
8229
8373
|
const prior = findRangeOverlap(getFileLineRanges(key), start, end);
|
|
8230
8374
|
if (prior !== null) return sedOverlapHint(hintPath, prior, start, end);
|
|
8231
|
-
|
|
8375
|
+
if (substitute === null) return null;
|
|
8376
|
+
return lead + substituteSentence(hintPath, [[start, end]], substitute);
|
|
8232
8377
|
}
|
|
8233
8378
|
function findRangeOverlap(prior, start, end) {
|
|
8234
8379
|
let best = null;
|
|
@@ -8372,7 +8517,7 @@ function extractTasksOutput(cmd) {
|
|
|
8372
8517
|
return null;
|
|
8373
8518
|
}
|
|
8374
8519
|
function extractToolResultsFile(cmd) {
|
|
8375
|
-
const toolResultsRe =
|
|
8520
|
+
const toolResultsRe = new RegExp("[/\\\\]tool-results[/\\\\](" + TOOL_RESULTS_ID_CHARS + ")\\.txt$", "i");
|
|
8376
8521
|
const catM = /^cat(?:\s+(?:-[a-zA-Z]+|--[a-zA-Z-]+))*\s+(?:"([^"]+)"|'([^']+)'|(\S+))\s*$/.exec(cmd);
|
|
8377
8522
|
if (catM) {
|
|
8378
8523
|
const fp = catM[1] ?? catM[2] ?? catM[3];
|
|
@@ -8726,7 +8871,43 @@ function extractTerminalXmlParsing(cmd) {
|
|
|
8726
8871
|
return null;
|
|
8727
8872
|
}
|
|
8728
8873
|
|
|
8874
|
+
// src/bash_surgical_target.ts
|
|
8875
|
+
var ALL_SYMBOLS_IN_FILE2 = -1;
|
|
8876
|
+
function quotableName(name2) {
|
|
8877
|
+
const safe = escapeHintName(name2);
|
|
8878
|
+
return safe === "" || safe.includes("::") ? null : safe;
|
|
8879
|
+
}
|
|
8880
|
+
function runnableTargetFor(hintPath, cwd, event) {
|
|
8881
|
+
try {
|
|
8882
|
+
if (!commandPathIsTouchable(hintPath, event)) return null;
|
|
8883
|
+
const resolved = resolveIndexPath(hintPath, cwd);
|
|
8884
|
+
if (!statSync7(resolved).isFile()) return null;
|
|
8885
|
+
const entry = getFileEntry(resolved);
|
|
8886
|
+
if (entry === null) return null;
|
|
8887
|
+
if (entry.sha !== "") {
|
|
8888
|
+
const diskSha = fingerprintFile(resolved);
|
|
8889
|
+
if (diskSha === null || diskSha !== entry.sha) return null;
|
|
8890
|
+
}
|
|
8891
|
+
const symbols = querySymbols({ filePath: resolved, limit: ALL_SYMBOLS_IN_FILE2 });
|
|
8892
|
+
if (symbols.length === 0) return null;
|
|
8893
|
+
const inLineOrder = [...symbols].sort((a, b) => a.lineStart - b.lineStart);
|
|
8894
|
+
for (const s of inLineOrder) {
|
|
8895
|
+
const safe = quotableName(s.name);
|
|
8896
|
+
if (safe !== null) return safe;
|
|
8897
|
+
}
|
|
8898
|
+
return null;
|
|
8899
|
+
} catch {
|
|
8900
|
+
return null;
|
|
8901
|
+
}
|
|
8902
|
+
}
|
|
8903
|
+
|
|
8904
|
+
// src/hooks_bash.ts
|
|
8905
|
+
import { statSync as statSync9, existsSync, readFileSync as readFileSync4 } from "node:fs";
|
|
8906
|
+
import * as path4 from "node:path";
|
|
8907
|
+
|
|
8729
8908
|
// src/hooks_bash_commands.ts
|
|
8909
|
+
init_define_import_meta_env();
|
|
8910
|
+
import { statSync as statSync8 } from "node:fs";
|
|
8730
8911
|
var CD_PREFIX_RE = /^(?:cd\s+(?:"[^"]*"|'[^']*'|\S+)[ \t]*(?:&&|;|\r?\n)\s*)+/;
|
|
8731
8912
|
function stripCdPrefix(cmd) {
|
|
8732
8913
|
const stripped = cmd.replace(CD_PREFIX_RE, "");
|
|
@@ -8942,7 +9123,7 @@ function workingTreeStatusPaths(gitDir) {
|
|
|
8942
9123
|
function enqueueRewrittenPath(absPath) {
|
|
8943
9124
|
if (/(?:^|[/\\])(?:NUL|nul)$/.test(absPath) || absPath.replace(/\\/g, "/").endsWith("/dev/null")) return;
|
|
8944
9125
|
try {
|
|
8945
|
-
if (!
|
|
9126
|
+
if (!statSync8(absPath).isFile()) return;
|
|
8946
9127
|
} catch {
|
|
8947
9128
|
return;
|
|
8948
9129
|
}
|
|
@@ -9007,6 +9188,15 @@ function pipelineShapeFilter(cmd, cwd) {
|
|
|
9007
9188
|
const detected = detectFromCommand(stripOutputPipeline(cmd), cwd ?? void 0);
|
|
9008
9189
|
return detected === null ? null : detected.filter;
|
|
9009
9190
|
}
|
|
9191
|
+
function isFullRecallCommand(cmd) {
|
|
9192
|
+
const forSplit = cmd.replace(/\s2>(?:&1|\/dev\/null)/g, "");
|
|
9193
|
+
if (hasBareBackgroundOrNewline(forSplit)) return false;
|
|
9194
|
+
const first2 = splitShellSegments(forSplit)[0];
|
|
9195
|
+
if (first2 === void 0 || !segmentCommandIs(first2, "token-goat")) return false;
|
|
9196
|
+
const tokens = safeShlexSplit(stripOutputPipeline(first2));
|
|
9197
|
+
if (tokens === null) return false;
|
|
9198
|
+
return Object.values(RECALL_COMMAND).includes(tokens[1] ?? "") && tokens.includes("--full");
|
|
9199
|
+
}
|
|
9010
9200
|
function pureFileReadPath(cmd) {
|
|
9011
9201
|
const single = extractCatFile(cmd)?.filePath ?? extractHeadFile(cmd)?.filePath ?? extractTailFile(cmd)?.filePath ?? extractLineRangeRead(cmd)?.filePath;
|
|
9012
9202
|
if (single !== void 0) return single;
|
|
@@ -9211,6 +9401,7 @@ function detectUnbalancedShellSyntax(cmd) {
|
|
|
9211
9401
|
function maybeCompressRewrite(event, rawCmd, cmd) {
|
|
9212
9402
|
if (process.env["TOKEN_GOAT_BASH_COMPRESS"] === "0") return null;
|
|
9213
9403
|
if (event.raw["_tg_harness"] === "vscode") return null;
|
|
9404
|
+
if ((event.raw["_tg_harness"] === "codex" || event.raw["_tg_harness"] === "copilot_cli") && process.platform === "win32") return null;
|
|
9214
9405
|
let cfg;
|
|
9215
9406
|
try {
|
|
9216
9407
|
cfg = loadConfig().bash_compress;
|
|
@@ -9290,7 +9481,7 @@ function foldShellReadStructure(cmd, filePath, output, fileKey, cwd) {
|
|
|
9290
9481
|
const originalBytes = Buffer.byteLength(output, "utf-8");
|
|
9291
9482
|
let onDisk;
|
|
9292
9483
|
try {
|
|
9293
|
-
onDisk =
|
|
9484
|
+
onDisk = statSync9(fileKey).size;
|
|
9294
9485
|
} catch {
|
|
9295
9486
|
return null;
|
|
9296
9487
|
}
|
|
@@ -9398,6 +9589,7 @@ async function maybeElideServedGenericOutput(cmd, output, exitCode, cwd, cacheMi
|
|
|
9398
9589
|
async function maybeCompressCompoundOutput(cmd, output, exitCode, cwd, cacheMinBytes, isUnwrapped = false) {
|
|
9399
9590
|
if (process.env["TOKEN_GOAT_BASH_COMPRESS"] === "0") return null;
|
|
9400
9591
|
if (!isUnwrapped && isCompressibleSingleCommand(cmd)) return null;
|
|
9592
|
+
if (isFullRecallCommand(cmd)) return null;
|
|
9401
9593
|
if (exitCode !== null && exitCode !== 0) return null;
|
|
9402
9594
|
let cfg;
|
|
9403
9595
|
try {
|
|
@@ -9416,7 +9608,7 @@ async function maybeCompressCompoundOutput(cmd, output, exitCode, cwd, cacheMinB
|
|
|
9416
9608
|
});
|
|
9417
9609
|
const minNet = resolveMinNetSavingsBytes();
|
|
9418
9610
|
if (!compressed.worthApplying(minNet)) return null;
|
|
9419
|
-
const id =
|
|
9611
|
+
const id = bashOutputIdSync(cmd, output, cwd);
|
|
9420
9612
|
const { body: untrusted, notices } = splitOwnTrailingNotices(compressed.text);
|
|
9421
9613
|
const marker = compressed.withMarker(minNet).slice(compressed.text.length);
|
|
9422
9614
|
const body = fenceUntrusted(untrusted, UNTRUSTED_TOOL_TAG) + notices + marker + "\n[token-goat] full output: bash-output " + id + " --full";
|
|
@@ -9495,6 +9687,15 @@ function preBashHandlerInner(event) {
|
|
|
9495
9687
|
const cdStripped = cmd !== rawCmd;
|
|
9496
9688
|
const preHookCwd = getCwd(event) ?? null;
|
|
9497
9689
|
const hintCwd = preHookCwd ?? process.cwd();
|
|
9690
|
+
const pathHint = (paths, text) => contextOutput(text, typeof paths === "string" ? [paths] : paths);
|
|
9691
|
+
const pricedSubstitute = (hintPath, start, end) => {
|
|
9692
|
+
const sub = rangeSubstituteFor(hintPath, hintCwd, [[start, end]]);
|
|
9693
|
+
return sub !== null && meetsSavingsFloor(sub.requestedBytes - sub.replacementBytes) ? sub : null;
|
|
9694
|
+
};
|
|
9695
|
+
const declineUnpriced = (paths) => {
|
|
9696
|
+
for (const p of typeof paths === "string" ? [paths] : paths) logSuppressedDetection("bash_redirect", event.sessionId, p);
|
|
9697
|
+
return passOutput();
|
|
9698
|
+
};
|
|
9498
9699
|
const cfg = loadConfig();
|
|
9499
9700
|
if (cfg.hints.warn_unbalanced_shell_quoting) {
|
|
9500
9701
|
const quoteError = detectUnbalancedShellSyntax(cmd);
|
|
@@ -9555,17 +9756,19 @@ function preBashHandlerInner(event) {
|
|
|
9555
9756
|
const { filePath, toolOrScript } = terminalXml;
|
|
9556
9757
|
recordStat("session_hint", 0, 0);
|
|
9557
9758
|
const target = filePath ? displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath) : "<file>";
|
|
9558
|
-
return
|
|
9759
|
+
return pathHint(
|
|
9760
|
+
filePath ? [target] : [],
|
|
9559
9761
|
`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}).`
|
|
9560
9762
|
);
|
|
9561
9763
|
}
|
|
9562
9764
|
const singleLineRangeRead = extractLineRangeRead(cmd);
|
|
9563
9765
|
const sedReads = singleLineRangeRead !== null ? [singleLineRangeRead] : extractLineRangeReadsCompound(cmd);
|
|
9564
9766
|
if (sedReads !== null) {
|
|
9565
|
-
recordStat("session_hint", 0, 0);
|
|
9566
9767
|
const hints = [];
|
|
9768
|
+
const hintPaths = [];
|
|
9567
9769
|
for (const { filePath, ranges, tool } of sedReads) {
|
|
9568
9770
|
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
|
|
9771
|
+
hintPaths.push(hintPath);
|
|
9569
9772
|
const sedDedupKey = resolveIndexPath(hintPath, preHookCwd ?? process.cwd());
|
|
9570
9773
|
const overlapHints = [];
|
|
9571
9774
|
const freshRanges = [];
|
|
@@ -9579,23 +9782,30 @@ function preBashHandlerInner(event) {
|
|
|
9579
9782
|
}
|
|
9580
9783
|
}
|
|
9581
9784
|
hints.push(...overlapHints);
|
|
9582
|
-
|
|
9785
|
+
const sub = freshRanges.length > 0 ? rangeSubstituteFor(hintPath, hintCwd, freshRanges) : null;
|
|
9786
|
+
if (sub !== null && meetsSavingsFloor(sub.requestedBytes - sub.replacementBytes)) {
|
|
9787
|
+
hints.push(sedRangeHint(hintPath, freshRanges, tool, sub));
|
|
9788
|
+
}
|
|
9583
9789
|
}
|
|
9584
|
-
|
|
9790
|
+
if (hints.length === 0) return declineUnpriced(hintPaths);
|
|
9791
|
+
recordStat("session_hint", 0, 0);
|
|
9792
|
+
return pathHint(hintPaths, hints.join(" "));
|
|
9585
9793
|
}
|
|
9586
9794
|
const gcTailResult = extractGetContentTail(cmd);
|
|
9587
9795
|
if (gcTailResult !== null) {
|
|
9588
9796
|
const { filePath, isDoc, isConfig, isSql, isXml } = gcTailResult;
|
|
9589
9797
|
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
|
|
9590
9798
|
recordStat("session_hint", 0, 0);
|
|
9591
|
-
return
|
|
9799
|
+
return pathHint(hintPath, "`Get-Content -Tail` bypasses read hooks. " + surgicalHintForConfigDoc(hintPath, isConfig, isDoc, isSql, isXml));
|
|
9592
9800
|
}
|
|
9593
9801
|
const gcSelectResult = extractGetContentSelectFirst(cmd);
|
|
9594
9802
|
if (gcSelectResult !== null) {
|
|
9595
|
-
const { filePath,
|
|
9803
|
+
const { filePath, n } = gcSelectResult;
|
|
9596
9804
|
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
|
|
9805
|
+
const gcSelectHint = leadingLinesHint("`Select-Object -First` bypasses read hooks. ", hintPath, 1, n, preHookCwd, pricedSubstitute(hintPath, 1, n));
|
|
9806
|
+
if (gcSelectHint === null) return declineUnpriced(hintPath);
|
|
9597
9807
|
recordStat("session_hint", 0, 0);
|
|
9598
|
-
return
|
|
9808
|
+
return pathHint(hintPath, gcSelectHint);
|
|
9599
9809
|
}
|
|
9600
9810
|
const catJsonPipe = extractCatJsonPipe(cmd);
|
|
9601
9811
|
if (catJsonPipe !== null) {
|
|
@@ -9603,7 +9813,8 @@ function preBashHandlerInner(event) {
|
|
|
9603
9813
|
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
|
|
9604
9814
|
recordStat("session_hint", 0, 0);
|
|
9605
9815
|
const label = isDirectJq ? "`jq`" : "`cat | jq`";
|
|
9606
|
-
return
|
|
9816
|
+
return pathHint(
|
|
9817
|
+
hintPath,
|
|
9607
9818
|
label + ' loads the whole file. Use `token-goat json-query "' + hintPath + '" "<key>"` or `token-goat config-get "' + hintPath + '" KEY_NAME` to slice one value.'
|
|
9608
9819
|
);
|
|
9609
9820
|
}
|
|
@@ -9612,7 +9823,8 @@ function preBashHandlerInner(event) {
|
|
|
9612
9823
|
recordStat("session_hint", 0, 0);
|
|
9613
9824
|
if (psJsonPipe.filePath) {
|
|
9614
9825
|
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, psJsonPipe.filePath, hintCwd) : psJsonPipe.filePath);
|
|
9615
|
-
return
|
|
9826
|
+
return pathHint(
|
|
9827
|
+
hintPath,
|
|
9616
9828
|
'PowerShell `ConvertFrom-Json` pipeline detected. Use `token-goat json-query "' + hintPath + '" "<selector>"` to extract fields directly without shell conversion scripts.'
|
|
9617
9829
|
);
|
|
9618
9830
|
}
|
|
@@ -9626,12 +9838,13 @@ function preBashHandlerInner(event) {
|
|
|
9626
9838
|
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
|
|
9627
9839
|
recordStat("session_hint", 0, 0);
|
|
9628
9840
|
if (isSql) {
|
|
9629
|
-
return
|
|
9841
|
+
return pathHint(
|
|
9842
|
+
hintPath,
|
|
9630
9843
|
"`" + cmd0 + '` loads the entire file into context. Use `token-goat section "' + hintPath + '::table_name"` to pull one CREATE TABLE / CREATE TYPE block.'
|
|
9631
9844
|
);
|
|
9632
9845
|
}
|
|
9633
|
-
const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc, isXml);
|
|
9634
|
-
return cdStripped || advisoryOnly ?
|
|
9846
|
+
const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc, isXml, runnableTargetFor(hintPath, hintCwd, event));
|
|
9847
|
+
return cdStripped || advisoryOnly ? pathHint(hintPath, "`" + cmd0 + "` loads the entire file into context. " + hint) : denyOutput("`" + cmd0 + "` loads the entire file into context. " + hint);
|
|
9635
9848
|
}
|
|
9636
9849
|
const catMulti = extractCatFilesMulti(cmd);
|
|
9637
9850
|
if (catMulti !== null) {
|
|
@@ -9643,7 +9856,7 @@ function preBashHandlerInner(event) {
|
|
|
9643
9856
|
return " " + hintPath + " -> `" + how + "`";
|
|
9644
9857
|
});
|
|
9645
9858
|
const msg = "`" + cmd0 + "` on multiple files loads them all into context. Read each surgically instead:\n" + perPath.join("\n");
|
|
9646
|
-
return cdStripped ?
|
|
9859
|
+
return cdStripped ? pathHint(catMulti.map((m) => displaySafePath(resolveCdHintPath(rawCmd, m.filePath, hintCwd))), msg) : denyOutput(msg);
|
|
9647
9860
|
}
|
|
9648
9861
|
const psGetContentResult = extractPowerShellWrappedGetContent(cmd, event);
|
|
9649
9862
|
if (psGetContentResult !== null) {
|
|
@@ -9652,10 +9865,10 @@ function preBashHandlerInner(event) {
|
|
|
9652
9865
|
recordStat("session_hint", 0, 0);
|
|
9653
9866
|
const lead = "`Get-Content` via a `powershell -Command` wrapper bypasses read hooks and loads the entire file into context. ";
|
|
9654
9867
|
if (isSql) {
|
|
9655
|
-
return
|
|
9868
|
+
return pathHint(hintPath, lead + 'Use `token-goat section "' + hintPath + '::table_name"` to pull one CREATE TABLE / CREATE TYPE block.');
|
|
9656
9869
|
}
|
|
9657
|
-
const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc, isXml);
|
|
9658
|
-
return cdStripped ?
|
|
9870
|
+
const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc, isXml, runnableTargetFor(hintPath, hintCwd, event));
|
|
9871
|
+
return cdStripped ? pathHint(hintPath, lead + hint) : denyOutput(lead + hint);
|
|
9659
9872
|
}
|
|
9660
9873
|
const wslCatResult = extractWslCatFile(cmd);
|
|
9661
9874
|
if (wslCatResult !== null) {
|
|
@@ -9663,12 +9876,13 @@ function preBashHandlerInner(event) {
|
|
|
9663
9876
|
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
|
|
9664
9877
|
recordStat("session_hint", 0, 0);
|
|
9665
9878
|
if (isSql) {
|
|
9666
|
-
return
|
|
9879
|
+
return pathHint(
|
|
9880
|
+
hintPath,
|
|
9667
9881
|
'`cat` loads the entire file into context. Use `token-goat section "' + hintPath + '::table_name"` to pull one CREATE TABLE / CREATE TYPE block.'
|
|
9668
9882
|
);
|
|
9669
9883
|
}
|
|
9670
|
-
const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc, isXml);
|
|
9671
|
-
return cdStripped ?
|
|
9884
|
+
const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc, isXml, runnableTargetFor(hintPath, hintCwd, event));
|
|
9885
|
+
return cdStripped ? pathHint(hintPath, "`cat` loads the entire file into context. " + hint) : denyOutput("`cat` loads the entire file into context. " + hint);
|
|
9672
9886
|
}
|
|
9673
9887
|
const pyRead = extractPythonFileRead(cmd);
|
|
9674
9888
|
if (pyRead !== null) {
|
|
@@ -9678,33 +9892,37 @@ function preBashHandlerInner(event) {
|
|
|
9678
9892
|
if (isOutputFile) {
|
|
9679
9893
|
if (taskOutputIsJsonlTranscript(filePath)) {
|
|
9680
9894
|
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.';
|
|
9681
|
-
return cdStripped ?
|
|
9895
|
+
return cdStripped ? pathHint(hintPath, tHint) : denyOutput(tHint);
|
|
9682
9896
|
}
|
|
9683
|
-
return
|
|
9897
|
+
return pathHint(
|
|
9898
|
+
hintPath,
|
|
9684
9899
|
"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.'
|
|
9685
9900
|
);
|
|
9686
9901
|
}
|
|
9687
9902
|
if (isSql) {
|
|
9688
|
-
return
|
|
9903
|
+
return pathHint(
|
|
9904
|
+
hintPath,
|
|
9689
9905
|
'Python `open()` file reads bypass read hooks. Use `token-goat section "' + hintPath + '::table_name"` to pull one CREATE TABLE / CREATE TYPE block.'
|
|
9690
9906
|
);
|
|
9691
9907
|
}
|
|
9692
|
-
const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc, isXml);
|
|
9693
|
-
return cdStripped ?
|
|
9908
|
+
const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc, isXml, runnableTargetFor(hintPath, hintCwd, event));
|
|
9909
|
+
return cdStripped ? pathHint(hintPath, "Python `open()` file reads bypass read hooks. " + hint) : denyOutput("Python `open()` file reads bypass read hooks. " + hint);
|
|
9694
9910
|
}
|
|
9695
9911
|
const tailResult = extractTailFile(cmd);
|
|
9696
9912
|
if (tailResult !== null) {
|
|
9697
9913
|
const { filePath, isDoc, isConfig, isSql, isXml } = tailResult;
|
|
9698
9914
|
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
|
|
9699
9915
|
recordStat("session_hint", 0, 0);
|
|
9700
|
-
return
|
|
9916
|
+
return pathHint(hintPath, "`tail` bypasses read hooks. " + surgicalHintForConfigDoc(hintPath, isConfig, isDoc, isSql, isXml));
|
|
9701
9917
|
}
|
|
9702
9918
|
const headResult = extractHeadFile(cmd);
|
|
9703
9919
|
if (headResult !== null) {
|
|
9704
|
-
const { filePath,
|
|
9920
|
+
const { filePath, n } = headResult;
|
|
9705
9921
|
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
|
|
9922
|
+
const headHint = leadingLinesHint("`head` bypasses read hooks. ", hintPath, 1, n, preHookCwd, pricedSubstitute(hintPath, 1, n));
|
|
9923
|
+
if (headHint === null) return declineUnpriced(hintPath);
|
|
9706
9924
|
recordStat("session_hint", 0, 0);
|
|
9707
|
-
return
|
|
9925
|
+
return pathHint(hintPath, headHint);
|
|
9708
9926
|
}
|
|
9709
9927
|
const nodeRead = extractNodeFileRead(cmd);
|
|
9710
9928
|
if (nodeRead !== null) {
|
|
@@ -9713,11 +9931,11 @@ function preBashHandlerInner(event) {
|
|
|
9713
9931
|
const lead = "Node.js `fs.readFileSync()` bypasses read hooks. ";
|
|
9714
9932
|
if (isSql) {
|
|
9715
9933
|
recordStat("session_hint", 0, 0);
|
|
9716
|
-
return
|
|
9934
|
+
return pathHint(hintPath, lead + 'Use `token-goat section "' + hintPath + '::table_name"` to pull one CREATE TABLE / CREATE TYPE block.');
|
|
9717
9935
|
}
|
|
9718
|
-
const hint =
|
|
9936
|
+
const hint = surgicalHintFor(hintPath, false, isConfig, isDoc, false, runnableTargetFor(hintPath, hintCwd, event));
|
|
9719
9937
|
recordStat("session_hint", 0, 0);
|
|
9720
|
-
return cdStripped ?
|
|
9938
|
+
return cdStripped ? pathHint(hintPath, lead + hint) : denyOutput(lead + hint);
|
|
9721
9939
|
}
|
|
9722
9940
|
const psMethodRead = extractPowerShellFileMethodRead(cmd, event);
|
|
9723
9941
|
if (psMethodRead !== null) {
|
|
@@ -9726,11 +9944,15 @@ function preBashHandlerInner(event) {
|
|
|
9726
9944
|
const lead = "PowerShell `[IO.File]::ReadAllText()` bypasses read hooks. ";
|
|
9727
9945
|
if (isSql) {
|
|
9728
9946
|
recordStat("session_hint", 0, 0);
|
|
9729
|
-
return
|
|
9947
|
+
return pathHint(hintPath, lead + 'Use `token-goat section "' + hintPath + '::table_name"` to pull one CREATE TABLE / CREATE TYPE block.');
|
|
9730
9948
|
}
|
|
9731
|
-
const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc, isXml);
|
|
9949
|
+
const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc, isXml, runnableTargetFor(hintPath, hintCwd, event));
|
|
9732
9950
|
recordStat("session_hint", 0, 0);
|
|
9733
|
-
return cdStripped ?
|
|
9951
|
+
return cdStripped ? pathHint(hintPath, lead + hint) : denyOutput(lead + hint);
|
|
9952
|
+
}
|
|
9953
|
+
const structuralRewrite = detectStructuralIndexRewrite(rawCmd, hintCwd);
|
|
9954
|
+
if (structuralRewrite !== null) {
|
|
9955
|
+
return { hookType: "rewriteInput", updatedInput: { ...event.toolInput, command: structuralRewrite.command } };
|
|
9734
9956
|
}
|
|
9735
9957
|
if (extractGrepPipeChain(cmd)) {
|
|
9736
9958
|
recordStat("session_hint", 0, 0);
|
|
@@ -9743,7 +9965,8 @@ function preBashHandlerInner(event) {
|
|
|
9743
9965
|
const { filePath } = mdHeadingGrep;
|
|
9744
9966
|
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
|
|
9745
9967
|
recordStat("session_hint", 0, 0);
|
|
9746
|
-
return
|
|
9968
|
+
return pathHint(
|
|
9969
|
+
hintPath,
|
|
9747
9970
|
'Use `token-goat outline "' + hintPath + '"` to get all headings with line ranges \u2014 then `token-goat section "' + hintPath + '::Heading"` to read one section.'
|
|
9748
9971
|
);
|
|
9749
9972
|
}
|
|
@@ -9752,7 +9975,8 @@ function preBashHandlerInner(event) {
|
|
|
9752
9975
|
const { identifier } = rgSymbol;
|
|
9753
9976
|
recordStat("session_hint", 0, 0);
|
|
9754
9977
|
return contextOutput(
|
|
9755
|
-
"Use `token-goat symbol " + identifier + "` to jump directly to the definition without scanning the file."
|
|
9978
|
+
"Use `token-goat symbol " + identifier + "` to jump directly to the definition without scanning the file.",
|
|
9979
|
+
[identifier]
|
|
9756
9980
|
);
|
|
9757
9981
|
}
|
|
9758
9982
|
const rgStructural = extractRgStructuralSearch(cmd);
|
|
@@ -9760,7 +9984,8 @@ function preBashHandlerInner(event) {
|
|
|
9760
9984
|
const { filePath } = rgStructural;
|
|
9761
9985
|
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
|
|
9762
9986
|
recordStat("session_hint", 0, 0);
|
|
9763
|
-
return
|
|
9987
|
+
return pathHint(
|
|
9988
|
+
hintPath,
|
|
9764
9989
|
'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.'
|
|
9765
9990
|
);
|
|
9766
9991
|
}
|
|
@@ -9792,7 +10017,7 @@ function preBashHandlerInner(event) {
|
|
|
9792
10017
|
const prevResolvedPath = prevPath !== null && commandPathIsTouchable(prevPath, event) ? resolveIndexPath(prevPath, preHookCwd ?? process.cwd()) : null;
|
|
9793
10018
|
if (prevPath !== null && prevResolvedPath !== null && !existsSync(prevResolvedPath)) {
|
|
9794
10019
|
clearCurlDownload(curlDl.url);
|
|
9795
|
-
} else if (prevPath !== null && prevResolvedPath !== null &&
|
|
10020
|
+
} else if (prevPath !== null && prevResolvedPath !== null && statSync9(prevResolvedPath).size >= loadConfig().hints.bash_dedup_min_bytes) {
|
|
9796
10021
|
recordStat("session_hint", 0, 0);
|
|
9797
10022
|
return denyOutput(
|
|
9798
10023
|
"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.'
|
|
@@ -9854,7 +10079,7 @@ function preBashHandlerInner(event) {
|
|
|
9854
10079
|
}
|
|
9855
10080
|
if (notes.length > 0) {
|
|
9856
10081
|
recordStat("session_hint", 0, 0);
|
|
9857
|
-
return
|
|
10082
|
+
return pathHint(tgRead.filePath !== null ? [tgRead.filePath] : [], notes.join(" "));
|
|
9858
10083
|
}
|
|
9859
10084
|
return passOutput();
|
|
9860
10085
|
}
|
|
@@ -9886,7 +10111,28 @@ function preBashHandler(event) {
|
|
|
9886
10111
|
return applyHintTracking(event, preBashHandlerInner(event), classifyBashHint);
|
|
9887
10112
|
}
|
|
9888
10113
|
registerHook("pre_tool_use", preBashHandler, { toolName: "Bash" });
|
|
9889
|
-
function
|
|
10114
|
+
function readPersistedBashOutput(resp, cwd, sessionId) {
|
|
10115
|
+
const persistedPath = resp["persistedOutputPath"];
|
|
10116
|
+
if (typeof persistedPath !== "string" || persistedPath === "" || sessionId === "") return null;
|
|
10117
|
+
const root = path4.join(projectTranscriptsDir(cwd ?? process.cwd()), sessionId, "tool-results");
|
|
10118
|
+
if (!isInsideRoot(persistedPath, root)) return null;
|
|
10119
|
+
try {
|
|
10120
|
+
const stat = statSync9(persistedPath);
|
|
10121
|
+
if (!stat.isFile() || stat.size > MAX_CAPTURE_BYTES) return null;
|
|
10122
|
+
const persistedSize = resp["persistedOutputSize"];
|
|
10123
|
+
if (typeof persistedSize === "number" && Math.abs(stat.size - persistedSize) > 8) return null;
|
|
10124
|
+
return readFileSync4(persistedPath, "utf-8");
|
|
10125
|
+
} catch {
|
|
10126
|
+
return null;
|
|
10127
|
+
}
|
|
10128
|
+
}
|
|
10129
|
+
function extractBashOutput(event) {
|
|
10130
|
+
const raw = event.raw;
|
|
10131
|
+
const resp = raw["tool_response"];
|
|
10132
|
+
if (resp !== null && typeof resp === "object") {
|
|
10133
|
+
const persisted = readPersistedBashOutput(resp, getCwd(event) ?? null, event.sessionId);
|
|
10134
|
+
if (persisted !== null) return persisted;
|
|
10135
|
+
}
|
|
9890
10136
|
return extractToolResponseField(raw, OUTPUT_FIRST_TOOL_RESPONSE_KEYS);
|
|
9891
10137
|
}
|
|
9892
10138
|
function extractExitCode(raw) {
|
|
@@ -10014,7 +10260,7 @@ async function postBashHandler(event) {
|
|
|
10014
10260
|
const isUnwrapped = unwrapCompressCommand(rawCmdRaw) === null;
|
|
10015
10261
|
const rawCmd = unwrapCompressCommand(rawCmdRaw) ?? rawCmdRaw;
|
|
10016
10262
|
const cmd = stripCdPrefix(rawCmd);
|
|
10017
|
-
const output = extractBashOutput(event
|
|
10263
|
+
const output = extractBashOutput(event);
|
|
10018
10264
|
const exitCode = extractExitCode(event.raw);
|
|
10019
10265
|
const cwd = getCwd(event) ?? null;
|
|
10020
10266
|
const cacheMinBytes = loadConfig().bash_compress.cache_min_bytes;
|
|
@@ -10201,9 +10447,11 @@ function postBashOutputHandler(event) {
|
|
|
10201
10447
|
const delta = output.slice(prior.output.length);
|
|
10202
10448
|
storePollSnapshot(event.sessionId, bashId, output);
|
|
10203
10449
|
if (Buffer.byteLength(delta, "utf-8") < minBytes) return passOutput();
|
|
10450
|
+
const compressedDelta = looksLikePytestOutput(delta) ? pytestFilter.apply(delta, "", 0, []) : null;
|
|
10451
|
+
const deltaBody = compressedDelta !== null && compressedDelta.worthApplying(resolveMinNetSavingsBytes()) ? compressedDelta.withMarker(resolveMinNetSavingsBytes()) : delta;
|
|
10204
10452
|
const deltaNotice = `[token-goat: bash_id ${bashId} delta since last poll]
|
|
10205
10453
|
`;
|
|
10206
|
-
const fenced = fenceUntrusted(
|
|
10454
|
+
const fenced = fenceUntrusted(deltaBody, UNTRUSTED_TOOL_TAG);
|
|
10207
10455
|
const body = `${deltaNotice}${fenced}`;
|
|
10208
10456
|
if (!isRewriteWorthwhile({
|
|
10209
10457
|
originalBytes: Buffer.byteLength(output, "utf-8"),
|
|
@@ -10325,8 +10573,8 @@ registerHook("post_tool_use", postTaskOutputHandler, { toolName: "TaskOutput" })
|
|
|
10325
10573
|
|
|
10326
10574
|
// src/hooks_tool_failure.ts
|
|
10327
10575
|
init_define_import_meta_env();
|
|
10328
|
-
import { existsSync as existsSync2, readFileSync as
|
|
10329
|
-
import { dirname as dirname2, relative } from "node:path";
|
|
10576
|
+
import { existsSync as existsSync2, readFileSync as readFileSync5, statSync as statSync10, writeFileSync as writeFileSync2 } from "node:fs";
|
|
10577
|
+
import { dirname as dirname2, relative as relative2 } from "node:path";
|
|
10330
10578
|
var FAILURE_SUFFIX = ".tool-failures.json";
|
|
10331
10579
|
var MAX_TRACKED_FAILURES = 64;
|
|
10332
10580
|
var SIGNATURE_ERROR_CHARS = 200;
|
|
@@ -10353,7 +10601,7 @@ function ledgerPath(sessionId) {
|
|
|
10353
10601
|
}
|
|
10354
10602
|
function readLedger(target) {
|
|
10355
10603
|
try {
|
|
10356
|
-
const parsed = JSON.parse(
|
|
10604
|
+
const parsed = JSON.parse(readFileSync5(target, "utf8"));
|
|
10357
10605
|
if (parsed !== null && typeof parsed === "object") {
|
|
10358
10606
|
const seen = parsed.seen;
|
|
10359
10607
|
const order = parsed.order;
|
|
@@ -10394,10 +10642,10 @@ function diagnoseEditFailure(event, errorText) {
|
|
|
10394
10642
|
const absPath = normalizePath(filePath);
|
|
10395
10643
|
if (!existsSync2(absPath)) return null;
|
|
10396
10644
|
try {
|
|
10397
|
-
const stat =
|
|
10645
|
+
const stat = statSync10(absPath);
|
|
10398
10646
|
if (stat.size > MAX_EDIT_DIAGNOSE_BYTES) return null;
|
|
10399
|
-
const fileContent =
|
|
10400
|
-
const relDisplay = displaySafeText(
|
|
10647
|
+
const fileContent = readFileSync5(absPath, "utf8");
|
|
10648
|
+
const relDisplay = displaySafeText(relative2(process.cwd(), absPath).replace(/\\/g, "/") || absPath);
|
|
10401
10649
|
if (isMultiple) {
|
|
10402
10650
|
const matchLines = [];
|
|
10403
10651
|
let pos = 0;
|
|
@@ -11044,8 +11292,8 @@ The full payload was cached to prevent harness context spill. Slicing commands:
|
|
|
11044
11292
|
}
|
|
11045
11293
|
return passOrFence();
|
|
11046
11294
|
}
|
|
11047
|
-
registerHook("pre_tool_use", preMcpHandler, { toolPattern:
|
|
11048
|
-
registerHook("post_tool_use", postMcpHandler, { toolPattern:
|
|
11295
|
+
registerHook("pre_tool_use", preMcpHandler, { toolPattern: MCP_TOOL_PATTERN });
|
|
11296
|
+
registerHook("post_tool_use", postMcpHandler, { toolPattern: MCP_TOOL_PATTERN });
|
|
11049
11297
|
|
|
11050
11298
|
// src/hooks_websearch.ts
|
|
11051
11299
|
init_define_import_meta_env();
|
|
@@ -11246,8 +11494,7 @@ registerHook("post_tool_use", postBrowserImageHandler, { toolPattern: "^mcp__" }
|
|
|
11246
11494
|
init_define_import_meta_env();
|
|
11247
11495
|
import { createHash as createHash2 } from "node:crypto";
|
|
11248
11496
|
import * as fs2 from "node:fs";
|
|
11249
|
-
import * as
|
|
11250
|
-
import * as path3 from "node:path";
|
|
11497
|
+
import * as path5 from "node:path";
|
|
11251
11498
|
var BRIEFING_TARGET_TOKENS = 550;
|
|
11252
11499
|
function buildSubagentBriefing() {
|
|
11253
11500
|
try {
|
|
@@ -11472,8 +11719,8 @@ function parseAgentDefinition(text, fallbackName) {
|
|
|
11472
11719
|
return { name: name2, restricted: false };
|
|
11473
11720
|
}
|
|
11474
11721
|
function findRestrictedAgentNames(roots) {
|
|
11475
|
-
const projectAgentsRoot =
|
|
11476
|
-
const scanRoots = roots ?? [
|
|
11722
|
+
const projectAgentsRoot = path5.join(process.cwd(), ".claude", "agents");
|
|
11723
|
+
const scanRoots = roots ?? [path5.join(claudeConfigDir(), "agents"), projectAgentsRoot];
|
|
11477
11724
|
const names = /* @__PURE__ */ new Set();
|
|
11478
11725
|
const visited = /* @__PURE__ */ new Set();
|
|
11479
11726
|
let filesSeen = 0;
|
|
@@ -11497,7 +11744,7 @@ function findRestrictedAgentNames(roots) {
|
|
|
11497
11744
|
}
|
|
11498
11745
|
for (const entry of entries) {
|
|
11499
11746
|
if (filesSeen >= ROSTER_WALK_MAX_FILES) return;
|
|
11500
|
-
const full =
|
|
11747
|
+
const full = path5.join(dir, entry.name);
|
|
11501
11748
|
let stat;
|
|
11502
11749
|
try {
|
|
11503
11750
|
stat = fs2.statSync(full);
|
|
@@ -11516,12 +11763,12 @@ function findRestrictedAgentNames(roots) {
|
|
|
11516
11763
|
} catch {
|
|
11517
11764
|
continue;
|
|
11518
11765
|
}
|
|
11519
|
-
const parsed = parseAgentDefinition(text,
|
|
11766
|
+
const parsed = parseAgentDefinition(text, path5.basename(entry.name, path5.extname(entry.name)));
|
|
11520
11767
|
if (parsed !== null && parsed.restricted) names.add(parsed.name);
|
|
11521
11768
|
}
|
|
11522
11769
|
};
|
|
11523
11770
|
for (const root of scanRoots) {
|
|
11524
|
-
const isProjectRoot = roots === void 0 &&
|
|
11771
|
+
const isProjectRoot = roots === void 0 && path5.resolve(root) === path5.resolve(projectAgentsRoot);
|
|
11525
11772
|
if (!isProjectRoot) {
|
|
11526
11773
|
walk(root, 0, null);
|
|
11527
11774
|
continue;
|
|
@@ -11651,11 +11898,11 @@ function safeSuggestions(output) {
|
|
|
11651
11898
|
if (output.hookType === "context") return { hookType: "context", context: stripUnsafeSuggestions(output.context) };
|
|
11652
11899
|
return output;
|
|
11653
11900
|
}
|
|
11654
|
-
async function relayInProcess(eventName, rawPayload) {
|
|
11901
|
+
async function relayInProcess(eventName, rawPayload, harnessWaitMs) {
|
|
11902
|
+
if (!isHookEventName(eventName)) {
|
|
11903
|
+
return "{}";
|
|
11904
|
+
}
|
|
11655
11905
|
try {
|
|
11656
|
-
if (!isHookEventName(eventName)) {
|
|
11657
|
-
return "{}";
|
|
11658
|
-
}
|
|
11659
11906
|
const harness = detectHarness();
|
|
11660
11907
|
const payload = eventName === "pre_tool_use" || eventName === "post_tool_use" ? normalizePayload(rawPayload, harnessForNormalization()) : rawPayload;
|
|
11661
11908
|
const event = buildEvent(eventName, payload);
|
|
@@ -11686,6 +11933,8 @@ async function relayInProcess(eventName, rawPayload) {
|
|
|
11686
11933
|
return serializeOutput(output, event.eventName, harness, event);
|
|
11687
11934
|
} catch {
|
|
11688
11935
|
return "{}";
|
|
11936
|
+
} finally {
|
|
11937
|
+
recordStat(`hook:${eventName}`, 0, 0, void 0, void 0, void 0, harnessWaitMs ?? performance.now());
|
|
11689
11938
|
}
|
|
11690
11939
|
}
|
|
11691
11940
|
async function relay(eventName) {
|