token-goat 2.6.33 → 2.6.35
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 +4 -0
- package/SECURITY.md +11 -10
- package/dist/{token-goat-chunk-FN6WLFIX.mjs → token-goat-chunk-D5IZEGN2.mjs} +817 -53
- package/dist/{token-goat-hook-chunk-HXHFLKEL.mjs → token-goat-chunk-DQIT5JIF.mjs} +7 -2
- package/dist/{token-goat-chunk-72R7M5H7.mjs → token-goat-chunk-DRAIYVUI.mjs} +16 -14
- package/dist/{token-goat-chunk-RPOXYJWK.mjs → token-goat-chunk-HW4VUKJ5.mjs} +77 -26
- package/dist/{token-goat-chunk-TDYTBCT4.mjs → token-goat-chunk-I2KCEGIP.mjs} +704 -138
- package/dist/{token-goat-chunk-WFRR2DBG.mjs → token-goat-chunk-K7W3P2TJ.mjs} +583 -343
- package/dist/{token-goat-chunk-OHQ7RBLL.mjs → token-goat-chunk-SIHYQCTM.mjs} +2 -2
- package/dist/{token-goat-chunk-77CX3MCN.mjs → token-goat-chunk-UIZVX3HN.mjs} +2 -2
- package/dist/{token-goat-chunk-L6FVTDPD.mjs → token-goat-chunk-Y2NH6DGZ.mjs} +564 -31
- package/dist/{token-goat-chunk-HPWPUBAK.mjs → token-goat-chunk-YF67EWRH.mjs} +5 -5
- package/dist/{token-goat-hook-chunk-E6AIPKNF.mjs → token-goat-hook-chunk-2O7P4Z6Q.mjs} +579 -339
- package/dist/{token-goat-hook-chunk-WBBHV6TZ.mjs → token-goat-hook-chunk-3AAH72SO.mjs} +77 -26
- package/dist/{token-goat-hook-chunk-QD2DX2U7.mjs → token-goat-hook-chunk-4POU6OHK.mjs} +564 -31
- package/dist/{token-goat-hook-chunk-ZT47M3IA.mjs → token-goat-hook-chunk-CRBMPT74.mjs} +2 -2
- package/dist/{token-goat-hook-chunk-26DLRODR.mjs → token-goat-hook-chunk-D6UJEFHX.mjs} +2 -2
- package/dist/{token-goat-hook-chunk-XBOWDNJB.mjs → token-goat-hook-chunk-J4HKWUWZ.mjs} +5 -5
- package/dist/{token-goat-chunk-RQQ4SY2P.mjs → token-goat-hook-chunk-NIPXIAPA.mjs} +7 -2
- package/dist/{token-goat-hook-chunk-VDXQXYJK.mjs → token-goat-hook-chunk-NXVT4F3S.mjs} +817 -53
- package/dist/{token-goat-hook-chunk-EKGDLI4D.mjs → token-goat-hook-chunk-VU6VGZBM.mjs} +16 -14
- package/dist/{token-goat-hook-chunk-UWV4MTYI.mjs → token-goat-hook-chunk-XM4W3WCO.mjs} +704 -138
- package/dist/token-goat-hook.mjs +5 -5
- package/dist/token-goat.core.mjs +5 -5
- package/package.json +11 -10
|
@@ -2,13 +2,15 @@ import { createRequire as __cjsRequire } from 'node:module';
|
|
|
2
2
|
const require = __cjsRequire(import.meta.url);
|
|
3
3
|
import {
|
|
4
4
|
canRunWrappedShell,
|
|
5
|
+
compressOutput,
|
|
5
6
|
dedupeConsecutive,
|
|
6
7
|
detectFromCommand,
|
|
8
|
+
filterByName,
|
|
7
9
|
hasBareBackgroundOrNewline,
|
|
8
10
|
isRewriteWorthwhile,
|
|
9
11
|
resolveMinNetSavingsBytes,
|
|
10
12
|
shlexSplit
|
|
11
|
-
} from "./token-goat-hook-chunk-
|
|
13
|
+
} from "./token-goat-hook-chunk-NIPXIAPA.mjs";
|
|
12
14
|
import {
|
|
13
15
|
BASH_OUTPUT_SUBDIR,
|
|
14
16
|
HOOK_EVENTS,
|
|
@@ -26,7 +28,7 @@ import {
|
|
|
26
28
|
storeBashOutput,
|
|
27
29
|
storeWebOutput,
|
|
28
30
|
summarizeOutputDelta
|
|
29
|
-
} from "./token-goat-hook-chunk-
|
|
31
|
+
} from "./token-goat-hook-chunk-CRBMPT74.mjs";
|
|
30
32
|
import {
|
|
31
33
|
BODY_FIRST_TOOL_RESPONSE_KEYS,
|
|
32
34
|
OUTPUT_FIRST_TOOL_RESPONSE_KEYS,
|
|
@@ -116,11 +118,12 @@ import {
|
|
|
116
118
|
wasCliReadThisSession,
|
|
117
119
|
wasFileReadThisSession,
|
|
118
120
|
wasHintShown
|
|
119
|
-
} from "./token-goat-hook-chunk-
|
|
121
|
+
} from "./token-goat-hook-chunk-XM4W3WCO.mjs";
|
|
120
122
|
import {
|
|
121
123
|
VERSION,
|
|
122
124
|
detectHarness,
|
|
123
125
|
detectLanguage,
|
|
126
|
+
displaySafePath,
|
|
124
127
|
extractErrorMessage,
|
|
125
128
|
foldPath,
|
|
126
129
|
getDb,
|
|
@@ -134,7 +137,7 @@ import {
|
|
|
134
137
|
runGit,
|
|
135
138
|
shortFingerprint,
|
|
136
139
|
toKB
|
|
137
|
-
} from "./token-goat-hook-chunk-
|
|
140
|
+
} from "./token-goat-hook-chunk-2O7P4Z6Q.mjs";
|
|
138
141
|
|
|
139
142
|
// src/hooks_grep.ts
|
|
140
143
|
function grepIntInput(toolInput, key) {
|
|
@@ -311,9 +314,9 @@ function postEditHandlerInner(event) {
|
|
|
311
314
|
} catch {
|
|
312
315
|
}
|
|
313
316
|
if (meetsSavingsFloor(editedSize)) {
|
|
314
|
-
const escapedPath = normalized.replace(/`/g, "\\`").replace(/"/g, '\\"');
|
|
317
|
+
const escapedPath = displaySafePath(normalized).replace(/`/g, "\\`").replace(/"/g, '\\"');
|
|
315
318
|
return contextOutput(
|
|
316
|
-
editedBasename + ' was edited. Use `token-goat section "' + escapedPath + '::HeadingName"` to re-read a specific section rather than the full file.'
|
|
319
|
+
displaySafePath(editedBasename) + ' was edited. Use `token-goat section "' + escapedPath + '::HeadingName"` to re-read a specific section rather than the full file.'
|
|
317
320
|
);
|
|
318
321
|
}
|
|
319
322
|
}
|
|
@@ -572,7 +575,7 @@ import crypto from "node:crypto";
|
|
|
572
575
|
var TRACKED_SKILL = "token-goat";
|
|
573
576
|
var MAX_COMMANDS_SHOWN = 8;
|
|
574
577
|
async function currentCommandNames() {
|
|
575
|
-
const { buildProgram } = await import("./token-goat-hook-chunk-
|
|
578
|
+
const { buildProgram } = await import("./token-goat-hook-chunk-3AAH72SO.mjs");
|
|
576
579
|
return flattenCommandNames(buildCommandManifest(buildProgram()));
|
|
577
580
|
}
|
|
578
581
|
async function recordSkillVersionSnapshot(sessionId, skillName) {
|
|
@@ -7051,6 +7054,10 @@ function stripCdPrefix(cmd) {
|
|
|
7051
7054
|
const stripped = cmd.replace(/^(?:cd\s+(?:"[^"]*"|'[^']*'|\S+)\s*&&\s*)+/, "");
|
|
7052
7055
|
return stripped.trim() || cmd;
|
|
7053
7056
|
}
|
|
7057
|
+
function stripTrailingStderrRedirect(cmd) {
|
|
7058
|
+
const stripped = cmd.replace(/\s*2>&1\s*$/, "");
|
|
7059
|
+
return stripped.trim() || cmd;
|
|
7060
|
+
}
|
|
7054
7061
|
function hasTestRunScopeOrBudget(cmd) {
|
|
7055
7062
|
const hasTimeout = /(?:^|\s)(?:timeout(?:\.exe)?\s+\S+|--(?:test)?timeout(?:=|\s)\S+)/i.test(cmd);
|
|
7056
7063
|
const hasSelector = /(?:\s::\S+|\s(?:-k|-t|-run|--(?:testNamePattern|last-failed|test|project))(?:=|\s)|\b(?:tests?|spec)\S*\.(?:[cm]?[jt]sx?|py|go|rs)\b|(?:^|\s)\.\/\S+)/i.test(cmd);
|
|
@@ -7142,6 +7149,182 @@ function isHeadMovingGitCommand(cmd) {
|
|
|
7142
7149
|
}
|
|
7143
7150
|
var ORIG_HEAD_ELIGIBLE_GIT_RE = /^\s*git\s+(?:pull|merge|rebase)\b/i;
|
|
7144
7151
|
var ORIG_HEAD_REFLOG_MSG_RE = /^(merge\s|rebase\s\(|pull\s)/i;
|
|
7152
|
+
var GIT_RESTORE_RE = /^\s*git\s+restore\b/i;
|
|
7153
|
+
var GIT_RESTORE_STAGED_ONLY_RE = /(?:^|\s)(?:--staged|-S)(?:\s|$)/;
|
|
7154
|
+
var GIT_STASH_APPLY_RE = /^\s*git\s+stash\s+(?:pop|apply)\b/i;
|
|
7155
|
+
var PATCH_APPLY_SEGMENT_RE = /^\s*(?:\S*[/\\])?(?:git\s+apply|patch)\b/i;
|
|
7156
|
+
var PATCH_APPLY_INSPECT_ONLY_RE = /(?:^|\s)--(?:check|stat|numstat|summary|dry-run)(?:\s|=|$)/i;
|
|
7157
|
+
function isFormatterWriteSegment(segment) {
|
|
7158
|
+
return /(?:^|\s|[/\\])(?:prettier|eslint)\b/i.test(segment) && /(?:^|\s)--(?:write|fix)(?:\s|$)/i.test(segment);
|
|
7159
|
+
}
|
|
7160
|
+
function splitShellSegments(cmd) {
|
|
7161
|
+
const segments = [];
|
|
7162
|
+
let cur = "";
|
|
7163
|
+
let quote = null;
|
|
7164
|
+
for (let i = 0; i < cmd.length; i++) {
|
|
7165
|
+
const ch = cmd[i];
|
|
7166
|
+
if (quote !== null) {
|
|
7167
|
+
cur += ch;
|
|
7168
|
+
if (ch === quote) quote = null;
|
|
7169
|
+
continue;
|
|
7170
|
+
}
|
|
7171
|
+
if (ch === '"' || ch === "'") {
|
|
7172
|
+
quote = ch;
|
|
7173
|
+
cur += ch;
|
|
7174
|
+
continue;
|
|
7175
|
+
}
|
|
7176
|
+
if (ch === "\\" && i + 1 < cmd.length) {
|
|
7177
|
+
cur += ch + cmd[i + 1];
|
|
7178
|
+
i++;
|
|
7179
|
+
continue;
|
|
7180
|
+
}
|
|
7181
|
+
if (ch === "|" || ch === ";" || ch === "\n" || ch === "&") {
|
|
7182
|
+
segments.push(cur);
|
|
7183
|
+
cur = "";
|
|
7184
|
+
if ((ch === "|" || ch === "&") && cmd[i + 1] === ch) i++;
|
|
7185
|
+
continue;
|
|
7186
|
+
}
|
|
7187
|
+
cur += ch;
|
|
7188
|
+
}
|
|
7189
|
+
segments.push(cur);
|
|
7190
|
+
return segments.map((s) => s.trim()).filter((s) => s.length > 0);
|
|
7191
|
+
}
|
|
7192
|
+
function safeShlexSplit(segment) {
|
|
7193
|
+
try {
|
|
7194
|
+
return shlexSplit(segment);
|
|
7195
|
+
} catch {
|
|
7196
|
+
return null;
|
|
7197
|
+
}
|
|
7198
|
+
}
|
|
7199
|
+
function segmentCommandIs(segment, name2) {
|
|
7200
|
+
return new RegExp("^\\s*(?:\\S*[/\\\\])?" + name2 + "(?:\\.exe)?\\b", "i").test(segment);
|
|
7201
|
+
}
|
|
7202
|
+
function extractSedInPlaceFiles(segment) {
|
|
7203
|
+
if (!segmentCommandIs(segment, "sed")) return [];
|
|
7204
|
+
const tokens = safeShlexSplit(segment);
|
|
7205
|
+
if (tokens === null) return [];
|
|
7206
|
+
const rest = tokens.slice(1);
|
|
7207
|
+
const inPlaceIdx = rest.findIndex((t) => t === "--in-place" || t.startsWith("--in-place=") || /^-[a-zA-Z]*i/.test(t));
|
|
7208
|
+
if (inPlaceIdx === -1) return [];
|
|
7209
|
+
const files = [];
|
|
7210
|
+
let scriptTaken = rest.some((t) => t === "-e" || t === "-f" || t.startsWith("--expression") || t.startsWith("--file"));
|
|
7211
|
+
for (let i = 0; i < rest.length; i++) {
|
|
7212
|
+
const t = rest[i];
|
|
7213
|
+
if (i === inPlaceIdx) {
|
|
7214
|
+
if (t === "-i" && rest[i + 1] === "") i++;
|
|
7215
|
+
continue;
|
|
7216
|
+
}
|
|
7217
|
+
if (t === "-e" || t === "-f" || t === "--expression" || t === "--file") {
|
|
7218
|
+
i++;
|
|
7219
|
+
continue;
|
|
7220
|
+
}
|
|
7221
|
+
if (t.length > 1 && t.startsWith("-")) continue;
|
|
7222
|
+
if (!scriptTaken) {
|
|
7223
|
+
scriptTaken = true;
|
|
7224
|
+
continue;
|
|
7225
|
+
}
|
|
7226
|
+
files.push(t);
|
|
7227
|
+
}
|
|
7228
|
+
return files;
|
|
7229
|
+
}
|
|
7230
|
+
function extractTeeFiles(segment) {
|
|
7231
|
+
if (!segmentCommandIs(segment, "tee")) return [];
|
|
7232
|
+
const tokens = safeShlexSplit(segment);
|
|
7233
|
+
if (tokens === null) return [];
|
|
7234
|
+
return tokens.slice(1).filter((t) => t.length > 0 && !t.startsWith("-"));
|
|
7235
|
+
}
|
|
7236
|
+
function extractRedirectTargets(segment) {
|
|
7237
|
+
const targets = [];
|
|
7238
|
+
const re = /(?:^|\s)[0-9]?>>?\s*("[^"]*"|'[^']*'|[^\s|&<>]+)/g;
|
|
7239
|
+
let m;
|
|
7240
|
+
while ((m = re.exec(segment)) !== null) {
|
|
7241
|
+
const raw = m[1];
|
|
7242
|
+
targets.push(raw.length >= 2 && (raw[0] === '"' || raw[0] === "'") ? raw.slice(1, -1) : raw);
|
|
7243
|
+
}
|
|
7244
|
+
return targets;
|
|
7245
|
+
}
|
|
7246
|
+
function extractGitRestorePathspecs(cmd) {
|
|
7247
|
+
const m = /^\s*git\s+restore\b(.*)$/i.exec(cmd);
|
|
7248
|
+
if (m === null) return [];
|
|
7249
|
+
const tokens = safeShlexSplit(m[1] ?? "");
|
|
7250
|
+
if (tokens === null) return [];
|
|
7251
|
+
const dashDash = tokens.indexOf("--");
|
|
7252
|
+
if (dashDash !== -1) return tokens.slice(dashDash + 1);
|
|
7253
|
+
const specs = [];
|
|
7254
|
+
let skipNext = false;
|
|
7255
|
+
for (const t of tokens) {
|
|
7256
|
+
if (skipNext) {
|
|
7257
|
+
skipNext = false;
|
|
7258
|
+
continue;
|
|
7259
|
+
}
|
|
7260
|
+
if (t === "-s" || t === "--source") {
|
|
7261
|
+
skipNext = true;
|
|
7262
|
+
continue;
|
|
7263
|
+
}
|
|
7264
|
+
if (t.startsWith("-")) continue;
|
|
7265
|
+
specs.push(t);
|
|
7266
|
+
}
|
|
7267
|
+
return specs;
|
|
7268
|
+
}
|
|
7269
|
+
function gitRepoRoot(gitDir) {
|
|
7270
|
+
const toplevel = runGit(["rev-parse", "--show-toplevel"], { cwd: gitDir, timeoutMs: 5e3 });
|
|
7271
|
+
return toplevel.exitCode === 0 && toplevel.stdout.trim() !== "" ? toplevel.stdout.trim() : gitDir;
|
|
7272
|
+
}
|
|
7273
|
+
function expandGitPathspecs(specs, gitDir) {
|
|
7274
|
+
if (specs.length === 0) return [];
|
|
7275
|
+
const listed = runGit(["ls-files", "--full-name", "--", ...specs], { cwd: gitDir, timeoutMs: 5e3 });
|
|
7276
|
+
if (listed.exitCode !== 0) return [];
|
|
7277
|
+
const repoRoot = gitRepoRoot(gitDir);
|
|
7278
|
+
return listed.stdout.split("\n").map((l) => l.trim()).filter((l) => l.length > 0).map((rel) => resolveIndexPath(rel, repoRoot));
|
|
7279
|
+
}
|
|
7280
|
+
function workingTreeStatusPaths(gitDir) {
|
|
7281
|
+
const status = runGit(["status", "--porcelain"], { cwd: gitDir, timeoutMs: 5e3 });
|
|
7282
|
+
if (status.exitCode !== 0) return [];
|
|
7283
|
+
const repoRoot = gitRepoRoot(gitDir);
|
|
7284
|
+
const out = [];
|
|
7285
|
+
for (const line of status.stdout.split("\n")) {
|
|
7286
|
+
if (line.length < 4) continue;
|
|
7287
|
+
let rel = line.slice(3).trim();
|
|
7288
|
+
if (rel.length === 0) continue;
|
|
7289
|
+
const arrow = rel.lastIndexOf(" -> ");
|
|
7290
|
+
if (arrow !== -1) rel = rel.slice(arrow + 4);
|
|
7291
|
+
if (rel.startsWith('"')) {
|
|
7292
|
+
try {
|
|
7293
|
+
rel = JSON.parse(rel);
|
|
7294
|
+
} catch {
|
|
7295
|
+
continue;
|
|
7296
|
+
}
|
|
7297
|
+
}
|
|
7298
|
+
out.push(resolveIndexPath(rel, repoRoot));
|
|
7299
|
+
}
|
|
7300
|
+
return out;
|
|
7301
|
+
}
|
|
7302
|
+
function enqueueRewrittenPath(absPath) {
|
|
7303
|
+
if (/(?:^|[/\\])(?:NUL|nul)$/.test(absPath) || absPath.replace(/\\/g, "/").endsWith("/dev/null")) return;
|
|
7304
|
+
try {
|
|
7305
|
+
if (!statSync3(absPath).isFile()) return;
|
|
7306
|
+
} catch {
|
|
7307
|
+
return;
|
|
7308
|
+
}
|
|
7309
|
+
enqueueDirtyPathSafe(absPath, { alreadyResolved: true });
|
|
7310
|
+
}
|
|
7311
|
+
function enqueueNonHeadMovingRewrites(cmd, rawCmd, cwd) {
|
|
7312
|
+
const atCwd = (f) => resolveIndexPath(resolveCdHintPath(rawCmd, f, cwd), cwd);
|
|
7313
|
+
const paths = [];
|
|
7314
|
+
let needsStatusSweep = GIT_STASH_APPLY_RE.test(cmd);
|
|
7315
|
+
if (GIT_RESTORE_RE.test(cmd) && !GIT_RESTORE_STAGED_ONLY_RE.test(cmd)) {
|
|
7316
|
+
paths.push(...expandGitPathspecs(extractGitRestorePathspecs(cmd), cwd));
|
|
7317
|
+
}
|
|
7318
|
+
for (const segment of splitShellSegments(cmd)) {
|
|
7319
|
+
if (PATCH_APPLY_SEGMENT_RE.test(segment) && !PATCH_APPLY_INSPECT_ONLY_RE.test(segment)) needsStatusSweep = true;
|
|
7320
|
+
if (isFormatterWriteSegment(segment)) needsStatusSweep = true;
|
|
7321
|
+
for (const f of extractSedInPlaceFiles(segment)) paths.push(atCwd(f));
|
|
7322
|
+
for (const f of extractTeeFiles(segment)) paths.push(atCwd(f));
|
|
7323
|
+
for (const f of extractRedirectTargets(segment)) paths.push(atCwd(f));
|
|
7324
|
+
}
|
|
7325
|
+
if (needsStatusSweep) paths.push(...workingTreeStatusPaths(cwd));
|
|
7326
|
+
for (const p of paths) enqueueRewrittenPath(p);
|
|
7327
|
+
}
|
|
7145
7328
|
function isTempPath(fp) {
|
|
7146
7329
|
const norm = fp.replace(/\\/g, "/");
|
|
7147
7330
|
return /^\/tmp\//i.test(norm) || /\/var\/folders\//i.test(norm) || /AppData\/Local\/Temp\//i.test(norm) || norm.startsWith("/c/Users/") && norm.includes("/AppData/Local/Temp/") || isUnderSystemTemp(fp);
|
|
@@ -7256,9 +7439,126 @@ function extractWslCatFile(cmd) {
|
|
|
7256
7439
|
if (flags === null) return null;
|
|
7257
7440
|
return { filePath, ...flags };
|
|
7258
7441
|
}
|
|
7442
|
+
function pythonScanText(text) {
|
|
7443
|
+
const dashC = /^python3?\s+-c\s*(['"])([\s\S]*)\1\s*$/.exec(text);
|
|
7444
|
+
if (dashC) {
|
|
7445
|
+
const source = dashC[2] ?? "";
|
|
7446
|
+
return { source, masked: maskPythonStrings(source) };
|
|
7447
|
+
}
|
|
7448
|
+
if (/^python3?\b/.test(text)) return { source: text, masked: text };
|
|
7449
|
+
return { source: text, masked: maskPythonStrings(text) };
|
|
7450
|
+
}
|
|
7451
|
+
function maskPythonStrings(text) {
|
|
7452
|
+
const out = text.split("");
|
|
7453
|
+
let i = 0;
|
|
7454
|
+
while (i < text.length) {
|
|
7455
|
+
const ch = text[i];
|
|
7456
|
+
if (ch !== "'" && ch !== '"') {
|
|
7457
|
+
i += 1;
|
|
7458
|
+
continue;
|
|
7459
|
+
}
|
|
7460
|
+
const triple = text.slice(i, i + 3);
|
|
7461
|
+
const delim = triple === ch + ch + ch ? triple : ch;
|
|
7462
|
+
let j = i + delim.length;
|
|
7463
|
+
while (j < text.length) {
|
|
7464
|
+
if (text[j] === "\\") {
|
|
7465
|
+
j += 2;
|
|
7466
|
+
continue;
|
|
7467
|
+
}
|
|
7468
|
+
if (text.slice(j, j + delim.length) === delim) break;
|
|
7469
|
+
out[j] = " ";
|
|
7470
|
+
j += 1;
|
|
7471
|
+
}
|
|
7472
|
+
i = j + delim.length;
|
|
7473
|
+
}
|
|
7474
|
+
return out.join("");
|
|
7475
|
+
}
|
|
7476
|
+
function pythonOpenCalls(text) {
|
|
7477
|
+
const { source, masked } = pythonScanText(text);
|
|
7478
|
+
const calls = [];
|
|
7479
|
+
for (const opener of masked.matchAll(/\bopen\s*\(/g)) {
|
|
7480
|
+
const start = (opener.index ?? 0) + opener[0].length;
|
|
7481
|
+
const args = [];
|
|
7482
|
+
let current = "";
|
|
7483
|
+
let depth = 0;
|
|
7484
|
+
let quote = "";
|
|
7485
|
+
let closed = false;
|
|
7486
|
+
for (let i = start; i < source.length; i++) {
|
|
7487
|
+
const ch = source[i] ?? "";
|
|
7488
|
+
if (quote !== "") {
|
|
7489
|
+
if (ch === "\\") {
|
|
7490
|
+
current += ch + (source[i + 1] ?? "");
|
|
7491
|
+
i += 1;
|
|
7492
|
+
continue;
|
|
7493
|
+
}
|
|
7494
|
+
if (ch === quote) quote = "";
|
|
7495
|
+
current += ch;
|
|
7496
|
+
continue;
|
|
7497
|
+
}
|
|
7498
|
+
if (ch === "'" || ch === '"') {
|
|
7499
|
+
quote = ch;
|
|
7500
|
+
current += ch;
|
|
7501
|
+
continue;
|
|
7502
|
+
}
|
|
7503
|
+
if (ch === "(" || ch === "[" || ch === "{") {
|
|
7504
|
+
depth += 1;
|
|
7505
|
+
current += ch;
|
|
7506
|
+
continue;
|
|
7507
|
+
}
|
|
7508
|
+
if (ch === ")" && depth === 0) {
|
|
7509
|
+
args.push(current);
|
|
7510
|
+
closed = true;
|
|
7511
|
+
break;
|
|
7512
|
+
}
|
|
7513
|
+
if (ch === ")" || ch === "]" || ch === "}") {
|
|
7514
|
+
depth -= 1;
|
|
7515
|
+
current += ch;
|
|
7516
|
+
continue;
|
|
7517
|
+
}
|
|
7518
|
+
if (ch === "," && depth === 0) {
|
|
7519
|
+
args.push(current);
|
|
7520
|
+
current = "";
|
|
7521
|
+
continue;
|
|
7522
|
+
}
|
|
7523
|
+
current += ch;
|
|
7524
|
+
}
|
|
7525
|
+
if (!closed) continue;
|
|
7526
|
+
const literalOf = (arg) => {
|
|
7527
|
+
const m = /^\s*r?(['"])([^'"]*)\1\s*$/.exec(arg);
|
|
7528
|
+
return m?.[2] ?? null;
|
|
7529
|
+
};
|
|
7530
|
+
const keyword = args.find((a) => /^\s*mode\s*=/.test(a));
|
|
7531
|
+
const positional = args[1] !== void 0 && /^\s*[A-Za-z_]\w*\s*=[^=]/.test(args[1]) ? void 0 : args[1];
|
|
7532
|
+
const modeArg = keyword !== void 0 ? keyword.replace(/^\s*mode\s*=/, "") : positional;
|
|
7533
|
+
calls.push({
|
|
7534
|
+
pathLiteral: args.length > 0 ? literalOf(args[0] ?? "") : null,
|
|
7535
|
+
hasMode: modeArg !== void 0,
|
|
7536
|
+
modeLiteral: modeArg === void 0 ? null : literalOf(modeArg)
|
|
7537
|
+
});
|
|
7538
|
+
}
|
|
7539
|
+
return calls;
|
|
7540
|
+
}
|
|
7541
|
+
function pythonOpenWritesAFile(text) {
|
|
7542
|
+
return pythonOpenCalls(text).some(
|
|
7543
|
+
(call) => call.hasMode && (call.modeLiteral === null || /[wax+]/.test(call.modeLiteral))
|
|
7544
|
+
);
|
|
7545
|
+
}
|
|
7546
|
+
function pythonWritesThroughFileObject(text) {
|
|
7547
|
+
const { masked } = pythonScanText(text);
|
|
7548
|
+
for (const call of masked.matchAll(/\.write(?:lines)?\s*\(/g)) {
|
|
7549
|
+
const receiver = masked.slice(0, call.index ?? 0);
|
|
7550
|
+
if (/(?:^|[^\w.])(?:sys\s*\.\s*)?(?:stdout|stderr|stdin)(?:\s*\.\s*buffer)?$/.test(receiver)) continue;
|
|
7551
|
+
return true;
|
|
7552
|
+
}
|
|
7553
|
+
return false;
|
|
7554
|
+
}
|
|
7555
|
+
function pythonOpenPathsAreAllLiteral(text) {
|
|
7556
|
+
const calls = pythonOpenCalls(text);
|
|
7557
|
+
return calls.length > 0 && calls.every((call) => call.pathLiteral !== null);
|
|
7558
|
+
}
|
|
7259
7559
|
function extractPythonFileRead(cmd) {
|
|
7260
7560
|
if (!/^python3?\b/.test(cmd)) return null;
|
|
7261
|
-
if (
|
|
7561
|
+
if (pythonOpenWritesAFile(cmd) || pythonWritesThroughFileObject(cmd)) return null;
|
|
7262
7562
|
const outputOpen = /open\s*\(\s*r?['"]([^'"]+\.output)['"]/i.exec(cmd);
|
|
7263
7563
|
if (outputOpen?.[1]) {
|
|
7264
7564
|
const filePath = outputOpen[1];
|
|
@@ -7269,7 +7569,7 @@ function extractPythonFileRead(cmd) {
|
|
|
7269
7569
|
const heredocMatch = /^python3?\s+-\s+<<\s*'?(\w+)'?\s*\n([\s\S]*?)\n\1\s*$/.exec(cmd);
|
|
7270
7570
|
if (heredocMatch) {
|
|
7271
7571
|
const body = heredocMatch[2] ?? "";
|
|
7272
|
-
if (
|
|
7572
|
+
if (pythonOpenWritesAFile(body) || pythonWritesThroughFileObject(body)) return null;
|
|
7273
7573
|
const heredocOpen = /open\s*\(\s*r?['"]([^'"]+\.(?:java|py|ts|tsx|js|jsx|go|rb|rs|cpp|cc|cxx|c|h|hpp|kt|swift|cs|php|scala|clj|md|mdx|rst|txt|json|yaml|yml|toml|xml|conf|cfg|ini|properties))['"]/i.exec(body);
|
|
7274
7574
|
if (heredocOpen?.[1]) {
|
|
7275
7575
|
const filePath = heredocOpen[1];
|
|
@@ -7277,7 +7577,7 @@ function extractPythonFileRead(cmd) {
|
|
|
7277
7577
|
const isDoc = /\.(?:md|mdx|rst|txt)$/i.test(filePath);
|
|
7278
7578
|
return { filePath, isDoc, isTranscript: false };
|
|
7279
7579
|
}
|
|
7280
|
-
if (/open\s*\(/.test(body)) {
|
|
7580
|
+
if (/open\s*\(/.test(body) && !pythonOpenPathsAreAllLiteral(body)) {
|
|
7281
7581
|
const literal2 = /['"]([^'"]+\.(?:java|py|ts|tsx|js|jsx|go|rb|rs|cpp|cc|cxx|c|h|hpp|kt|swift|cs|php|scala|clj|md|mdx|rst|txt|json|yaml|yml|toml|xml|conf|cfg|ini|properties))['"]/i.exec(body);
|
|
7282
7582
|
if (literal2?.[1]) {
|
|
7283
7583
|
const filePath = literal2[1];
|
|
@@ -7298,7 +7598,7 @@ function extractPythonFileRead(cmd) {
|
|
|
7298
7598
|
const isDoc = /\.(?:md|mdx|rst|txt)$/i.test(filePath);
|
|
7299
7599
|
return { filePath, isDoc, isTranscript: false };
|
|
7300
7600
|
}
|
|
7301
|
-
if (/open\s*\(/.test(cmd)) {
|
|
7601
|
+
if (/open\s*\(/.test(cmd) && !pythonOpenPathsAreAllLiteral(cmd)) {
|
|
7302
7602
|
const literal2 = /['"]([^'"]+\.(?:java|py|ts|tsx|js|jsx|go|rb|rs|cpp|cc|cxx|c|h|hpp|kt|swift|cs|php|scala|clj|md|mdx|rst|txt|json|yaml|yml|toml|xml|conf|cfg|ini|properties))['"]/i.exec(cmd);
|
|
7303
7603
|
if (literal2) {
|
|
7304
7604
|
const filePath = literal2[1] ?? "";
|
|
@@ -7345,6 +7645,30 @@ function extractSedRange(cmd) {
|
|
|
7345
7645
|
if (isTempPath(filePath)) return null;
|
|
7346
7646
|
return { filePath, ranges };
|
|
7347
7647
|
}
|
|
7648
|
+
function extractAwkRange(cmd) {
|
|
7649
|
+
const m = /^awk\s+(?:'([^']+)'|"([^"]+)")\s+(?:"([^"]+)"|'([^']+)'|(\S+))(?:\s+2>\/dev\/null)?\s*$/.exec(cmd);
|
|
7650
|
+
if (!m) return null;
|
|
7651
|
+
const program = m[1] ?? m[2];
|
|
7652
|
+
if (program === void 0) return null;
|
|
7653
|
+
const cmp = /^\s*NR\s*>=\s*(\d+)\s*&&\s*NR\s*<=\s*(\d+)\s*$/.exec(program);
|
|
7654
|
+
const rng = /^\s*NR\s*==\s*(\d+)\s*,\s*NR\s*==\s*(\d+)\s*$/.exec(program);
|
|
7655
|
+
const hit = cmp ?? rng;
|
|
7656
|
+
if (!hit) return null;
|
|
7657
|
+
const start = parseInt(hit[1], 10);
|
|
7658
|
+
const end = parseInt(hit[2], 10);
|
|
7659
|
+
if (start < 1 || end < start) return null;
|
|
7660
|
+
const filePath = m[3] ?? m[4] ?? m[5];
|
|
7661
|
+
if (filePath === void 0) return null;
|
|
7662
|
+
if (isTempPath(filePath)) return null;
|
|
7663
|
+
return { filePath, ranges: [[start, end]] };
|
|
7664
|
+
}
|
|
7665
|
+
function extractLineRangeRead(cmd) {
|
|
7666
|
+
const sed = extractSedRange(cmd);
|
|
7667
|
+
if (sed !== null) return { ...sed, tool: "sed" };
|
|
7668
|
+
const awk = extractAwkRange(cmd);
|
|
7669
|
+
if (awk !== null) return { ...awk, tool: "awk" };
|
|
7670
|
+
return null;
|
|
7671
|
+
}
|
|
7348
7672
|
var SYMBOL_BEARING_LANGUAGES = /* @__PURE__ */ new Set([
|
|
7349
7673
|
"python",
|
|
7350
7674
|
"typescript",
|
|
@@ -7375,11 +7699,11 @@ var SYMBOL_BEARING_LANGUAGES = /* @__PURE__ */ new Set([
|
|
|
7375
7699
|
"salesforce_metadata",
|
|
7376
7700
|
"salesforce_markup"
|
|
7377
7701
|
]);
|
|
7378
|
-
function sedRangeHint(filePath, ranges) {
|
|
7702
|
+
function sedRangeHint(filePath, ranges, tool) {
|
|
7379
7703
|
const lang = detectLanguage(filePath);
|
|
7380
7704
|
const rangeReads = ranges.map(([s, e]) => '`token-goat read "' + filePath + "@" + s + "-" + e + '"`');
|
|
7381
7705
|
const allReads = rangeReads.length === 2 ? rangeReads.join(" and ") : rangeReads.length >= 3 ? rangeReads.slice(0, -1).join(", ") + ", and " + rangeReads[rangeReads.length - 1] : rangeReads[0];
|
|
7382
|
-
const prefix = "`sed -n` line-range reads bypass read hooks. ";
|
|
7706
|
+
const prefix = "`" + (tool === "awk" ? "awk" : "sed -n") + "` line-range reads bypass read hooks. ";
|
|
7383
7707
|
if (lang === "markdown") {
|
|
7384
7708
|
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.";
|
|
7385
7709
|
}
|
|
@@ -7404,7 +7728,7 @@ function findRangeOverlap(prior, start, end) {
|
|
|
7404
7728
|
return best;
|
|
7405
7729
|
}
|
|
7406
7730
|
function sedOverlapHint(filePath, prior, start, end) {
|
|
7407
|
-
const base = "You already read lines " + prior[0] + "-" + prior[1] + " of " + filePath + " via an earlier
|
|
7731
|
+
const base = "You already read lines " + prior[0] + "-" + prior[1] + " of " + filePath + " via an earlier line-range read this session; this read (" + start + "-" + end + ") overlaps. ";
|
|
7408
7732
|
const segments = [];
|
|
7409
7733
|
if (start < prior[0]) segments.push([start, Math.min(end, prior[0] - 1)]);
|
|
7410
7734
|
if (end > prior[1]) segments.push([Math.max(start, prior[1] + 1), end]);
|
|
@@ -7580,12 +7904,151 @@ function extractRgStructuralSearch(cmd) {
|
|
|
7580
7904
|
function extractGrepPipeChain(cmd) {
|
|
7581
7905
|
return /^(?:rg|grep)\b.*\|\s*(?:rg|grep)\b/.test(cmd);
|
|
7582
7906
|
}
|
|
7907
|
+
function splitCommandWords(cmd) {
|
|
7908
|
+
const words = [];
|
|
7909
|
+
let cur = "";
|
|
7910
|
+
let quote = null;
|
|
7911
|
+
let started = false;
|
|
7912
|
+
for (let i = 0; i < cmd.length; i++) {
|
|
7913
|
+
const ch = cmd[i];
|
|
7914
|
+
if (quote !== null) {
|
|
7915
|
+
if (ch === "\\" && quote === '"' && i + 1 < cmd.length) {
|
|
7916
|
+
cur += cmd[i + 1];
|
|
7917
|
+
i++;
|
|
7918
|
+
continue;
|
|
7919
|
+
}
|
|
7920
|
+
if (ch === quote) quote = null;
|
|
7921
|
+
else cur += ch;
|
|
7922
|
+
continue;
|
|
7923
|
+
}
|
|
7924
|
+
if (ch === "\\" && i + 1 < cmd.length) {
|
|
7925
|
+
cur += cmd[i + 1];
|
|
7926
|
+
i++;
|
|
7927
|
+
started = true;
|
|
7928
|
+
continue;
|
|
7929
|
+
}
|
|
7930
|
+
if (ch === '"' || ch === "'") {
|
|
7931
|
+
quote = ch;
|
|
7932
|
+
started = true;
|
|
7933
|
+
continue;
|
|
7934
|
+
}
|
|
7935
|
+
if (/\s/.test(ch)) {
|
|
7936
|
+
if (started) words.push(cur);
|
|
7937
|
+
cur = "";
|
|
7938
|
+
started = false;
|
|
7939
|
+
continue;
|
|
7940
|
+
}
|
|
7941
|
+
cur += ch;
|
|
7942
|
+
started = true;
|
|
7943
|
+
}
|
|
7944
|
+
if (started) words.push(cur);
|
|
7945
|
+
return words;
|
|
7946
|
+
}
|
|
7947
|
+
var CURL_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
7948
|
+
"-H",
|
|
7949
|
+
"--header",
|
|
7950
|
+
"-A",
|
|
7951
|
+
"--user-agent",
|
|
7952
|
+
"-e",
|
|
7953
|
+
"--referer",
|
|
7954
|
+
"-b",
|
|
7955
|
+
"--cookie",
|
|
7956
|
+
"-c",
|
|
7957
|
+
"--cookie-jar",
|
|
7958
|
+
"-o",
|
|
7959
|
+
"--output",
|
|
7960
|
+
"--output-dir",
|
|
7961
|
+
"-u",
|
|
7962
|
+
"--user",
|
|
7963
|
+
"-U",
|
|
7964
|
+
"--proxy-user",
|
|
7965
|
+
"-x",
|
|
7966
|
+
"--proxy",
|
|
7967
|
+
"-X",
|
|
7968
|
+
"--request",
|
|
7969
|
+
"-d",
|
|
7970
|
+
"--data",
|
|
7971
|
+
"--data-raw",
|
|
7972
|
+
"--data-binary",
|
|
7973
|
+
"--data-urlencode",
|
|
7974
|
+
"-F",
|
|
7975
|
+
"--form",
|
|
7976
|
+
"-T",
|
|
7977
|
+
"--upload-file",
|
|
7978
|
+
"-K",
|
|
7979
|
+
"--config",
|
|
7980
|
+
"-w",
|
|
7981
|
+
"--write-out",
|
|
7982
|
+
"-m",
|
|
7983
|
+
"--max-time",
|
|
7984
|
+
"--connect-to",
|
|
7985
|
+
"--resolve",
|
|
7986
|
+
"--retry",
|
|
7987
|
+
"--cacert",
|
|
7988
|
+
"-E",
|
|
7989
|
+
"--cert",
|
|
7990
|
+
"--key",
|
|
7991
|
+
"--range",
|
|
7992
|
+
"-r",
|
|
7993
|
+
"--interface",
|
|
7994
|
+
// Options whose own value is a URL or a host. Leaving these out was the same defect as reading
|
|
7995
|
+
// the first URL in the string: `curl --doh-url https://doh/ -o f https://target` answered with
|
|
7996
|
+
// the DoH resolver rather than the file being fetched. `--url-query` belongs here too -- it
|
|
7997
|
+
// appends query data to the request, it does not name the request target.
|
|
7998
|
+
"--url-query",
|
|
7999
|
+
"--doh-url",
|
|
8000
|
+
"--preproxy",
|
|
8001
|
+
"--proxy1.0",
|
|
8002
|
+
"--socks4",
|
|
8003
|
+
"--socks4a",
|
|
8004
|
+
"--socks5",
|
|
8005
|
+
"--socks5-hostname",
|
|
8006
|
+
"--proxy-header",
|
|
8007
|
+
"--noproxy",
|
|
8008
|
+
"--dns-servers",
|
|
8009
|
+
"--aws-sigv4",
|
|
8010
|
+
"--proxy-cacert",
|
|
8011
|
+
"--proxy-cert",
|
|
8012
|
+
"--proxy-key",
|
|
8013
|
+
"--oauth2-bearer",
|
|
8014
|
+
"--hsts",
|
|
8015
|
+
"--alt-svc",
|
|
8016
|
+
"--etag-save",
|
|
8017
|
+
"--etag-compare",
|
|
8018
|
+
"--trace",
|
|
8019
|
+
"--trace-ascii",
|
|
8020
|
+
"--dump-header",
|
|
8021
|
+
"-D"
|
|
8022
|
+
]);
|
|
7583
8023
|
function extractCurlUrl(cmd) {
|
|
7584
|
-
const
|
|
7585
|
-
|
|
8024
|
+
const words = splitCommandWords(cmd);
|
|
8025
|
+
let positional = null;
|
|
8026
|
+
for (let i = 0; i < words.length; i++) {
|
|
8027
|
+
const w = words[i];
|
|
8028
|
+
if (w === "--url") {
|
|
8029
|
+
const next = words[i + 1];
|
|
8030
|
+
if (next !== void 0 && /^https?:\/\//.test(next)) return next;
|
|
8031
|
+
i++;
|
|
8032
|
+
continue;
|
|
8033
|
+
}
|
|
8034
|
+
const eq = w.indexOf("=");
|
|
8035
|
+
if (w.startsWith("--") && eq > 0) {
|
|
8036
|
+
if (w.slice(0, eq) === "--url" && /^https?:\/\//.test(w.slice(eq + 1))) return w.slice(eq + 1);
|
|
8037
|
+
continue;
|
|
8038
|
+
}
|
|
8039
|
+
if (CURL_VALUE_FLAGS.has(w)) {
|
|
8040
|
+
i++;
|
|
8041
|
+
continue;
|
|
8042
|
+
}
|
|
8043
|
+
if (w.startsWith("-") && w !== "-") continue;
|
|
8044
|
+
if (/^https?:\/\//.test(w)) return w;
|
|
8045
|
+
positional ??= /https?:\/\/[^\s'")}`]+/.exec(w)?.[0] ?? null;
|
|
8046
|
+
}
|
|
8047
|
+
if (positional !== null) return positional;
|
|
8048
|
+
return null;
|
|
7586
8049
|
}
|
|
7587
8050
|
function extractTgSurgicalRead(cmd, cwd) {
|
|
7588
|
-
const m = /^token-goat\s+(symbol|read|section|skill-body|skill-compact|map)(?:\s+(.*))?$/.exec(cmd);
|
|
8051
|
+
const m = /^(?:token-goat|tg)\s+(symbol|read|section|skill-body|skill-compact|map)(?:\s+(.*))?$/.exec(cmd);
|
|
7589
8052
|
if (!m) return null;
|
|
7590
8053
|
const sub = m[1];
|
|
7591
8054
|
const rest = (m[2] ?? "").trim();
|
|
@@ -7664,9 +8127,9 @@ function extractCurlDownload(cmd) {
|
|
|
7664
8127
|
const outputPath = outputMatch[1] ?? outputMatch[2] ?? outputMatch[3];
|
|
7665
8128
|
if (!outputPath) return null;
|
|
7666
8129
|
if (curlHasUnsafeFlags(cmd)) return null;
|
|
7667
|
-
const
|
|
7668
|
-
if (
|
|
7669
|
-
return { url
|
|
8130
|
+
const url = extractCurlUrl(cmd);
|
|
8131
|
+
if (url === null) return null;
|
|
8132
|
+
return { url, outputPath };
|
|
7670
8133
|
}
|
|
7671
8134
|
function isTscCommand(cmd) {
|
|
7672
8135
|
return /^\s*tsc(\s|$)/i.test(cmd);
|
|
@@ -7705,11 +8168,12 @@ function maybeCompressRewrite(event, rawCmd, cmd) {
|
|
|
7705
8168
|
}
|
|
7706
8169
|
if (!cfg.enabled) return null;
|
|
7707
8170
|
if (!canRunWrappedShell()) return null;
|
|
7708
|
-
const
|
|
8171
|
+
const gateCmd = stripTrailingStderrRedirect(cmd);
|
|
8172
|
+
const detected = detectFromCommand(gateCmd, getCwd(event));
|
|
7709
8173
|
let filterName;
|
|
7710
8174
|
if (detected !== null) {
|
|
7711
8175
|
filterName = detected.filter.name;
|
|
7712
|
-
} else if (isCompressibleSingleCommand(
|
|
8176
|
+
} else if (isCompressibleSingleCommand(gateCmd)) {
|
|
7713
8177
|
filterName = "generic";
|
|
7714
8178
|
} else {
|
|
7715
8179
|
return null;
|
|
@@ -7718,9 +8182,34 @@ function maybeCompressRewrite(event, rawCmd, cmd) {
|
|
|
7718
8182
|
const wrapped = `token-goat compress -f ${filterName} --timeout ${cfg.timeout_seconds} -c ${shellQuoteSingle(rawCmd)}`;
|
|
7719
8183
|
return { hookType: "rewriteInput", updatedInput: { ...event.toolInput, command: wrapped } };
|
|
7720
8184
|
}
|
|
8185
|
+
async function maybeCompressCompoundOutput(cmd, output, exitCode, cwd, cacheMinBytes) {
|
|
8186
|
+
if (process.env["TOKEN_GOAT_BASH_COMPRESS"] === "0") return null;
|
|
8187
|
+
if (isCompressibleSingleCommand(cmd)) return null;
|
|
8188
|
+
if (exitCode !== null && exitCode !== 0) return null;
|
|
8189
|
+
let cfg;
|
|
8190
|
+
try {
|
|
8191
|
+
cfg = loadConfig().bash_compress;
|
|
8192
|
+
} catch {
|
|
8193
|
+
return null;
|
|
8194
|
+
}
|
|
8195
|
+
if (!cfg.enabled || cfg.disabled_filters.includes("generic")) return null;
|
|
8196
|
+
if (Buffer.byteLength(output, "utf-8") < cacheMinBytes) return null;
|
|
8197
|
+
const filter3 = filterByName("generic");
|
|
8198
|
+
if (filter3 === null) return null;
|
|
8199
|
+
const compressed = compressOutput(filter3, output, "", exitCode ?? 0, [], {
|
|
8200
|
+
maxLines: cfg.max_lines,
|
|
8201
|
+
maxBytes: cfg.max_bytes
|
|
8202
|
+
});
|
|
8203
|
+
const minNet = resolveMinNetSavingsBytes();
|
|
8204
|
+
if (!compressed.worthApplying(minNet)) return null;
|
|
8205
|
+
const id = await storeBashOutput(cmd, output, exitCode ?? 0, cwd);
|
|
8206
|
+
recordStat("bash_compress:generic", compressed.bytesSaved, compressed.tokensSaved);
|
|
8207
|
+
const body = compressed.withMarker(minNet) + "\n[token-goat] full output: bash-output " + id + " --full";
|
|
8208
|
+
return { hookType: "rewriteOutput", updatedOutput: body };
|
|
8209
|
+
}
|
|
7721
8210
|
function unwrapCompressCommand(executed) {
|
|
7722
8211
|
const t = executed.trim();
|
|
7723
|
-
if (!/^token-goat\s+compress\b/.test(t)) return null;
|
|
8212
|
+
if (!/^(?:token-goat|tg)\s+compress\b/.test(t)) return null;
|
|
7724
8213
|
let argv;
|
|
7725
8214
|
try {
|
|
7726
8215
|
argv = shlexSplit(t);
|
|
@@ -7772,6 +8261,28 @@ function detectUnbalancedShellSyntax(cmd) {
|
|
|
7772
8261
|
i = j;
|
|
7773
8262
|
continue;
|
|
7774
8263
|
}
|
|
8264
|
+
if (ch === "$" && cmd[i + 1] === "'") {
|
|
8265
|
+
let j = i + 2;
|
|
8266
|
+
let closed = false;
|
|
8267
|
+
while (j < cmd.length) {
|
|
8268
|
+
if (cmd[j] === "\\") {
|
|
8269
|
+
j += 2;
|
|
8270
|
+
continue;
|
|
8271
|
+
}
|
|
8272
|
+
if (cmd[j] === "'") {
|
|
8273
|
+
j++;
|
|
8274
|
+
closed = true;
|
|
8275
|
+
break;
|
|
8276
|
+
}
|
|
8277
|
+
j++;
|
|
8278
|
+
}
|
|
8279
|
+
if (!closed) {
|
|
8280
|
+
inSingle = true;
|
|
8281
|
+
break;
|
|
8282
|
+
}
|
|
8283
|
+
i = j;
|
|
8284
|
+
continue;
|
|
8285
|
+
}
|
|
7775
8286
|
if (ch === "#" && (i === 0 || /\s/.test(cmd[i - 1] ?? ""))) {
|
|
7776
8287
|
const nl2 = cmd.indexOf("\n", i);
|
|
7777
8288
|
i = nl2 === -1 ? cmd.length : nl2 + 1;
|
|
@@ -7926,9 +8437,9 @@ function preBashHandlerInner(event) {
|
|
|
7926
8437
|
"Use `token-goat outline <file>` to see symbol names and line counts without loading files."
|
|
7927
8438
|
);
|
|
7928
8439
|
}
|
|
7929
|
-
const sedRange =
|
|
8440
|
+
const sedRange = extractLineRangeRead(cmd);
|
|
7930
8441
|
if (sedRange !== null) {
|
|
7931
|
-
const { filePath, ranges } = sedRange;
|
|
8442
|
+
const { filePath, ranges, tool } = sedRange;
|
|
7932
8443
|
const hintPath = cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath;
|
|
7933
8444
|
recordStat("session_hint", 0, 0);
|
|
7934
8445
|
const sedDedupKey = resolveIndexPath(hintPath, preHookCwd ?? process.cwd());
|
|
@@ -7944,7 +8455,7 @@ function preBashHandlerInner(event) {
|
|
|
7944
8455
|
}
|
|
7945
8456
|
}
|
|
7946
8457
|
const hints = [...overlapHints];
|
|
7947
|
-
if (freshRanges.length > 0) hints.push(sedRangeHint(hintPath, freshRanges));
|
|
8458
|
+
if (freshRanges.length > 0) hints.push(sedRangeHint(hintPath, freshRanges, tool));
|
|
7948
8459
|
return contextOutput(hints.join(" "));
|
|
7949
8460
|
}
|
|
7950
8461
|
const catJsonPipe = extractCatJsonPipe(cmd);
|
|
@@ -8197,7 +8708,7 @@ function preBashHandlerInner(event) {
|
|
|
8197
8708
|
);
|
|
8198
8709
|
}
|
|
8199
8710
|
}
|
|
8200
|
-
if (!isBuildCommand(cmd) && detectFromCommand(cmd, preHookCwd ?? void 0) === null) return passOutput();
|
|
8711
|
+
if (!isBuildCommand(cmd) && detectFromCommand(stripTrailingStderrRedirect(cmd), preHookCwd ?? void 0) === null) return passOutput();
|
|
8201
8712
|
const cmdHash = shortFingerprint(stripOutputPipeline(cmd));
|
|
8202
8713
|
const outputId = getBashOutputId(cmdHash);
|
|
8203
8714
|
const entryRaw = outputId !== null ? getBashOutput(outputId) : null;
|
|
@@ -8312,8 +8823,7 @@ async function postBashHandler(event) {
|
|
|
8312
8823
|
}
|
|
8313
8824
|
const mutationDiff = runGit(["diff", "--name-only", diffBase, "HEAD"], { cwd: gitDir, timeoutMs: 5e3 });
|
|
8314
8825
|
if (mutationDiff.exitCode === 0) {
|
|
8315
|
-
const
|
|
8316
|
-
const repoRoot = toplevel.exitCode === 0 && toplevel.stdout.trim() !== "" ? toplevel.stdout.trim() : gitDir;
|
|
8826
|
+
const repoRoot = gitRepoRoot(gitDir);
|
|
8317
8827
|
for (const rel of mutationDiff.stdout.split("\n")) {
|
|
8318
8828
|
const trimmed = rel.trim();
|
|
8319
8829
|
if (trimmed.length === 0) continue;
|
|
@@ -8321,6 +8831,9 @@ async function postBashHandler(event) {
|
|
|
8321
8831
|
}
|
|
8322
8832
|
}
|
|
8323
8833
|
}
|
|
8834
|
+
if (exitCode === null || exitCode === 0) {
|
|
8835
|
+
enqueueNonHeadMovingRewrites(cmd, rawCmd, cwd ?? process.cwd());
|
|
8836
|
+
}
|
|
8324
8837
|
const curlDl = extractCurlDownload(cmd);
|
|
8325
8838
|
if (curlDl !== null && (exitCode === null || exitCode === 0)) {
|
|
8326
8839
|
const resolvedOutputPath = resolveIndexPath(curlDl.outputPath, cwd ?? process.cwd());
|
|
@@ -8376,7 +8889,11 @@ async function postBashHandler(event) {
|
|
|
8376
8889
|
recordBashOutput(gitScopedCacheHash, gitScopedCacheId, Buffer.byteLength(output, "utf-8"));
|
|
8377
8890
|
}
|
|
8378
8891
|
const isMonitoring = getMonitoringRecallHint(cmd) !== null;
|
|
8379
|
-
if (!isMonitoring && !isBuildCommand(cmd) && !isCurlGetCommand(cmd))
|
|
8892
|
+
if (!isMonitoring && !isBuildCommand(cmd) && !isCurlGetCommand(cmd)) {
|
|
8893
|
+
const compound = await maybeCompressCompoundOutput(cmd, output, exitCode, cwd, cacheMinBytes);
|
|
8894
|
+
if (compound !== null) return compound;
|
|
8895
|
+
return passOutput();
|
|
8896
|
+
}
|
|
8380
8897
|
if (Buffer.byteLength(output, "utf-8") < cacheMinBytes) return passOutput();
|
|
8381
8898
|
const cacheKey = isCurlGetCommand(cmd) ? extractCurlUrl(cmd) ?? cmd : stripOutputPipeline(cmd);
|
|
8382
8899
|
const simpleHash = shortFingerprint(cacheKey);
|
|
@@ -8968,8 +9485,21 @@ function compressGWorkspaceMcpResult(toolName, resultText) {
|
|
|
8968
9485
|
const summarized = summarizeAttachments(trimmed);
|
|
8969
9486
|
return finishWithGenericPass(resultText, summarized);
|
|
8970
9487
|
}
|
|
9488
|
+
var DATA_URL_BASE64_RE = /data:([a-zA-Z0-9.+-]+\/[a-zA-Z0-9.+-]+);base64,([A-Za-z0-9+/=]+)/g;
|
|
9489
|
+
var MIN_BASE64_PAYLOAD_LEN = 100;
|
|
9490
|
+
function stripBase64DataUrls(resultText) {
|
|
9491
|
+
let changed = false;
|
|
9492
|
+
const out = resultText.replace(DATA_URL_BASE64_RE, (match, mime, payload) => {
|
|
9493
|
+
if (payload.length < MIN_BASE64_PAYLOAD_LEN) return match;
|
|
9494
|
+
changed = true;
|
|
9495
|
+
return `data:${mime};base64,<stripped ${payload.length} bytes>`;
|
|
9496
|
+
});
|
|
9497
|
+
return changed ? out : null;
|
|
9498
|
+
}
|
|
8971
9499
|
function compressMcpResultWithPacks(toolName, resultText) {
|
|
8972
|
-
|
|
9500
|
+
const base64Stripped = stripBase64DataUrls(resultText);
|
|
9501
|
+
const text = base64Stripped ?? resultText;
|
|
9502
|
+
return compressGithubMcpResult(toolName, text) ?? compressBrowserMcpResult(toolName, text) ?? compressGWorkspaceMcpResult(toolName, text) ?? base64Stripped;
|
|
8973
9503
|
}
|
|
8974
9504
|
|
|
8975
9505
|
// src/hooks_mcp.ts
|
|
@@ -9489,6 +10019,9 @@ async function relayInProcess(eventName, rawPayload) {
|
|
|
9489
10019
|
async function relay(eventName) {
|
|
9490
10020
|
try {
|
|
9491
10021
|
if (!isHookEventName(eventName)) {
|
|
10022
|
+
console.error(
|
|
10023
|
+
`[relay] unknown hook event '${eventName}'; nothing ran. Valid events: ${HOOK_EVENTS.join(", ")}`
|
|
10024
|
+
);
|
|
9492
10025
|
process.stdout.write("{}");
|
|
9493
10026
|
return;
|
|
9494
10027
|
}
|