token-goat 2.9.18 → 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-V3ZWBEHM.mjs → token-goat-chunk-2GTB5RBZ.mjs} +365 -26
- package/dist/{token-goat-chunk-ARNRFR6M.mjs → token-goat-chunk-3W5P673V.mjs} +1 -1
- package/dist/{token-goat-chunk-YQZE5NS6.mjs → token-goat-chunk-62VJ47WN.mjs} +6 -6
- package/dist/{token-goat-chunk-CTOYJ232.mjs → token-goat-chunk-7DGTVBWP.mjs} +45 -8
- package/dist/{token-goat-chunk-5MMJAJF3.mjs → token-goat-chunk-AVRD7SBO.mjs} +60 -8
- package/dist/{token-goat-chunk-7VLWNQDQ.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-6LGVCCMY.mjs → token-goat-chunk-DA6TIDLI.mjs} +21 -16
- package/dist/{token-goat-chunk-UENEL6KX.mjs → token-goat-chunk-DHIXE6GV.mjs} +2 -2
- package/dist/{token-goat-chunk-W3E65QOW.mjs → token-goat-chunk-EP7TCWHM.mjs} +304 -46
- package/dist/{token-goat-chunk-HIZXQVNU.mjs → token-goat-chunk-EXLLLIC3.mjs} +4 -4
- package/dist/{token-goat-chunk-H3FONFBO.mjs → token-goat-chunk-F24NE3FO.mjs} +3 -3
- package/dist/{token-goat-chunk-LA3O4JGD.mjs → token-goat-chunk-FJZ5MZK7.mjs} +3 -3
- package/dist/{token-goat-chunk-BFEWOMA3.mjs → token-goat-chunk-FQYMZASI.mjs} +632 -352
- package/dist/{token-goat-chunk-OWSJJY5R.mjs → token-goat-chunk-FX5DHKGF.mjs} +5 -5
- package/dist/{token-goat-chunk-K4DG6EEF.mjs → token-goat-chunk-GYC34EGE.mjs} +7 -8
- package/dist/{token-goat-chunk-WFX2V3OF.mjs → token-goat-chunk-HPMMLV37.mjs} +358 -116
- package/dist/{token-goat-chunk-I67CPTDD.mjs → token-goat-chunk-IBTFGZBX.mjs} +19 -14
- package/dist/{token-goat-chunk-L7VCX33I.mjs → token-goat-chunk-JNIFSVWM.mjs} +36 -10
- package/dist/{token-goat-chunk-EPXNOKIV.mjs → token-goat-chunk-KWM6KBPA.mjs} +2 -2
- package/dist/{token-goat-chunk-BWCRC23L.mjs → token-goat-chunk-L2UOJBV4.mjs} +21 -20
- package/dist/{token-goat-chunk-52IMXJ2L.mjs → token-goat-chunk-LACH3WKS.mjs} +4 -4
- package/dist/token-goat-chunk-MU6I4IDI.mjs +337 -0
- package/dist/{token-goat-chunk-GRI6HWX3.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-34OUJ2IE.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-QML7ITHJ.mjs → token-goat-chunk-PIAESKVU.mjs} +139 -23
- package/dist/{token-goat-chunk-HPOFR6QX.mjs → token-goat-chunk-S7RFIX4Y.mjs} +180 -50
- package/dist/{token-goat-chunk-CA6V36KW.mjs → token-goat-chunk-SOKAPOOC.mjs} +23 -4
- package/dist/token-goat-chunk-VJQBYSXG.mjs +20 -0
- package/dist/{token-goat-chunk-LTYWLKAG.mjs → token-goat-chunk-WKO5TVNA.mjs} +922 -611
- package/dist/{token-goat-chunk-QIDLJGXZ.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 +2 -1
- package/docs/security.md +1 -1
- package/package.json +1 -1
- package/dist/token-goat-chunk-2KTZ6J7T.mjs +0 -173
- package/dist/token-goat-chunk-RHHFEXKF.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",
|
|
@@ -953,7 +969,7 @@ init_define_import_meta_env();
|
|
|
953
969
|
var TRACKED_SKILL = "token-goat";
|
|
954
970
|
var MAX_COMMANDS_SHOWN = 8;
|
|
955
971
|
async function currentCommandNames() {
|
|
956
|
-
const { buildProgram } = await import("./token-goat-chunk-
|
|
972
|
+
const { buildProgram } = await import("./token-goat-chunk-L2UOJBV4.mjs");
|
|
957
973
|
return flattenCommandNames(buildCommandManifest(buildProgram()));
|
|
958
974
|
}
|
|
959
975
|
async function recordSkillVersionSnapshot(sessionId, skillName) {
|
|
@@ -1259,7 +1275,8 @@ function reconcileNote(cwd, indexed) {
|
|
|
1259
1275
|
if (drifted > 0) clauses.push(`${countNoun(drifted, "file")} that changed outside this session${breakdown}`);
|
|
1260
1276
|
if (upgraded > 0) clauses.push(`${countNoun(upgraded, "file")} unchanged on disk but indexed by an older version of token-goat`);
|
|
1261
1277
|
return `token-goat: reindexing ${clauses.join(" and ")}${truncated}. Symbol lookups may be briefly stale.`;
|
|
1262
|
-
} catch {
|
|
1278
|
+
} catch (e) {
|
|
1279
|
+
recordStat("reconcile_note_failed", 0, 0, void 0, extractErrorMessage(e));
|
|
1263
1280
|
return null;
|
|
1264
1281
|
}
|
|
1265
1282
|
}
|
|
@@ -1870,11 +1887,11 @@ function mapInner(r, f) {
|
|
|
1870
1887
|
function mapOuter(r, f) {
|
|
1871
1888
|
return r.matched ? f(r) : r;
|
|
1872
1889
|
}
|
|
1873
|
-
function ab(pa, pb,
|
|
1874
|
-
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)));
|
|
1875
1892
|
}
|
|
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) =>
|
|
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))));
|
|
1878
1895
|
}
|
|
1879
1896
|
function ahead(p) {
|
|
1880
1897
|
return (data, i) => mapOuter(p(data, i), (m1) => ({
|
|
@@ -5851,8 +5868,8 @@ function trimCharacterEnd(str, char) {
|
|
|
5851
5868
|
function unicodeEscape(str) {
|
|
5852
5869
|
return str.replace(/[\s\S]/g, (c) => "\\u" + c.charCodeAt().toString(16).padStart(4, "0"));
|
|
5853
5870
|
}
|
|
5854
|
-
function get(obj,
|
|
5855
|
-
for (const key of
|
|
5871
|
+
function get(obj, path6) {
|
|
5872
|
+
for (const key of path6) {
|
|
5856
5873
|
if (!obj) {
|
|
5857
5874
|
return void 0;
|
|
5858
5875
|
}
|
|
@@ -7053,8 +7070,8 @@ function withBrackets(str, brackets) {
|
|
|
7053
7070
|
const rbr = typeof brackets[1] === "string" ? brackets[1] : "]";
|
|
7054
7071
|
return lbr + str + rbr;
|
|
7055
7072
|
}
|
|
7056
|
-
function pathRewrite(
|
|
7057
|
-
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;
|
|
7058
7075
|
return modifiedPath[0] === "/" && baseUrl ? trimCharacterEnd(baseUrl, "/") + modifiedPath : modifiedPath;
|
|
7059
7076
|
}
|
|
7060
7077
|
function formatImage(elem, walk, builder, formatOptions) {
|
|
@@ -7378,9 +7395,9 @@ function handleDeprecatedOptions(options) {
|
|
|
7378
7395
|
options.selectors.push(...tagDefinitions);
|
|
7379
7396
|
options.selectors = mergeDuplicatesPreferLast(options.selectors, ((s) => s.selector));
|
|
7380
7397
|
}
|
|
7381
|
-
function set(obj,
|
|
7382
|
-
const valueKey =
|
|
7383
|
-
for (const key of
|
|
7398
|
+
function set(obj, path6, value) {
|
|
7399
|
+
const valueKey = path6.pop();
|
|
7400
|
+
for (const key of path6) {
|
|
7384
7401
|
let nested = obj[key];
|
|
7385
7402
|
if (!nested) {
|
|
7386
7403
|
nested = {};
|
|
@@ -7670,20 +7687,147 @@ registerHook("post_tool_use", postSkillHandler, { toolName: "Skill" });
|
|
|
7670
7687
|
|
|
7671
7688
|
// src/hooks_bash.ts
|
|
7672
7689
|
init_define_import_meta_env();
|
|
7673
|
-
import { statSync as statSync6, existsSync } from "node:fs";
|
|
7674
7690
|
|
|
7675
|
-
// 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
|
|
7676
7767
|
init_define_import_meta_env();
|
|
7677
|
-
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";
|
|
7678
7817
|
|
|
7679
7818
|
// src/bash_extractors.ts
|
|
7680
7819
|
init_define_import_meta_env();
|
|
7681
|
-
import { statSync as
|
|
7682
|
-
function
|
|
7683
|
-
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.";
|
|
7684
7828
|
}
|
|
7685
7829
|
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.' :
|
|
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.';
|
|
7687
7831
|
}
|
|
7688
7832
|
function splitShellSegments(cmd) {
|
|
7689
7833
|
const segments = [];
|
|
@@ -7782,7 +7926,7 @@ var PS_FILE_METHOD_RE = /\[(?:System\.)?IO\.File\]::(?:ReadAllText|ReadAllLines|
|
|
|
7782
7926
|
var PS_TEMP_READ_FLOOD_BYTES = 16 * 1024;
|
|
7783
7927
|
function isLargeFileOnDisk(filePath, floor) {
|
|
7784
7928
|
try {
|
|
7785
|
-
return
|
|
7929
|
+
return statSync6(filePath).size >= floor;
|
|
7786
7930
|
} catch {
|
|
7787
7931
|
return false;
|
|
7788
7932
|
}
|
|
@@ -8215,27 +8359,21 @@ function extractLineRangeReadsCompound(cmd) {
|
|
|
8215
8359
|
}
|
|
8216
8360
|
return [...merged.values()];
|
|
8217
8361
|
}
|
|
8218
|
-
function sedRangeHint(filePath, ranges, tool) {
|
|
8219
|
-
|
|
8220
|
-
const rangeReads = ranges.map(([s, e]) => '`token-goat read "' + filePath + "@" + s + "-" + e + '"`');
|
|
8221
|
-
const allReads = rangeReads.length === 2 ? rangeReads.join(" and ") : rangeReads.length >= 3 ? rangeReads.slice(0, -1).join(", ") + ", and " + rangeReads[rangeReads.length - 1] : rangeReads[0];
|
|
8222
|
-
const prefix = "`" + (tool === "awk" ? "awk" : "sed -n") + "` line-range reads bypass read hooks. ";
|
|
8223
|
-
if (lang === "markdown") {
|
|
8224
|
-
return prefix + 'For Markdown, `token-goat section "' + filePath + '::<heading>"` extracts a whole section by name (robust to line shifts); or ' + allReads + " for exactly those lines.";
|
|
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.";
|
|
8362
|
+
function sedRangeHint(filePath, ranges, tool, sub) {
|
|
8363
|
+
return "`" + (tool === "awk" ? "awk" : "sed -n") + "` line-range reads bypass read hooks. " + substituteSentence(filePath, ranges, sub);
|
|
8233
8364
|
}
|
|
8234
|
-
function
|
|
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.";
|
|
8370
|
+
}
|
|
8371
|
+
function leadingLinesHint(lead, hintPath, start, end, preHookCwd, substitute) {
|
|
8235
8372
|
const key = resolveIndexPath(hintPath, preHookCwd ?? process.cwd());
|
|
8236
8373
|
const prior = findRangeOverlap(getFileLineRanges(key), start, end);
|
|
8237
8374
|
if (prior !== null) return sedOverlapHint(hintPath, prior, start, end);
|
|
8238
|
-
|
|
8375
|
+
if (substitute === null) return null;
|
|
8376
|
+
return lead + substituteSentence(hintPath, [[start, end]], substitute);
|
|
8239
8377
|
}
|
|
8240
8378
|
function findRangeOverlap(prior, start, end) {
|
|
8241
8379
|
let best = null;
|
|
@@ -8379,7 +8517,7 @@ function extractTasksOutput(cmd) {
|
|
|
8379
8517
|
return null;
|
|
8380
8518
|
}
|
|
8381
8519
|
function extractToolResultsFile(cmd) {
|
|
8382
|
-
const toolResultsRe =
|
|
8520
|
+
const toolResultsRe = new RegExp("[/\\\\]tool-results[/\\\\](" + TOOL_RESULTS_ID_CHARS + ")\\.txt$", "i");
|
|
8383
8521
|
const catM = /^cat(?:\s+(?:-[a-zA-Z]+|--[a-zA-Z-]+))*\s+(?:"([^"]+)"|'([^']+)'|(\S+))\s*$/.exec(cmd);
|
|
8384
8522
|
if (catM) {
|
|
8385
8523
|
const fp = catM[1] ?? catM[2] ?? catM[3];
|
|
@@ -8733,7 +8871,43 @@ function extractTerminalXmlParsing(cmd) {
|
|
|
8733
8871
|
return null;
|
|
8734
8872
|
}
|
|
8735
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
|
+
|
|
8736
8908
|
// src/hooks_bash_commands.ts
|
|
8909
|
+
init_define_import_meta_env();
|
|
8910
|
+
import { statSync as statSync8 } from "node:fs";
|
|
8737
8911
|
var CD_PREFIX_RE = /^(?:cd\s+(?:"[^"]*"|'[^']*'|\S+)[ \t]*(?:&&|;|\r?\n)\s*)+/;
|
|
8738
8912
|
function stripCdPrefix(cmd) {
|
|
8739
8913
|
const stripped = cmd.replace(CD_PREFIX_RE, "");
|
|
@@ -8949,7 +9123,7 @@ function workingTreeStatusPaths(gitDir) {
|
|
|
8949
9123
|
function enqueueRewrittenPath(absPath) {
|
|
8950
9124
|
if (/(?:^|[/\\])(?:NUL|nul)$/.test(absPath) || absPath.replace(/\\/g, "/").endsWith("/dev/null")) return;
|
|
8951
9125
|
try {
|
|
8952
|
-
if (!
|
|
9126
|
+
if (!statSync8(absPath).isFile()) return;
|
|
8953
9127
|
} catch {
|
|
8954
9128
|
return;
|
|
8955
9129
|
}
|
|
@@ -9014,6 +9188,15 @@ function pipelineShapeFilter(cmd, cwd) {
|
|
|
9014
9188
|
const detected = detectFromCommand(stripOutputPipeline(cmd), cwd ?? void 0);
|
|
9015
9189
|
return detected === null ? null : detected.filter;
|
|
9016
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
|
+
}
|
|
9017
9200
|
function pureFileReadPath(cmd) {
|
|
9018
9201
|
const single = extractCatFile(cmd)?.filePath ?? extractHeadFile(cmd)?.filePath ?? extractTailFile(cmd)?.filePath ?? extractLineRangeRead(cmd)?.filePath;
|
|
9019
9202
|
if (single !== void 0) return single;
|
|
@@ -9218,6 +9401,7 @@ function detectUnbalancedShellSyntax(cmd) {
|
|
|
9218
9401
|
function maybeCompressRewrite(event, rawCmd, cmd) {
|
|
9219
9402
|
if (process.env["TOKEN_GOAT_BASH_COMPRESS"] === "0") return null;
|
|
9220
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;
|
|
9221
9405
|
let cfg;
|
|
9222
9406
|
try {
|
|
9223
9407
|
cfg = loadConfig().bash_compress;
|
|
@@ -9297,7 +9481,7 @@ function foldShellReadStructure(cmd, filePath, output, fileKey, cwd) {
|
|
|
9297
9481
|
const originalBytes = Buffer.byteLength(output, "utf-8");
|
|
9298
9482
|
let onDisk;
|
|
9299
9483
|
try {
|
|
9300
|
-
onDisk =
|
|
9484
|
+
onDisk = statSync9(fileKey).size;
|
|
9301
9485
|
} catch {
|
|
9302
9486
|
return null;
|
|
9303
9487
|
}
|
|
@@ -9405,6 +9589,7 @@ async function maybeElideServedGenericOutput(cmd, output, exitCode, cwd, cacheMi
|
|
|
9405
9589
|
async function maybeCompressCompoundOutput(cmd, output, exitCode, cwd, cacheMinBytes, isUnwrapped = false) {
|
|
9406
9590
|
if (process.env["TOKEN_GOAT_BASH_COMPRESS"] === "0") return null;
|
|
9407
9591
|
if (!isUnwrapped && isCompressibleSingleCommand(cmd)) return null;
|
|
9592
|
+
if (isFullRecallCommand(cmd)) return null;
|
|
9408
9593
|
if (exitCode !== null && exitCode !== 0) return null;
|
|
9409
9594
|
let cfg;
|
|
9410
9595
|
try {
|
|
@@ -9423,7 +9608,7 @@ async function maybeCompressCompoundOutput(cmd, output, exitCode, cwd, cacheMinB
|
|
|
9423
9608
|
});
|
|
9424
9609
|
const minNet = resolveMinNetSavingsBytes();
|
|
9425
9610
|
if (!compressed.worthApplying(minNet)) return null;
|
|
9426
|
-
const id =
|
|
9611
|
+
const id = bashOutputIdSync(cmd, output, cwd);
|
|
9427
9612
|
const { body: untrusted, notices } = splitOwnTrailingNotices(compressed.text);
|
|
9428
9613
|
const marker = compressed.withMarker(minNet).slice(compressed.text.length);
|
|
9429
9614
|
const body = fenceUntrusted(untrusted, UNTRUSTED_TOOL_TAG) + notices + marker + "\n[token-goat] full output: bash-output " + id + " --full";
|
|
@@ -9502,6 +9687,15 @@ function preBashHandlerInner(event) {
|
|
|
9502
9687
|
const cdStripped = cmd !== rawCmd;
|
|
9503
9688
|
const preHookCwd = getCwd(event) ?? null;
|
|
9504
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
|
+
};
|
|
9505
9699
|
const cfg = loadConfig();
|
|
9506
9700
|
if (cfg.hints.warn_unbalanced_shell_quoting) {
|
|
9507
9701
|
const quoteError = detectUnbalancedShellSyntax(cmd);
|
|
@@ -9562,17 +9756,19 @@ function preBashHandlerInner(event) {
|
|
|
9562
9756
|
const { filePath, toolOrScript } = terminalXml;
|
|
9563
9757
|
recordStat("session_hint", 0, 0);
|
|
9564
9758
|
const target = filePath ? displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath) : "<file>";
|
|
9565
|
-
return
|
|
9759
|
+
return pathHint(
|
|
9760
|
+
filePath ? [target] : [],
|
|
9566
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}).`
|
|
9567
9762
|
);
|
|
9568
9763
|
}
|
|
9569
9764
|
const singleLineRangeRead = extractLineRangeRead(cmd);
|
|
9570
9765
|
const sedReads = singleLineRangeRead !== null ? [singleLineRangeRead] : extractLineRangeReadsCompound(cmd);
|
|
9571
9766
|
if (sedReads !== null) {
|
|
9572
|
-
recordStat("session_hint", 0, 0);
|
|
9573
9767
|
const hints = [];
|
|
9768
|
+
const hintPaths = [];
|
|
9574
9769
|
for (const { filePath, ranges, tool } of sedReads) {
|
|
9575
9770
|
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
|
|
9771
|
+
hintPaths.push(hintPath);
|
|
9576
9772
|
const sedDedupKey = resolveIndexPath(hintPath, preHookCwd ?? process.cwd());
|
|
9577
9773
|
const overlapHints = [];
|
|
9578
9774
|
const freshRanges = [];
|
|
@@ -9586,23 +9782,30 @@ function preBashHandlerInner(event) {
|
|
|
9586
9782
|
}
|
|
9587
9783
|
}
|
|
9588
9784
|
hints.push(...overlapHints);
|
|
9589
|
-
|
|
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
|
+
}
|
|
9590
9789
|
}
|
|
9591
|
-
|
|
9790
|
+
if (hints.length === 0) return declineUnpriced(hintPaths);
|
|
9791
|
+
recordStat("session_hint", 0, 0);
|
|
9792
|
+
return pathHint(hintPaths, hints.join(" "));
|
|
9592
9793
|
}
|
|
9593
9794
|
const gcTailResult = extractGetContentTail(cmd);
|
|
9594
9795
|
if (gcTailResult !== null) {
|
|
9595
9796
|
const { filePath, isDoc, isConfig, isSql, isXml } = gcTailResult;
|
|
9596
9797
|
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
|
|
9597
9798
|
recordStat("session_hint", 0, 0);
|
|
9598
|
-
return
|
|
9799
|
+
return pathHint(hintPath, "`Get-Content -Tail` bypasses read hooks. " + surgicalHintForConfigDoc(hintPath, isConfig, isDoc, isSql, isXml));
|
|
9599
9800
|
}
|
|
9600
9801
|
const gcSelectResult = extractGetContentSelectFirst(cmd);
|
|
9601
9802
|
if (gcSelectResult !== null) {
|
|
9602
|
-
const { filePath,
|
|
9803
|
+
const { filePath, n } = gcSelectResult;
|
|
9603
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);
|
|
9604
9807
|
recordStat("session_hint", 0, 0);
|
|
9605
|
-
return
|
|
9808
|
+
return pathHint(hintPath, gcSelectHint);
|
|
9606
9809
|
}
|
|
9607
9810
|
const catJsonPipe = extractCatJsonPipe(cmd);
|
|
9608
9811
|
if (catJsonPipe !== null) {
|
|
@@ -9610,7 +9813,8 @@ function preBashHandlerInner(event) {
|
|
|
9610
9813
|
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
|
|
9611
9814
|
recordStat("session_hint", 0, 0);
|
|
9612
9815
|
const label = isDirectJq ? "`jq`" : "`cat | jq`";
|
|
9613
|
-
return
|
|
9816
|
+
return pathHint(
|
|
9817
|
+
hintPath,
|
|
9614
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.'
|
|
9615
9819
|
);
|
|
9616
9820
|
}
|
|
@@ -9619,7 +9823,8 @@ function preBashHandlerInner(event) {
|
|
|
9619
9823
|
recordStat("session_hint", 0, 0);
|
|
9620
9824
|
if (psJsonPipe.filePath) {
|
|
9621
9825
|
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, psJsonPipe.filePath, hintCwd) : psJsonPipe.filePath);
|
|
9622
|
-
return
|
|
9826
|
+
return pathHint(
|
|
9827
|
+
hintPath,
|
|
9623
9828
|
'PowerShell `ConvertFrom-Json` pipeline detected. Use `token-goat json-query "' + hintPath + '" "<selector>"` to extract fields directly without shell conversion scripts.'
|
|
9624
9829
|
);
|
|
9625
9830
|
}
|
|
@@ -9633,12 +9838,13 @@ function preBashHandlerInner(event) {
|
|
|
9633
9838
|
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
|
|
9634
9839
|
recordStat("session_hint", 0, 0);
|
|
9635
9840
|
if (isSql) {
|
|
9636
|
-
return
|
|
9841
|
+
return pathHint(
|
|
9842
|
+
hintPath,
|
|
9637
9843
|
"`" + cmd0 + '` loads the entire file into context. Use `token-goat section "' + hintPath + '::table_name"` to pull one CREATE TABLE / CREATE TYPE block.'
|
|
9638
9844
|
);
|
|
9639
9845
|
}
|
|
9640
|
-
const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc, isXml);
|
|
9641
|
-
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);
|
|
9642
9848
|
}
|
|
9643
9849
|
const catMulti = extractCatFilesMulti(cmd);
|
|
9644
9850
|
if (catMulti !== null) {
|
|
@@ -9650,7 +9856,7 @@ function preBashHandlerInner(event) {
|
|
|
9650
9856
|
return " " + hintPath + " -> `" + how + "`";
|
|
9651
9857
|
});
|
|
9652
9858
|
const msg = "`" + cmd0 + "` on multiple files loads them all into context. Read each surgically instead:\n" + perPath.join("\n");
|
|
9653
|
-
return cdStripped ?
|
|
9859
|
+
return cdStripped ? pathHint(catMulti.map((m) => displaySafePath(resolveCdHintPath(rawCmd, m.filePath, hintCwd))), msg) : denyOutput(msg);
|
|
9654
9860
|
}
|
|
9655
9861
|
const psGetContentResult = extractPowerShellWrappedGetContent(cmd, event);
|
|
9656
9862
|
if (psGetContentResult !== null) {
|
|
@@ -9659,10 +9865,10 @@ function preBashHandlerInner(event) {
|
|
|
9659
9865
|
recordStat("session_hint", 0, 0);
|
|
9660
9866
|
const lead = "`Get-Content` via a `powershell -Command` wrapper bypasses read hooks and loads the entire file into context. ";
|
|
9661
9867
|
if (isSql) {
|
|
9662
|
-
return
|
|
9868
|
+
return pathHint(hintPath, lead + 'Use `token-goat section "' + hintPath + '::table_name"` to pull one CREATE TABLE / CREATE TYPE block.');
|
|
9663
9869
|
}
|
|
9664
|
-
const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc, isXml);
|
|
9665
|
-
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);
|
|
9666
9872
|
}
|
|
9667
9873
|
const wslCatResult = extractWslCatFile(cmd);
|
|
9668
9874
|
if (wslCatResult !== null) {
|
|
@@ -9670,12 +9876,13 @@ function preBashHandlerInner(event) {
|
|
|
9670
9876
|
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
|
|
9671
9877
|
recordStat("session_hint", 0, 0);
|
|
9672
9878
|
if (isSql) {
|
|
9673
|
-
return
|
|
9879
|
+
return pathHint(
|
|
9880
|
+
hintPath,
|
|
9674
9881
|
'`cat` loads the entire file into context. Use `token-goat section "' + hintPath + '::table_name"` to pull one CREATE TABLE / CREATE TYPE block.'
|
|
9675
9882
|
);
|
|
9676
9883
|
}
|
|
9677
|
-
const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc, isXml);
|
|
9678
|
-
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);
|
|
9679
9886
|
}
|
|
9680
9887
|
const pyRead = extractPythonFileRead(cmd);
|
|
9681
9888
|
if (pyRead !== null) {
|
|
@@ -9685,33 +9892,37 @@ function preBashHandlerInner(event) {
|
|
|
9685
9892
|
if (isOutputFile) {
|
|
9686
9893
|
if (taskOutputIsJsonlTranscript(filePath)) {
|
|
9687
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.';
|
|
9688
|
-
return cdStripped ?
|
|
9895
|
+
return cdStripped ? pathHint(hintPath, tHint) : denyOutput(tHint);
|
|
9689
9896
|
}
|
|
9690
|
-
return
|
|
9897
|
+
return pathHint(
|
|
9898
|
+
hintPath,
|
|
9691
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.'
|
|
9692
9900
|
);
|
|
9693
9901
|
}
|
|
9694
9902
|
if (isSql) {
|
|
9695
|
-
return
|
|
9903
|
+
return pathHint(
|
|
9904
|
+
hintPath,
|
|
9696
9905
|
'Python `open()` file reads bypass read hooks. Use `token-goat section "' + hintPath + '::table_name"` to pull one CREATE TABLE / CREATE TYPE block.'
|
|
9697
9906
|
);
|
|
9698
9907
|
}
|
|
9699
|
-
const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc, isXml);
|
|
9700
|
-
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);
|
|
9701
9910
|
}
|
|
9702
9911
|
const tailResult = extractTailFile(cmd);
|
|
9703
9912
|
if (tailResult !== null) {
|
|
9704
9913
|
const { filePath, isDoc, isConfig, isSql, isXml } = tailResult;
|
|
9705
9914
|
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
|
|
9706
9915
|
recordStat("session_hint", 0, 0);
|
|
9707
|
-
return
|
|
9916
|
+
return pathHint(hintPath, "`tail` bypasses read hooks. " + surgicalHintForConfigDoc(hintPath, isConfig, isDoc, isSql, isXml));
|
|
9708
9917
|
}
|
|
9709
9918
|
const headResult = extractHeadFile(cmd);
|
|
9710
9919
|
if (headResult !== null) {
|
|
9711
|
-
const { filePath,
|
|
9920
|
+
const { filePath, n } = headResult;
|
|
9712
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);
|
|
9713
9924
|
recordStat("session_hint", 0, 0);
|
|
9714
|
-
return
|
|
9925
|
+
return pathHint(hintPath, headHint);
|
|
9715
9926
|
}
|
|
9716
9927
|
const nodeRead = extractNodeFileRead(cmd);
|
|
9717
9928
|
if (nodeRead !== null) {
|
|
@@ -9720,11 +9931,11 @@ function preBashHandlerInner(event) {
|
|
|
9720
9931
|
const lead = "Node.js `fs.readFileSync()` bypasses read hooks. ";
|
|
9721
9932
|
if (isSql) {
|
|
9722
9933
|
recordStat("session_hint", 0, 0);
|
|
9723
|
-
return
|
|
9934
|
+
return pathHint(hintPath, lead + 'Use `token-goat section "' + hintPath + '::table_name"` to pull one CREATE TABLE / CREATE TYPE block.');
|
|
9724
9935
|
}
|
|
9725
|
-
const hint =
|
|
9936
|
+
const hint = surgicalHintFor(hintPath, false, isConfig, isDoc, false, runnableTargetFor(hintPath, hintCwd, event));
|
|
9726
9937
|
recordStat("session_hint", 0, 0);
|
|
9727
|
-
return cdStripped ?
|
|
9938
|
+
return cdStripped ? pathHint(hintPath, lead + hint) : denyOutput(lead + hint);
|
|
9728
9939
|
}
|
|
9729
9940
|
const psMethodRead = extractPowerShellFileMethodRead(cmd, event);
|
|
9730
9941
|
if (psMethodRead !== null) {
|
|
@@ -9733,11 +9944,15 @@ function preBashHandlerInner(event) {
|
|
|
9733
9944
|
const lead = "PowerShell `[IO.File]::ReadAllText()` bypasses read hooks. ";
|
|
9734
9945
|
if (isSql) {
|
|
9735
9946
|
recordStat("session_hint", 0, 0);
|
|
9736
|
-
return
|
|
9947
|
+
return pathHint(hintPath, lead + 'Use `token-goat section "' + hintPath + '::table_name"` to pull one CREATE TABLE / CREATE TYPE block.');
|
|
9737
9948
|
}
|
|
9738
|
-
const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc, isXml);
|
|
9949
|
+
const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc, isXml, runnableTargetFor(hintPath, hintCwd, event));
|
|
9739
9950
|
recordStat("session_hint", 0, 0);
|
|
9740
|
-
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 } };
|
|
9741
9956
|
}
|
|
9742
9957
|
if (extractGrepPipeChain(cmd)) {
|
|
9743
9958
|
recordStat("session_hint", 0, 0);
|
|
@@ -9750,7 +9965,8 @@ function preBashHandlerInner(event) {
|
|
|
9750
9965
|
const { filePath } = mdHeadingGrep;
|
|
9751
9966
|
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
|
|
9752
9967
|
recordStat("session_hint", 0, 0);
|
|
9753
|
-
return
|
|
9968
|
+
return pathHint(
|
|
9969
|
+
hintPath,
|
|
9754
9970
|
'Use `token-goat outline "' + hintPath + '"` to get all headings with line ranges \u2014 then `token-goat section "' + hintPath + '::Heading"` to read one section.'
|
|
9755
9971
|
);
|
|
9756
9972
|
}
|
|
@@ -9759,7 +9975,8 @@ function preBashHandlerInner(event) {
|
|
|
9759
9975
|
const { identifier } = rgSymbol;
|
|
9760
9976
|
recordStat("session_hint", 0, 0);
|
|
9761
9977
|
return contextOutput(
|
|
9762
|
-
"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]
|
|
9763
9980
|
);
|
|
9764
9981
|
}
|
|
9765
9982
|
const rgStructural = extractRgStructuralSearch(cmd);
|
|
@@ -9767,7 +9984,8 @@ function preBashHandlerInner(event) {
|
|
|
9767
9984
|
const { filePath } = rgStructural;
|
|
9768
9985
|
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
|
|
9769
9986
|
recordStat("session_hint", 0, 0);
|
|
9770
|
-
return
|
|
9987
|
+
return pathHint(
|
|
9988
|
+
hintPath,
|
|
9771
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.'
|
|
9772
9990
|
);
|
|
9773
9991
|
}
|
|
@@ -9799,7 +10017,7 @@ function preBashHandlerInner(event) {
|
|
|
9799
10017
|
const prevResolvedPath = prevPath !== null && commandPathIsTouchable(prevPath, event) ? resolveIndexPath(prevPath, preHookCwd ?? process.cwd()) : null;
|
|
9800
10018
|
if (prevPath !== null && prevResolvedPath !== null && !existsSync(prevResolvedPath)) {
|
|
9801
10019
|
clearCurlDownload(curlDl.url);
|
|
9802
|
-
} else if (prevPath !== null && prevResolvedPath !== null &&
|
|
10020
|
+
} else if (prevPath !== null && prevResolvedPath !== null && statSync9(prevResolvedPath).size >= loadConfig().hints.bash_dedup_min_bytes) {
|
|
9803
10021
|
recordStat("session_hint", 0, 0);
|
|
9804
10022
|
return denyOutput(
|
|
9805
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.'
|
|
@@ -9861,7 +10079,7 @@ function preBashHandlerInner(event) {
|
|
|
9861
10079
|
}
|
|
9862
10080
|
if (notes.length > 0) {
|
|
9863
10081
|
recordStat("session_hint", 0, 0);
|
|
9864
|
-
return
|
|
10082
|
+
return pathHint(tgRead.filePath !== null ? [tgRead.filePath] : [], notes.join(" "));
|
|
9865
10083
|
}
|
|
9866
10084
|
return passOutput();
|
|
9867
10085
|
}
|
|
@@ -9893,7 +10111,28 @@ function preBashHandler(event) {
|
|
|
9893
10111
|
return applyHintTracking(event, preBashHandlerInner(event), classifyBashHint);
|
|
9894
10112
|
}
|
|
9895
10113
|
registerHook("pre_tool_use", preBashHandler, { toolName: "Bash" });
|
|
9896
|
-
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
|
+
}
|
|
9897
10136
|
return extractToolResponseField(raw, OUTPUT_FIRST_TOOL_RESPONSE_KEYS);
|
|
9898
10137
|
}
|
|
9899
10138
|
function extractExitCode(raw) {
|
|
@@ -10021,7 +10260,7 @@ async function postBashHandler(event) {
|
|
|
10021
10260
|
const isUnwrapped = unwrapCompressCommand(rawCmdRaw) === null;
|
|
10022
10261
|
const rawCmd = unwrapCompressCommand(rawCmdRaw) ?? rawCmdRaw;
|
|
10023
10262
|
const cmd = stripCdPrefix(rawCmd);
|
|
10024
|
-
const output = extractBashOutput(event
|
|
10263
|
+
const output = extractBashOutput(event);
|
|
10025
10264
|
const exitCode = extractExitCode(event.raw);
|
|
10026
10265
|
const cwd = getCwd(event) ?? null;
|
|
10027
10266
|
const cacheMinBytes = loadConfig().bash_compress.cache_min_bytes;
|
|
@@ -10208,9 +10447,11 @@ function postBashOutputHandler(event) {
|
|
|
10208
10447
|
const delta = output.slice(prior.output.length);
|
|
10209
10448
|
storePollSnapshot(event.sessionId, bashId, output);
|
|
10210
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;
|
|
10211
10452
|
const deltaNotice = `[token-goat: bash_id ${bashId} delta since last poll]
|
|
10212
10453
|
`;
|
|
10213
|
-
const fenced = fenceUntrusted(
|
|
10454
|
+
const fenced = fenceUntrusted(deltaBody, UNTRUSTED_TOOL_TAG);
|
|
10214
10455
|
const body = `${deltaNotice}${fenced}`;
|
|
10215
10456
|
if (!isRewriteWorthwhile({
|
|
10216
10457
|
originalBytes: Buffer.byteLength(output, "utf-8"),
|
|
@@ -10332,7 +10573,7 @@ registerHook("post_tool_use", postTaskOutputHandler, { toolName: "TaskOutput" })
|
|
|
10332
10573
|
|
|
10333
10574
|
// src/hooks_tool_failure.ts
|
|
10334
10575
|
init_define_import_meta_env();
|
|
10335
|
-
import { existsSync as existsSync2, readFileSync as
|
|
10576
|
+
import { existsSync as existsSync2, readFileSync as readFileSync5, statSync as statSync10, writeFileSync as writeFileSync2 } from "node:fs";
|
|
10336
10577
|
import { dirname as dirname2, relative as relative2 } from "node:path";
|
|
10337
10578
|
var FAILURE_SUFFIX = ".tool-failures.json";
|
|
10338
10579
|
var MAX_TRACKED_FAILURES = 64;
|
|
@@ -10360,7 +10601,7 @@ function ledgerPath(sessionId) {
|
|
|
10360
10601
|
}
|
|
10361
10602
|
function readLedger(target) {
|
|
10362
10603
|
try {
|
|
10363
|
-
const parsed = JSON.parse(
|
|
10604
|
+
const parsed = JSON.parse(readFileSync5(target, "utf8"));
|
|
10364
10605
|
if (parsed !== null && typeof parsed === "object") {
|
|
10365
10606
|
const seen = parsed.seen;
|
|
10366
10607
|
const order = parsed.order;
|
|
@@ -10401,9 +10642,9 @@ function diagnoseEditFailure(event, errorText) {
|
|
|
10401
10642
|
const absPath = normalizePath(filePath);
|
|
10402
10643
|
if (!existsSync2(absPath)) return null;
|
|
10403
10644
|
try {
|
|
10404
|
-
const stat =
|
|
10645
|
+
const stat = statSync10(absPath);
|
|
10405
10646
|
if (stat.size > MAX_EDIT_DIAGNOSE_BYTES) return null;
|
|
10406
|
-
const fileContent =
|
|
10647
|
+
const fileContent = readFileSync5(absPath, "utf8");
|
|
10407
10648
|
const relDisplay = displaySafeText(relative2(process.cwd(), absPath).replace(/\\/g, "/") || absPath);
|
|
10408
10649
|
if (isMultiple) {
|
|
10409
10650
|
const matchLines = [];
|
|
@@ -11051,8 +11292,8 @@ The full payload was cached to prevent harness context spill. Slicing commands:
|
|
|
11051
11292
|
}
|
|
11052
11293
|
return passOrFence();
|
|
11053
11294
|
}
|
|
11054
|
-
registerHook("pre_tool_use", preMcpHandler, { toolPattern:
|
|
11055
|
-
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 });
|
|
11056
11297
|
|
|
11057
11298
|
// src/hooks_websearch.ts
|
|
11058
11299
|
init_define_import_meta_env();
|
|
@@ -11253,8 +11494,7 @@ registerHook("post_tool_use", postBrowserImageHandler, { toolPattern: "^mcp__" }
|
|
|
11253
11494
|
init_define_import_meta_env();
|
|
11254
11495
|
import { createHash as createHash2 } from "node:crypto";
|
|
11255
11496
|
import * as fs2 from "node:fs";
|
|
11256
|
-
import * as
|
|
11257
|
-
import * as path4 from "node:path";
|
|
11497
|
+
import * as path5 from "node:path";
|
|
11258
11498
|
var BRIEFING_TARGET_TOKENS = 550;
|
|
11259
11499
|
function buildSubagentBriefing() {
|
|
11260
11500
|
try {
|
|
@@ -11479,8 +11719,8 @@ function parseAgentDefinition(text, fallbackName) {
|
|
|
11479
11719
|
return { name: name2, restricted: false };
|
|
11480
11720
|
}
|
|
11481
11721
|
function findRestrictedAgentNames(roots) {
|
|
11482
|
-
const projectAgentsRoot =
|
|
11483
|
-
const scanRoots = roots ?? [
|
|
11722
|
+
const projectAgentsRoot = path5.join(process.cwd(), ".claude", "agents");
|
|
11723
|
+
const scanRoots = roots ?? [path5.join(claudeConfigDir(), "agents"), projectAgentsRoot];
|
|
11484
11724
|
const names = /* @__PURE__ */ new Set();
|
|
11485
11725
|
const visited = /* @__PURE__ */ new Set();
|
|
11486
11726
|
let filesSeen = 0;
|
|
@@ -11504,7 +11744,7 @@ function findRestrictedAgentNames(roots) {
|
|
|
11504
11744
|
}
|
|
11505
11745
|
for (const entry of entries) {
|
|
11506
11746
|
if (filesSeen >= ROSTER_WALK_MAX_FILES) return;
|
|
11507
|
-
const full =
|
|
11747
|
+
const full = path5.join(dir, entry.name);
|
|
11508
11748
|
let stat;
|
|
11509
11749
|
try {
|
|
11510
11750
|
stat = fs2.statSync(full);
|
|
@@ -11523,12 +11763,12 @@ function findRestrictedAgentNames(roots) {
|
|
|
11523
11763
|
} catch {
|
|
11524
11764
|
continue;
|
|
11525
11765
|
}
|
|
11526
|
-
const parsed = parseAgentDefinition(text,
|
|
11766
|
+
const parsed = parseAgentDefinition(text, path5.basename(entry.name, path5.extname(entry.name)));
|
|
11527
11767
|
if (parsed !== null && parsed.restricted) names.add(parsed.name);
|
|
11528
11768
|
}
|
|
11529
11769
|
};
|
|
11530
11770
|
for (const root of scanRoots) {
|
|
11531
|
-
const isProjectRoot = roots === void 0 &&
|
|
11771
|
+
const isProjectRoot = roots === void 0 && path5.resolve(root) === path5.resolve(projectAgentsRoot);
|
|
11532
11772
|
if (!isProjectRoot) {
|
|
11533
11773
|
walk(root, 0, null);
|
|
11534
11774
|
continue;
|
|
@@ -11658,11 +11898,11 @@ function safeSuggestions(output) {
|
|
|
11658
11898
|
if (output.hookType === "context") return { hookType: "context", context: stripUnsafeSuggestions(output.context) };
|
|
11659
11899
|
return output;
|
|
11660
11900
|
}
|
|
11661
|
-
async function relayInProcess(eventName, rawPayload) {
|
|
11901
|
+
async function relayInProcess(eventName, rawPayload, harnessWaitMs) {
|
|
11902
|
+
if (!isHookEventName(eventName)) {
|
|
11903
|
+
return "{}";
|
|
11904
|
+
}
|
|
11662
11905
|
try {
|
|
11663
|
-
if (!isHookEventName(eventName)) {
|
|
11664
|
-
return "{}";
|
|
11665
|
-
}
|
|
11666
11906
|
const harness = detectHarness();
|
|
11667
11907
|
const payload = eventName === "pre_tool_use" || eventName === "post_tool_use" ? normalizePayload(rawPayload, harnessForNormalization()) : rawPayload;
|
|
11668
11908
|
const event = buildEvent(eventName, payload);
|
|
@@ -11693,6 +11933,8 @@ async function relayInProcess(eventName, rawPayload) {
|
|
|
11693
11933
|
return serializeOutput(output, event.eventName, harness, event);
|
|
11694
11934
|
} catch {
|
|
11695
11935
|
return "{}";
|
|
11936
|
+
} finally {
|
|
11937
|
+
recordStat(`hook:${eventName}`, 0, 0, void 0, void 0, void 0, harnessWaitMs ?? performance.now());
|
|
11696
11938
|
}
|
|
11697
11939
|
}
|
|
11698
11940
|
async function relay(eventName) {
|