token-goat 2.6.33 → 2.6.34
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/dist/{token-goat-chunk-WFRR2DBG.mjs → token-goat-chunk-7FWBSPCT.mjs} +446 -339
- package/dist/{token-goat-chunk-HPWPUBAK.mjs → token-goat-chunk-CKRATWCJ.mjs} +5 -5
- package/dist/{token-goat-chunk-TDYTBCT4.mjs → token-goat-chunk-CN7GUPX6.mjs} +328 -86
- package/dist/{token-goat-chunk-L6FVTDPD.mjs → token-goat-chunk-FRMEOIRU.mjs} +361 -23
- package/dist/{token-goat-chunk-RPOXYJWK.mjs → token-goat-chunk-GXBPEQR2.mjs} +70 -25
- package/dist/{token-goat-chunk-77CX3MCN.mjs → token-goat-chunk-I6EQH27G.mjs} +2 -2
- package/dist/{token-goat-chunk-FN6WLFIX.mjs → token-goat-chunk-IYYQOPY4.mjs} +801 -51
- package/dist/{token-goat-chunk-72R7M5H7.mjs → token-goat-chunk-L7QLEVO7.mjs} +3 -3
- package/dist/{token-goat-chunk-OHQ7RBLL.mjs → token-goat-chunk-OA3GFKP2.mjs} +2 -2
- package/dist/{token-goat-hook-chunk-HXHFLKEL.mjs → token-goat-chunk-WA2ER6TZ.mjs} +7 -2
- package/dist/{token-goat-chunk-RQQ4SY2P.mjs → token-goat-hook-chunk-2ISZ4AZR.mjs} +7 -2
- package/dist/{token-goat-hook-chunk-ZT47M3IA.mjs → token-goat-hook-chunk-6C5RN3CM.mjs} +2 -2
- package/dist/{token-goat-hook-chunk-UWV4MTYI.mjs → token-goat-hook-chunk-6QTWMSRO.mjs} +328 -86
- package/dist/{token-goat-hook-chunk-XBOWDNJB.mjs → token-goat-hook-chunk-ATEGFQAU.mjs} +5 -5
- package/dist/{token-goat-hook-chunk-QD2DX2U7.mjs → token-goat-hook-chunk-BU5OFDGQ.mjs} +361 -23
- package/dist/{token-goat-hook-chunk-E6AIPKNF.mjs → token-goat-hook-chunk-CNLMPHXT.mjs} +442 -335
- package/dist/{token-goat-hook-chunk-WBBHV6TZ.mjs → token-goat-hook-chunk-I5VM5PO5.mjs} +70 -25
- package/dist/{token-goat-hook-chunk-VDXQXYJK.mjs → token-goat-hook-chunk-KGLMNOGD.mjs} +801 -51
- package/dist/{token-goat-hook-chunk-EKGDLI4D.mjs → token-goat-hook-chunk-OQML5EMA.mjs} +3 -3
- package/dist/{token-goat-hook-chunk-26DLRODR.mjs → token-goat-hook-chunk-QTZ6YUMQ.mjs} +2 -2
- package/dist/token-goat-hook.mjs +5 -5
- package/dist/token-goat.core.mjs +5 -5
- package/package.json +8 -8
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
isRewriteWorthwhile,
|
|
9
9
|
resolveMinNetSavingsBytes,
|
|
10
10
|
shlexSplit
|
|
11
|
-
} from "./token-goat-chunk-
|
|
11
|
+
} from "./token-goat-chunk-WA2ER6TZ.mjs";
|
|
12
12
|
import {
|
|
13
13
|
BASH_OUTPUT_SUBDIR,
|
|
14
14
|
HOOK_EVENTS,
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
storeBashOutput,
|
|
28
28
|
storeWebOutput,
|
|
29
29
|
summarizeOutputDelta
|
|
30
|
-
} from "./token-goat-chunk-
|
|
30
|
+
} from "./token-goat-chunk-OA3GFKP2.mjs";
|
|
31
31
|
import {
|
|
32
32
|
BODY_FIRST_TOOL_RESPONSE_KEYS,
|
|
33
33
|
OUTPUT_FIRST_TOOL_RESPONSE_KEYS,
|
|
@@ -117,11 +117,12 @@ import {
|
|
|
117
117
|
wasCliReadThisSession,
|
|
118
118
|
wasFileReadThisSession,
|
|
119
119
|
wasHintShown
|
|
120
|
-
} from "./token-goat-chunk-
|
|
120
|
+
} from "./token-goat-chunk-CN7GUPX6.mjs";
|
|
121
121
|
import {
|
|
122
122
|
VERSION,
|
|
123
123
|
detectHarness,
|
|
124
124
|
detectLanguage,
|
|
125
|
+
displaySafePath,
|
|
125
126
|
extractErrorMessage,
|
|
126
127
|
foldPath,
|
|
127
128
|
getDb,
|
|
@@ -135,7 +136,7 @@ import {
|
|
|
135
136
|
runGit,
|
|
136
137
|
shortFingerprint,
|
|
137
138
|
toKB
|
|
138
|
-
} from "./token-goat-chunk-
|
|
139
|
+
} from "./token-goat-chunk-7FWBSPCT.mjs";
|
|
139
140
|
import "./token-goat-chunk-AO2QD2AG.mjs";
|
|
140
141
|
import "./token-goat-chunk-AEX54RUZ.mjs";
|
|
141
142
|
|
|
@@ -314,9 +315,9 @@ function postEditHandlerInner(event) {
|
|
|
314
315
|
} catch {
|
|
315
316
|
}
|
|
316
317
|
if (meetsSavingsFloor(editedSize)) {
|
|
317
|
-
const escapedPath = normalized.replace(/`/g, "\\`").replace(/"/g, '\\"');
|
|
318
|
+
const escapedPath = displaySafePath(normalized).replace(/`/g, "\\`").replace(/"/g, '\\"');
|
|
318
319
|
return contextOutput(
|
|
319
|
-
editedBasename + ' was edited. Use `token-goat section "' + escapedPath + '::HeadingName"` to re-read a specific section rather than the full file.'
|
|
320
|
+
displaySafePath(editedBasename) + ' was edited. Use `token-goat section "' + escapedPath + '::HeadingName"` to re-read a specific section rather than the full file.'
|
|
320
321
|
);
|
|
321
322
|
}
|
|
322
323
|
}
|
|
@@ -575,7 +576,7 @@ import crypto from "node:crypto";
|
|
|
575
576
|
var TRACKED_SKILL = "token-goat";
|
|
576
577
|
var MAX_COMMANDS_SHOWN = 8;
|
|
577
578
|
async function currentCommandNames() {
|
|
578
|
-
const { buildProgram } = await import("./token-goat-chunk-
|
|
579
|
+
const { buildProgram } = await import("./token-goat-chunk-CKRATWCJ.mjs");
|
|
579
580
|
return flattenCommandNames(buildCommandManifest(buildProgram()));
|
|
580
581
|
}
|
|
581
582
|
async function recordSkillVersionSnapshot(sessionId, skillName) {
|
|
@@ -7054,6 +7055,10 @@ function stripCdPrefix(cmd) {
|
|
|
7054
7055
|
const stripped = cmd.replace(/^(?:cd\s+(?:"[^"]*"|'[^']*'|\S+)\s*&&\s*)+/, "");
|
|
7055
7056
|
return stripped.trim() || cmd;
|
|
7056
7057
|
}
|
|
7058
|
+
function stripTrailingStderrRedirect(cmd) {
|
|
7059
|
+
const stripped = cmd.replace(/\s*2>&1\s*$/, "");
|
|
7060
|
+
return stripped.trim() || cmd;
|
|
7061
|
+
}
|
|
7057
7062
|
function hasTestRunScopeOrBudget(cmd) {
|
|
7058
7063
|
const hasTimeout = /(?:^|\s)(?:timeout(?:\.exe)?\s+\S+|--(?:test)?timeout(?:=|\s)\S+)/i.test(cmd);
|
|
7059
7064
|
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);
|
|
@@ -7145,6 +7150,182 @@ function isHeadMovingGitCommand(cmd) {
|
|
|
7145
7150
|
}
|
|
7146
7151
|
var ORIG_HEAD_ELIGIBLE_GIT_RE = /^\s*git\s+(?:pull|merge|rebase)\b/i;
|
|
7147
7152
|
var ORIG_HEAD_REFLOG_MSG_RE = /^(merge\s|rebase\s\(|pull\s)/i;
|
|
7153
|
+
var GIT_RESTORE_RE = /^\s*git\s+restore\b/i;
|
|
7154
|
+
var GIT_RESTORE_STAGED_ONLY_RE = /(?:^|\s)(?:--staged|-S)(?:\s|$)/;
|
|
7155
|
+
var GIT_STASH_APPLY_RE = /^\s*git\s+stash\s+(?:pop|apply)\b/i;
|
|
7156
|
+
var PATCH_APPLY_SEGMENT_RE = /^\s*(?:\S*[/\\])?(?:git\s+apply|patch)\b/i;
|
|
7157
|
+
var PATCH_APPLY_INSPECT_ONLY_RE = /(?:^|\s)--(?:check|stat|numstat|summary|dry-run)(?:\s|=|$)/i;
|
|
7158
|
+
function isFormatterWriteSegment(segment) {
|
|
7159
|
+
return /(?:^|\s|[/\\])(?:prettier|eslint)\b/i.test(segment) && /(?:^|\s)--(?:write|fix)(?:\s|$)/i.test(segment);
|
|
7160
|
+
}
|
|
7161
|
+
function splitShellSegments(cmd) {
|
|
7162
|
+
const segments = [];
|
|
7163
|
+
let cur = "";
|
|
7164
|
+
let quote = null;
|
|
7165
|
+
for (let i = 0; i < cmd.length; i++) {
|
|
7166
|
+
const ch = cmd[i];
|
|
7167
|
+
if (quote !== null) {
|
|
7168
|
+
cur += ch;
|
|
7169
|
+
if (ch === quote) quote = null;
|
|
7170
|
+
continue;
|
|
7171
|
+
}
|
|
7172
|
+
if (ch === '"' || ch === "'") {
|
|
7173
|
+
quote = ch;
|
|
7174
|
+
cur += ch;
|
|
7175
|
+
continue;
|
|
7176
|
+
}
|
|
7177
|
+
if (ch === "\\" && i + 1 < cmd.length) {
|
|
7178
|
+
cur += ch + cmd[i + 1];
|
|
7179
|
+
i++;
|
|
7180
|
+
continue;
|
|
7181
|
+
}
|
|
7182
|
+
if (ch === "|" || ch === ";" || ch === "\n" || ch === "&") {
|
|
7183
|
+
segments.push(cur);
|
|
7184
|
+
cur = "";
|
|
7185
|
+
if ((ch === "|" || ch === "&") && cmd[i + 1] === ch) i++;
|
|
7186
|
+
continue;
|
|
7187
|
+
}
|
|
7188
|
+
cur += ch;
|
|
7189
|
+
}
|
|
7190
|
+
segments.push(cur);
|
|
7191
|
+
return segments.map((s) => s.trim()).filter((s) => s.length > 0);
|
|
7192
|
+
}
|
|
7193
|
+
function safeShlexSplit(segment) {
|
|
7194
|
+
try {
|
|
7195
|
+
return shlexSplit(segment);
|
|
7196
|
+
} catch {
|
|
7197
|
+
return null;
|
|
7198
|
+
}
|
|
7199
|
+
}
|
|
7200
|
+
function segmentCommandIs(segment, name2) {
|
|
7201
|
+
return new RegExp("^\\s*(?:\\S*[/\\\\])?" + name2 + "(?:\\.exe)?\\b", "i").test(segment);
|
|
7202
|
+
}
|
|
7203
|
+
function extractSedInPlaceFiles(segment) {
|
|
7204
|
+
if (!segmentCommandIs(segment, "sed")) return [];
|
|
7205
|
+
const tokens = safeShlexSplit(segment);
|
|
7206
|
+
if (tokens === null) return [];
|
|
7207
|
+
const rest = tokens.slice(1);
|
|
7208
|
+
const inPlaceIdx = rest.findIndex((t) => t === "--in-place" || t.startsWith("--in-place=") || /^-[a-zA-Z]*i/.test(t));
|
|
7209
|
+
if (inPlaceIdx === -1) return [];
|
|
7210
|
+
const files = [];
|
|
7211
|
+
let scriptTaken = rest.some((t) => t === "-e" || t === "-f" || t.startsWith("--expression") || t.startsWith("--file"));
|
|
7212
|
+
for (let i = 0; i < rest.length; i++) {
|
|
7213
|
+
const t = rest[i];
|
|
7214
|
+
if (i === inPlaceIdx) {
|
|
7215
|
+
if (t === "-i" && rest[i + 1] === "") i++;
|
|
7216
|
+
continue;
|
|
7217
|
+
}
|
|
7218
|
+
if (t === "-e" || t === "-f" || t === "--expression" || t === "--file") {
|
|
7219
|
+
i++;
|
|
7220
|
+
continue;
|
|
7221
|
+
}
|
|
7222
|
+
if (t.length > 1 && t.startsWith("-")) continue;
|
|
7223
|
+
if (!scriptTaken) {
|
|
7224
|
+
scriptTaken = true;
|
|
7225
|
+
continue;
|
|
7226
|
+
}
|
|
7227
|
+
files.push(t);
|
|
7228
|
+
}
|
|
7229
|
+
return files;
|
|
7230
|
+
}
|
|
7231
|
+
function extractTeeFiles(segment) {
|
|
7232
|
+
if (!segmentCommandIs(segment, "tee")) return [];
|
|
7233
|
+
const tokens = safeShlexSplit(segment);
|
|
7234
|
+
if (tokens === null) return [];
|
|
7235
|
+
return tokens.slice(1).filter((t) => t.length > 0 && !t.startsWith("-"));
|
|
7236
|
+
}
|
|
7237
|
+
function extractRedirectTargets(segment) {
|
|
7238
|
+
const targets = [];
|
|
7239
|
+
const re = /(?:^|\s)[0-9]?>>?\s*("[^"]*"|'[^']*'|[^\s|&<>]+)/g;
|
|
7240
|
+
let m;
|
|
7241
|
+
while ((m = re.exec(segment)) !== null) {
|
|
7242
|
+
const raw = m[1];
|
|
7243
|
+
targets.push(raw.length >= 2 && (raw[0] === '"' || raw[0] === "'") ? raw.slice(1, -1) : raw);
|
|
7244
|
+
}
|
|
7245
|
+
return targets;
|
|
7246
|
+
}
|
|
7247
|
+
function extractGitRestorePathspecs(cmd) {
|
|
7248
|
+
const m = /^\s*git\s+restore\b(.*)$/i.exec(cmd);
|
|
7249
|
+
if (m === null) return [];
|
|
7250
|
+
const tokens = safeShlexSplit(m[1] ?? "");
|
|
7251
|
+
if (tokens === null) return [];
|
|
7252
|
+
const dashDash = tokens.indexOf("--");
|
|
7253
|
+
if (dashDash !== -1) return tokens.slice(dashDash + 1);
|
|
7254
|
+
const specs = [];
|
|
7255
|
+
let skipNext = false;
|
|
7256
|
+
for (const t of tokens) {
|
|
7257
|
+
if (skipNext) {
|
|
7258
|
+
skipNext = false;
|
|
7259
|
+
continue;
|
|
7260
|
+
}
|
|
7261
|
+
if (t === "-s" || t === "--source") {
|
|
7262
|
+
skipNext = true;
|
|
7263
|
+
continue;
|
|
7264
|
+
}
|
|
7265
|
+
if (t.startsWith("-")) continue;
|
|
7266
|
+
specs.push(t);
|
|
7267
|
+
}
|
|
7268
|
+
return specs;
|
|
7269
|
+
}
|
|
7270
|
+
function gitRepoRoot(gitDir) {
|
|
7271
|
+
const toplevel = runGit(["rev-parse", "--show-toplevel"], { cwd: gitDir, timeoutMs: 5e3 });
|
|
7272
|
+
return toplevel.exitCode === 0 && toplevel.stdout.trim() !== "" ? toplevel.stdout.trim() : gitDir;
|
|
7273
|
+
}
|
|
7274
|
+
function expandGitPathspecs(specs, gitDir) {
|
|
7275
|
+
if (specs.length === 0) return [];
|
|
7276
|
+
const listed = runGit(["ls-files", "--full-name", "--", ...specs], { cwd: gitDir, timeoutMs: 5e3 });
|
|
7277
|
+
if (listed.exitCode !== 0) return [];
|
|
7278
|
+
const repoRoot = gitRepoRoot(gitDir);
|
|
7279
|
+
return listed.stdout.split("\n").map((l) => l.trim()).filter((l) => l.length > 0).map((rel) => resolveIndexPath(rel, repoRoot));
|
|
7280
|
+
}
|
|
7281
|
+
function workingTreeStatusPaths(gitDir) {
|
|
7282
|
+
const status = runGit(["status", "--porcelain"], { cwd: gitDir, timeoutMs: 5e3 });
|
|
7283
|
+
if (status.exitCode !== 0) return [];
|
|
7284
|
+
const repoRoot = gitRepoRoot(gitDir);
|
|
7285
|
+
const out = [];
|
|
7286
|
+
for (const line of status.stdout.split("\n")) {
|
|
7287
|
+
if (line.length < 4) continue;
|
|
7288
|
+
let rel = line.slice(3).trim();
|
|
7289
|
+
if (rel.length === 0) continue;
|
|
7290
|
+
const arrow = rel.lastIndexOf(" -> ");
|
|
7291
|
+
if (arrow !== -1) rel = rel.slice(arrow + 4);
|
|
7292
|
+
if (rel.startsWith('"')) {
|
|
7293
|
+
try {
|
|
7294
|
+
rel = JSON.parse(rel);
|
|
7295
|
+
} catch {
|
|
7296
|
+
continue;
|
|
7297
|
+
}
|
|
7298
|
+
}
|
|
7299
|
+
out.push(resolveIndexPath(rel, repoRoot));
|
|
7300
|
+
}
|
|
7301
|
+
return out;
|
|
7302
|
+
}
|
|
7303
|
+
function enqueueRewrittenPath(absPath) {
|
|
7304
|
+
if (/(?:^|[/\\])(?:NUL|nul)$/.test(absPath) || absPath.replace(/\\/g, "/").endsWith("/dev/null")) return;
|
|
7305
|
+
try {
|
|
7306
|
+
if (!statSync3(absPath).isFile()) return;
|
|
7307
|
+
} catch {
|
|
7308
|
+
return;
|
|
7309
|
+
}
|
|
7310
|
+
enqueueDirtyPathSafe(absPath, { alreadyResolved: true });
|
|
7311
|
+
}
|
|
7312
|
+
function enqueueNonHeadMovingRewrites(cmd, rawCmd, cwd) {
|
|
7313
|
+
const atCwd = (f) => resolveIndexPath(resolveCdHintPath(rawCmd, f, cwd), cwd);
|
|
7314
|
+
const paths = [];
|
|
7315
|
+
let needsStatusSweep = GIT_STASH_APPLY_RE.test(cmd);
|
|
7316
|
+
if (GIT_RESTORE_RE.test(cmd) && !GIT_RESTORE_STAGED_ONLY_RE.test(cmd)) {
|
|
7317
|
+
paths.push(...expandGitPathspecs(extractGitRestorePathspecs(cmd), cwd));
|
|
7318
|
+
}
|
|
7319
|
+
for (const segment of splitShellSegments(cmd)) {
|
|
7320
|
+
if (PATCH_APPLY_SEGMENT_RE.test(segment) && !PATCH_APPLY_INSPECT_ONLY_RE.test(segment)) needsStatusSweep = true;
|
|
7321
|
+
if (isFormatterWriteSegment(segment)) needsStatusSweep = true;
|
|
7322
|
+
for (const f of extractSedInPlaceFiles(segment)) paths.push(atCwd(f));
|
|
7323
|
+
for (const f of extractTeeFiles(segment)) paths.push(atCwd(f));
|
|
7324
|
+
for (const f of extractRedirectTargets(segment)) paths.push(atCwd(f));
|
|
7325
|
+
}
|
|
7326
|
+
if (needsStatusSweep) paths.push(...workingTreeStatusPaths(cwd));
|
|
7327
|
+
for (const p of paths) enqueueRewrittenPath(p);
|
|
7328
|
+
}
|
|
7148
7329
|
function isTempPath(fp) {
|
|
7149
7330
|
const norm = fp.replace(/\\/g, "/");
|
|
7150
7331
|
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);
|
|
@@ -7259,9 +7440,126 @@ function extractWslCatFile(cmd) {
|
|
|
7259
7440
|
if (flags === null) return null;
|
|
7260
7441
|
return { filePath, ...flags };
|
|
7261
7442
|
}
|
|
7443
|
+
function pythonScanText(text) {
|
|
7444
|
+
const dashC = /^python3?\s+-c\s*(['"])([\s\S]*)\1\s*$/.exec(text);
|
|
7445
|
+
if (dashC) {
|
|
7446
|
+
const source = dashC[2] ?? "";
|
|
7447
|
+
return { source, masked: maskPythonStrings(source) };
|
|
7448
|
+
}
|
|
7449
|
+
if (/^python3?\b/.test(text)) return { source: text, masked: text };
|
|
7450
|
+
return { source: text, masked: maskPythonStrings(text) };
|
|
7451
|
+
}
|
|
7452
|
+
function maskPythonStrings(text) {
|
|
7453
|
+
const out = text.split("");
|
|
7454
|
+
let i = 0;
|
|
7455
|
+
while (i < text.length) {
|
|
7456
|
+
const ch = text[i];
|
|
7457
|
+
if (ch !== "'" && ch !== '"') {
|
|
7458
|
+
i += 1;
|
|
7459
|
+
continue;
|
|
7460
|
+
}
|
|
7461
|
+
const triple = text.slice(i, i + 3);
|
|
7462
|
+
const delim = triple === ch + ch + ch ? triple : ch;
|
|
7463
|
+
let j = i + delim.length;
|
|
7464
|
+
while (j < text.length) {
|
|
7465
|
+
if (text[j] === "\\") {
|
|
7466
|
+
j += 2;
|
|
7467
|
+
continue;
|
|
7468
|
+
}
|
|
7469
|
+
if (text.slice(j, j + delim.length) === delim) break;
|
|
7470
|
+
out[j] = " ";
|
|
7471
|
+
j += 1;
|
|
7472
|
+
}
|
|
7473
|
+
i = j + delim.length;
|
|
7474
|
+
}
|
|
7475
|
+
return out.join("");
|
|
7476
|
+
}
|
|
7477
|
+
function pythonOpenCalls(text) {
|
|
7478
|
+
const { source, masked } = pythonScanText(text);
|
|
7479
|
+
const calls = [];
|
|
7480
|
+
for (const opener of masked.matchAll(/\bopen\s*\(/g)) {
|
|
7481
|
+
const start = (opener.index ?? 0) + opener[0].length;
|
|
7482
|
+
const args = [];
|
|
7483
|
+
let current = "";
|
|
7484
|
+
let depth = 0;
|
|
7485
|
+
let quote = "";
|
|
7486
|
+
let closed = false;
|
|
7487
|
+
for (let i = start; i < source.length; i++) {
|
|
7488
|
+
const ch = source[i] ?? "";
|
|
7489
|
+
if (quote !== "") {
|
|
7490
|
+
if (ch === "\\") {
|
|
7491
|
+
current += ch + (source[i + 1] ?? "");
|
|
7492
|
+
i += 1;
|
|
7493
|
+
continue;
|
|
7494
|
+
}
|
|
7495
|
+
if (ch === quote) quote = "";
|
|
7496
|
+
current += ch;
|
|
7497
|
+
continue;
|
|
7498
|
+
}
|
|
7499
|
+
if (ch === "'" || ch === '"') {
|
|
7500
|
+
quote = ch;
|
|
7501
|
+
current += ch;
|
|
7502
|
+
continue;
|
|
7503
|
+
}
|
|
7504
|
+
if (ch === "(" || ch === "[" || ch === "{") {
|
|
7505
|
+
depth += 1;
|
|
7506
|
+
current += ch;
|
|
7507
|
+
continue;
|
|
7508
|
+
}
|
|
7509
|
+
if (ch === ")" && depth === 0) {
|
|
7510
|
+
args.push(current);
|
|
7511
|
+
closed = true;
|
|
7512
|
+
break;
|
|
7513
|
+
}
|
|
7514
|
+
if (ch === ")" || ch === "]" || ch === "}") {
|
|
7515
|
+
depth -= 1;
|
|
7516
|
+
current += ch;
|
|
7517
|
+
continue;
|
|
7518
|
+
}
|
|
7519
|
+
if (ch === "," && depth === 0) {
|
|
7520
|
+
args.push(current);
|
|
7521
|
+
current = "";
|
|
7522
|
+
continue;
|
|
7523
|
+
}
|
|
7524
|
+
current += ch;
|
|
7525
|
+
}
|
|
7526
|
+
if (!closed) continue;
|
|
7527
|
+
const literalOf = (arg) => {
|
|
7528
|
+
const m = /^\s*r?(['"])([^'"]*)\1\s*$/.exec(arg);
|
|
7529
|
+
return m?.[2] ?? null;
|
|
7530
|
+
};
|
|
7531
|
+
const keyword = args.find((a) => /^\s*mode\s*=/.test(a));
|
|
7532
|
+
const positional = args[1] !== void 0 && /^\s*[A-Za-z_]\w*\s*=[^=]/.test(args[1]) ? void 0 : args[1];
|
|
7533
|
+
const modeArg = keyword !== void 0 ? keyword.replace(/^\s*mode\s*=/, "") : positional;
|
|
7534
|
+
calls.push({
|
|
7535
|
+
pathLiteral: args.length > 0 ? literalOf(args[0] ?? "") : null,
|
|
7536
|
+
hasMode: modeArg !== void 0,
|
|
7537
|
+
modeLiteral: modeArg === void 0 ? null : literalOf(modeArg)
|
|
7538
|
+
});
|
|
7539
|
+
}
|
|
7540
|
+
return calls;
|
|
7541
|
+
}
|
|
7542
|
+
function pythonOpenWritesAFile(text) {
|
|
7543
|
+
return pythonOpenCalls(text).some(
|
|
7544
|
+
(call) => call.hasMode && (call.modeLiteral === null || /[wax+]/.test(call.modeLiteral))
|
|
7545
|
+
);
|
|
7546
|
+
}
|
|
7547
|
+
function pythonWritesThroughFileObject(text) {
|
|
7548
|
+
const { masked } = pythonScanText(text);
|
|
7549
|
+
for (const call of masked.matchAll(/\.write(?:lines)?\s*\(/g)) {
|
|
7550
|
+
const receiver = masked.slice(0, call.index ?? 0);
|
|
7551
|
+
if (/(?:^|[^\w.])(?:sys\s*\.\s*)?(?:stdout|stderr|stdin)(?:\s*\.\s*buffer)?$/.test(receiver)) continue;
|
|
7552
|
+
return true;
|
|
7553
|
+
}
|
|
7554
|
+
return false;
|
|
7555
|
+
}
|
|
7556
|
+
function pythonOpenPathsAreAllLiteral(text) {
|
|
7557
|
+
const calls = pythonOpenCalls(text);
|
|
7558
|
+
return calls.length > 0 && calls.every((call) => call.pathLiteral !== null);
|
|
7559
|
+
}
|
|
7262
7560
|
function extractPythonFileRead(cmd) {
|
|
7263
7561
|
if (!/^python3?\b/.test(cmd)) return null;
|
|
7264
|
-
if (
|
|
7562
|
+
if (pythonOpenWritesAFile(cmd) || pythonWritesThroughFileObject(cmd)) return null;
|
|
7265
7563
|
const outputOpen = /open\s*\(\s*r?['"]([^'"]+\.output)['"]/i.exec(cmd);
|
|
7266
7564
|
if (outputOpen?.[1]) {
|
|
7267
7565
|
const filePath = outputOpen[1];
|
|
@@ -7272,7 +7570,7 @@ function extractPythonFileRead(cmd) {
|
|
|
7272
7570
|
const heredocMatch = /^python3?\s+-\s+<<\s*'?(\w+)'?\s*\n([\s\S]*?)\n\1\s*$/.exec(cmd);
|
|
7273
7571
|
if (heredocMatch) {
|
|
7274
7572
|
const body = heredocMatch[2] ?? "";
|
|
7275
|
-
if (
|
|
7573
|
+
if (pythonOpenWritesAFile(body) || pythonWritesThroughFileObject(body)) return null;
|
|
7276
7574
|
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);
|
|
7277
7575
|
if (heredocOpen?.[1]) {
|
|
7278
7576
|
const filePath = heredocOpen[1];
|
|
@@ -7280,7 +7578,7 @@ function extractPythonFileRead(cmd) {
|
|
|
7280
7578
|
const isDoc = /\.(?:md|mdx|rst|txt)$/i.test(filePath);
|
|
7281
7579
|
return { filePath, isDoc, isTranscript: false };
|
|
7282
7580
|
}
|
|
7283
|
-
if (/open\s*\(/.test(body)) {
|
|
7581
|
+
if (/open\s*\(/.test(body) && !pythonOpenPathsAreAllLiteral(body)) {
|
|
7284
7582
|
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);
|
|
7285
7583
|
if (literal2?.[1]) {
|
|
7286
7584
|
const filePath = literal2[1];
|
|
@@ -7301,7 +7599,7 @@ function extractPythonFileRead(cmd) {
|
|
|
7301
7599
|
const isDoc = /\.(?:md|mdx|rst|txt)$/i.test(filePath);
|
|
7302
7600
|
return { filePath, isDoc, isTranscript: false };
|
|
7303
7601
|
}
|
|
7304
|
-
if (/open\s*\(/.test(cmd)) {
|
|
7602
|
+
if (/open\s*\(/.test(cmd) && !pythonOpenPathsAreAllLiteral(cmd)) {
|
|
7305
7603
|
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);
|
|
7306
7604
|
if (literal2) {
|
|
7307
7605
|
const filePath = literal2[1] ?? "";
|
|
@@ -7348,6 +7646,30 @@ function extractSedRange(cmd) {
|
|
|
7348
7646
|
if (isTempPath(filePath)) return null;
|
|
7349
7647
|
return { filePath, ranges };
|
|
7350
7648
|
}
|
|
7649
|
+
function extractAwkRange(cmd) {
|
|
7650
|
+
const m = /^awk\s+(?:'([^']+)'|"([^"]+)")\s+(?:"([^"]+)"|'([^']+)'|(\S+))(?:\s+2>\/dev\/null)?\s*$/.exec(cmd);
|
|
7651
|
+
if (!m) return null;
|
|
7652
|
+
const program = m[1] ?? m[2];
|
|
7653
|
+
if (program === void 0) return null;
|
|
7654
|
+
const cmp = /^\s*NR\s*>=\s*(\d+)\s*&&\s*NR\s*<=\s*(\d+)\s*$/.exec(program);
|
|
7655
|
+
const rng = /^\s*NR\s*==\s*(\d+)\s*,\s*NR\s*==\s*(\d+)\s*$/.exec(program);
|
|
7656
|
+
const hit = cmp ?? rng;
|
|
7657
|
+
if (!hit) return null;
|
|
7658
|
+
const start = parseInt(hit[1], 10);
|
|
7659
|
+
const end = parseInt(hit[2], 10);
|
|
7660
|
+
if (start < 1 || end < start) return null;
|
|
7661
|
+
const filePath = m[3] ?? m[4] ?? m[5];
|
|
7662
|
+
if (filePath === void 0) return null;
|
|
7663
|
+
if (isTempPath(filePath)) return null;
|
|
7664
|
+
return { filePath, ranges: [[start, end]] };
|
|
7665
|
+
}
|
|
7666
|
+
function extractLineRangeRead(cmd) {
|
|
7667
|
+
const sed = extractSedRange(cmd);
|
|
7668
|
+
if (sed !== null) return { ...sed, tool: "sed" };
|
|
7669
|
+
const awk = extractAwkRange(cmd);
|
|
7670
|
+
if (awk !== null) return { ...awk, tool: "awk" };
|
|
7671
|
+
return null;
|
|
7672
|
+
}
|
|
7351
7673
|
var SYMBOL_BEARING_LANGUAGES = /* @__PURE__ */ new Set([
|
|
7352
7674
|
"python",
|
|
7353
7675
|
"typescript",
|
|
@@ -7378,11 +7700,11 @@ var SYMBOL_BEARING_LANGUAGES = /* @__PURE__ */ new Set([
|
|
|
7378
7700
|
"salesforce_metadata",
|
|
7379
7701
|
"salesforce_markup"
|
|
7380
7702
|
]);
|
|
7381
|
-
function sedRangeHint(filePath, ranges) {
|
|
7703
|
+
function sedRangeHint(filePath, ranges, tool) {
|
|
7382
7704
|
const lang = detectLanguage(filePath);
|
|
7383
7705
|
const rangeReads = ranges.map(([s, e]) => '`token-goat read "' + filePath + "@" + s + "-" + e + '"`');
|
|
7384
7706
|
const allReads = rangeReads.length === 2 ? rangeReads.join(" and ") : rangeReads.length >= 3 ? rangeReads.slice(0, -1).join(", ") + ", and " + rangeReads[rangeReads.length - 1] : rangeReads[0];
|
|
7385
|
-
const prefix = "`sed -n` line-range reads bypass read hooks. ";
|
|
7707
|
+
const prefix = "`" + (tool === "awk" ? "awk" : "sed -n") + "` line-range reads bypass read hooks. ";
|
|
7386
7708
|
if (lang === "markdown") {
|
|
7387
7709
|
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.";
|
|
7388
7710
|
}
|
|
@@ -7407,7 +7729,7 @@ function findRangeOverlap(prior, start, end) {
|
|
|
7407
7729
|
return best;
|
|
7408
7730
|
}
|
|
7409
7731
|
function sedOverlapHint(filePath, prior, start, end) {
|
|
7410
|
-
const base = "You already read lines " + prior[0] + "-" + prior[1] + " of " + filePath + " via an earlier
|
|
7732
|
+
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. ";
|
|
7411
7733
|
const segments = [];
|
|
7412
7734
|
if (start < prior[0]) segments.push([start, Math.min(end, prior[0] - 1)]);
|
|
7413
7735
|
if (end > prior[1]) segments.push([Math.max(start, prior[1] + 1), end]);
|
|
@@ -7708,11 +8030,12 @@ function maybeCompressRewrite(event, rawCmd, cmd) {
|
|
|
7708
8030
|
}
|
|
7709
8031
|
if (!cfg.enabled) return null;
|
|
7710
8032
|
if (!canRunWrappedShell()) return null;
|
|
7711
|
-
const
|
|
8033
|
+
const gateCmd = stripTrailingStderrRedirect(cmd);
|
|
8034
|
+
const detected = detectFromCommand(gateCmd, getCwd(event));
|
|
7712
8035
|
let filterName;
|
|
7713
8036
|
if (detected !== null) {
|
|
7714
8037
|
filterName = detected.filter.name;
|
|
7715
|
-
} else if (isCompressibleSingleCommand(
|
|
8038
|
+
} else if (isCompressibleSingleCommand(gateCmd)) {
|
|
7716
8039
|
filterName = "generic";
|
|
7717
8040
|
} else {
|
|
7718
8041
|
return null;
|
|
@@ -7929,9 +8252,9 @@ function preBashHandlerInner(event) {
|
|
|
7929
8252
|
"Use `token-goat outline <file>` to see symbol names and line counts without loading files."
|
|
7930
8253
|
);
|
|
7931
8254
|
}
|
|
7932
|
-
const sedRange =
|
|
8255
|
+
const sedRange = extractLineRangeRead(cmd);
|
|
7933
8256
|
if (sedRange !== null) {
|
|
7934
|
-
const { filePath, ranges } = sedRange;
|
|
8257
|
+
const { filePath, ranges, tool } = sedRange;
|
|
7935
8258
|
const hintPath = cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath;
|
|
7936
8259
|
recordStat("session_hint", 0, 0);
|
|
7937
8260
|
const sedDedupKey = resolveIndexPath(hintPath, preHookCwd ?? process.cwd());
|
|
@@ -7947,7 +8270,7 @@ function preBashHandlerInner(event) {
|
|
|
7947
8270
|
}
|
|
7948
8271
|
}
|
|
7949
8272
|
const hints = [...overlapHints];
|
|
7950
|
-
if (freshRanges.length > 0) hints.push(sedRangeHint(hintPath, freshRanges));
|
|
8273
|
+
if (freshRanges.length > 0) hints.push(sedRangeHint(hintPath, freshRanges, tool));
|
|
7951
8274
|
return contextOutput(hints.join(" "));
|
|
7952
8275
|
}
|
|
7953
8276
|
const catJsonPipe = extractCatJsonPipe(cmd);
|
|
@@ -8200,7 +8523,7 @@ function preBashHandlerInner(event) {
|
|
|
8200
8523
|
);
|
|
8201
8524
|
}
|
|
8202
8525
|
}
|
|
8203
|
-
if (!isBuildCommand(cmd) && detectFromCommand(cmd, preHookCwd ?? void 0) === null) return passOutput();
|
|
8526
|
+
if (!isBuildCommand(cmd) && detectFromCommand(stripTrailingStderrRedirect(cmd), preHookCwd ?? void 0) === null) return passOutput();
|
|
8204
8527
|
const cmdHash = shortFingerprint(stripOutputPipeline(cmd));
|
|
8205
8528
|
const outputId = getBashOutputId(cmdHash);
|
|
8206
8529
|
const entryRaw = outputId !== null ? getBashOutput(outputId) : null;
|
|
@@ -8315,8 +8638,7 @@ async function postBashHandler(event) {
|
|
|
8315
8638
|
}
|
|
8316
8639
|
const mutationDiff = runGit(["diff", "--name-only", diffBase, "HEAD"], { cwd: gitDir, timeoutMs: 5e3 });
|
|
8317
8640
|
if (mutationDiff.exitCode === 0) {
|
|
8318
|
-
const
|
|
8319
|
-
const repoRoot = toplevel.exitCode === 0 && toplevel.stdout.trim() !== "" ? toplevel.stdout.trim() : gitDir;
|
|
8641
|
+
const repoRoot = gitRepoRoot(gitDir);
|
|
8320
8642
|
for (const rel of mutationDiff.stdout.split("\n")) {
|
|
8321
8643
|
const trimmed = rel.trim();
|
|
8322
8644
|
if (trimmed.length === 0) continue;
|
|
@@ -8324,6 +8646,9 @@ async function postBashHandler(event) {
|
|
|
8324
8646
|
}
|
|
8325
8647
|
}
|
|
8326
8648
|
}
|
|
8649
|
+
if (exitCode === null || exitCode === 0) {
|
|
8650
|
+
enqueueNonHeadMovingRewrites(cmd, rawCmd, cwd ?? process.cwd());
|
|
8651
|
+
}
|
|
8327
8652
|
const curlDl = extractCurlDownload(cmd);
|
|
8328
8653
|
if (curlDl !== null && (exitCode === null || exitCode === 0)) {
|
|
8329
8654
|
const resolvedOutputPath = resolveIndexPath(curlDl.outputPath, cwd ?? process.cwd());
|
|
@@ -8971,8 +9296,21 @@ function compressGWorkspaceMcpResult(toolName, resultText) {
|
|
|
8971
9296
|
const summarized = summarizeAttachments(trimmed);
|
|
8972
9297
|
return finishWithGenericPass(resultText, summarized);
|
|
8973
9298
|
}
|
|
9299
|
+
var DATA_URL_BASE64_RE = /data:([a-zA-Z0-9.+-]+\/[a-zA-Z0-9.+-]+);base64,([A-Za-z0-9+/=]+)/g;
|
|
9300
|
+
var MIN_BASE64_PAYLOAD_LEN = 100;
|
|
9301
|
+
function stripBase64DataUrls(resultText) {
|
|
9302
|
+
let changed = false;
|
|
9303
|
+
const out = resultText.replace(DATA_URL_BASE64_RE, (match, mime, payload) => {
|
|
9304
|
+
if (payload.length < MIN_BASE64_PAYLOAD_LEN) return match;
|
|
9305
|
+
changed = true;
|
|
9306
|
+
return `data:${mime};base64,<stripped ${payload.length} bytes>`;
|
|
9307
|
+
});
|
|
9308
|
+
return changed ? out : null;
|
|
9309
|
+
}
|
|
8974
9310
|
function compressMcpResultWithPacks(toolName, resultText) {
|
|
8975
|
-
|
|
9311
|
+
const base64Stripped = stripBase64DataUrls(resultText);
|
|
9312
|
+
const text = base64Stripped ?? resultText;
|
|
9313
|
+
return compressGithubMcpResult(toolName, text) ?? compressBrowserMcpResult(toolName, text) ?? compressGWorkspaceMcpResult(toolName, text) ?? base64Stripped;
|
|
8976
9314
|
}
|
|
8977
9315
|
|
|
8978
9316
|
// src/hooks_mcp.ts
|