token-goat 2.9.9 → 2.9.11
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 +54 -11
- package/SECURITY.md +17 -19
- package/THIRD_PARTY_NOTICES.md +667 -0
- package/dist/token-goat-chunk-2ZUPQYLO.mjs +3728 -0
- package/dist/{token-goat-chunk-LQ3SGWIW.mjs → token-goat-chunk-3ZALKJ23.mjs} +69 -37
- package/dist/{token-goat-chunk-WBPVYAEO.mjs → token-goat-chunk-4NZUXC4F.mjs} +3 -2
- package/dist/{token-goat-chunk-KIEOFWLL.mjs → token-goat-chunk-AAEYU2U2.mjs} +5307 -6350
- package/dist/{token-goat-chunk-OBDTBOQA.mjs → token-goat-chunk-FDURVZQD.mjs} +10 -2
- package/dist/{token-goat-chunk-DRH4CVGF.mjs → token-goat-chunk-HDL77BN3.mjs} +11 -7
- package/dist/{token-goat-chunk-LYWIRYCF.mjs → token-goat-chunk-LHLQFGWQ.mjs} +1 -1
- package/dist/{token-goat-chunk-L6Q2XM5X.mjs → token-goat-chunk-LKOYKSID.mjs} +1275 -655
- package/dist/{token-goat-chunk-P5SQX2VK.mjs → token-goat-chunk-MKITQ5RY.mjs} +354 -180
- package/dist/{token-goat-chunk-PNMGVJ4C.mjs → token-goat-chunk-MRZ555B3.mjs} +2699 -2175
- package/dist/{token-goat-chunk-K5GKX6ND.mjs → token-goat-chunk-PJPFOOGM.mjs} +6 -5
- package/dist/{token-goat-chunk-FQD3OB5W.mjs → token-goat-chunk-QCQUIPIP.mjs} +6068 -1687
- package/dist/{token-goat-chunk-BYDDWQ2T.mjs → token-goat-chunk-SY7WTZMW.mjs} +5043 -7721
- package/dist/{token-goat-chunk-PXWBHSFB.mjs → token-goat-chunk-T6M7DAW3.mjs} +31 -4
- package/dist/{token-goat-chunk-QCHD2ENV.mjs → token-goat-chunk-V53Z47ZH.mjs} +85 -328
- package/dist/token-goat-chunk-Y5VKNLVD.mjs +5942 -0
- package/dist/token-goat-chunk-YCKCFYTM.mjs +3564 -0
- package/dist/token-goat-hook.mjs +6 -5
- package/dist/token-goat.core.mjs +8 -6
- package/docs/C4_RUNTIME_ARCHITECTURE.md +1 -1
- package/docs/cli.md +4 -1
- package/docs/install.md +131 -11
- package/docs/security.md +3 -1
- package/package.json +8 -3
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { createRequire as __cjsRequire } from 'node:module';
|
|
2
2
|
const require = __cjsRequire(import.meta.url);
|
|
3
|
+
import {
|
|
4
|
+
canRunWrappedShell,
|
|
5
|
+
clipToDeliveryCap,
|
|
6
|
+
deliveredOutputBytes
|
|
7
|
+
} from "./token-goat-chunk-FDURVZQD.mjs";
|
|
3
8
|
import {
|
|
4
9
|
DEFAULT_RECONCILE_BUDGET_MS,
|
|
5
10
|
HOOK_EVENTS,
|
|
@@ -10,7 +15,6 @@ import {
|
|
|
10
15
|
getWebOutput,
|
|
11
16
|
isReconcileClean,
|
|
12
17
|
lineMayCarryResidentSignal,
|
|
13
|
-
normalizePayload,
|
|
14
18
|
readStdinJson,
|
|
15
19
|
readTranscriptTail,
|
|
16
20
|
reconcileProject,
|
|
@@ -18,7 +22,7 @@ import {
|
|
|
18
22
|
storeWebOutput,
|
|
19
23
|
summarizeResidentContext,
|
|
20
24
|
taskListPruneHint
|
|
21
|
-
} from "./token-goat-chunk-
|
|
25
|
+
} from "./token-goat-chunk-V53Z47ZH.mjs";
|
|
22
26
|
import {
|
|
23
27
|
BASH_OUTPUT_SUBDIR,
|
|
24
28
|
GENERIC_SERVED_OUTPUT_KEY,
|
|
@@ -38,10 +42,10 @@ import {
|
|
|
38
42
|
countSymbols,
|
|
39
43
|
enqueueDirtyPathSafe,
|
|
40
44
|
ensureWorkerAlive,
|
|
41
|
-
estimateTokens,
|
|
42
45
|
extractCompactFromMarker,
|
|
43
46
|
extractMarkdownHeadings,
|
|
44
47
|
fenceUntrusted,
|
|
48
|
+
fenceUntrustedSpans,
|
|
45
49
|
fenceWithMatches,
|
|
46
50
|
foldDelivery,
|
|
47
51
|
foldDetail,
|
|
@@ -90,6 +94,7 @@ import {
|
|
|
90
94
|
planMarkdownOutline,
|
|
91
95
|
planServedElisions,
|
|
92
96
|
planSourceSkeleton,
|
|
97
|
+
preToolPathDeclined,
|
|
93
98
|
probeImageMeta,
|
|
94
99
|
recordBashOutput,
|
|
95
100
|
recordBashRerun,
|
|
@@ -122,68 +127,51 @@ import {
|
|
|
122
127
|
takePendingLargeFileHint,
|
|
123
128
|
visionTokens,
|
|
124
129
|
visionTokensSaved,
|
|
130
|
+
vscodePathAllowed,
|
|
125
131
|
wasCliReadThisSession,
|
|
126
132
|
wasFileReadThisSession,
|
|
127
133
|
wasHintShown
|
|
128
|
-
} from "./token-goat-chunk-
|
|
129
|
-
import {
|
|
130
|
-
bashOutputCapBytes,
|
|
131
|
-
canRunWrappedShell,
|
|
132
|
-
deliveredOutputBytes
|
|
133
|
-
} from "./token-goat-chunk-OBDTBOQA.mjs";
|
|
134
|
+
} from "./token-goat-chunk-SY7WTZMW.mjs";
|
|
134
135
|
import {
|
|
135
136
|
BODY_FIRST_TOOL_RESPONSE_KEYS,
|
|
136
|
-
ENV_KEYS,
|
|
137
|
-
IDENTICAL_READ_MIN_BODY_BYTES,
|
|
138
137
|
OUTPUT_FIRST_TOOL_RESPONSE_KEYS,
|
|
139
|
-
PER_FILE_COUNTERFACTUAL_CEILING,
|
|
140
|
-
UNTRUSTED_TOOL_TAG,
|
|
141
|
-
VERSION,
|
|
142
138
|
compressOutput,
|
|
143
|
-
containsLineRun,
|
|
144
139
|
contextOutput,
|
|
145
|
-
countNoun,
|
|
146
140
|
dedupeConsecutive,
|
|
147
141
|
denyOutput,
|
|
148
142
|
detectFromCommand,
|
|
149
143
|
detectHarness,
|
|
150
144
|
detectLanguage,
|
|
151
|
-
displaySafePath,
|
|
152
|
-
displaySafeText,
|
|
153
145
|
emitRewrite,
|
|
154
146
|
emitRewriteIfChanged,
|
|
155
147
|
envBool,
|
|
156
148
|
envInt,
|
|
157
|
-
|
|
149
|
+
estimateTokens,
|
|
158
150
|
extractToolResponseField,
|
|
159
151
|
extractToolResultText,
|
|
160
|
-
fenceUntrustedFileContent,
|
|
161
152
|
filterByName,
|
|
162
153
|
findProject,
|
|
163
|
-
foldPath,
|
|
164
154
|
getCwd,
|
|
165
155
|
getDb,
|
|
166
156
|
getFilePath,
|
|
167
157
|
getHarnessName,
|
|
168
158
|
getToolInput,
|
|
169
159
|
getToolName,
|
|
170
|
-
globalDbPath,
|
|
171
160
|
hasBareBackgroundOrNewline,
|
|
172
161
|
hasUnquotedOperator,
|
|
173
162
|
isMcpErrorResponse,
|
|
174
163
|
isRewriteWorthwhile,
|
|
175
164
|
isUnderSystemTemp,
|
|
165
|
+
languageHasFlag,
|
|
176
166
|
loadConfig,
|
|
177
167
|
makeDedupHintHandlers,
|
|
178
|
-
|
|
179
|
-
normalizePath,
|
|
168
|
+
normalizePayload,
|
|
180
169
|
passOutput,
|
|
170
|
+
readCopilotHooksOwners,
|
|
181
171
|
recordStat,
|
|
182
172
|
redactSecrets,
|
|
183
173
|
registerHook,
|
|
184
|
-
resolveIndexPath,
|
|
185
174
|
resolveMinNetSavingsBytes,
|
|
186
|
-
runGit,
|
|
187
175
|
runHook,
|
|
188
176
|
savedTokensFromBytes,
|
|
189
177
|
serializeOutput,
|
|
@@ -191,10 +179,35 @@ import {
|
|
|
191
179
|
shlexSplit,
|
|
192
180
|
shortFingerprint,
|
|
193
181
|
splitOwnTrailingNotices,
|
|
194
|
-
stripAnsiEscapes
|
|
182
|
+
stripAnsiEscapes
|
|
183
|
+
} from "./token-goat-chunk-AAEYU2U2.mjs";
|
|
184
|
+
import {
|
|
185
|
+
ENV_KEYS,
|
|
186
|
+
IDENTICAL_READ_MIN_BODY_BYTES,
|
|
187
|
+
PER_FILE_COUNTERFACTUAL_CEILING,
|
|
188
|
+
UNTRUSTED_TOOL_TAG,
|
|
189
|
+
VERSION,
|
|
190
|
+
atomicWriteText,
|
|
191
|
+
containsLineRun,
|
|
192
|
+
countNoun,
|
|
193
|
+
dataDir,
|
|
194
|
+
displaySafePath,
|
|
195
|
+
displaySafeText,
|
|
196
|
+
ensureDirSync,
|
|
197
|
+
escapesOntoNetworkThroughLinks,
|
|
198
|
+
extractErrorMessage,
|
|
199
|
+
fenceUntrustedFileContent,
|
|
200
|
+
foldPath,
|
|
201
|
+
globalDbPath,
|
|
202
|
+
hasCreatedConfig,
|
|
203
|
+
isInsideRoot,
|
|
204
|
+
neutralizeSpokenMarkers,
|
|
205
|
+
normalizePath,
|
|
206
|
+
resolveIndexPath,
|
|
207
|
+
runGit,
|
|
195
208
|
toDisplayPath,
|
|
196
209
|
toKB
|
|
197
|
-
} from "./token-goat-chunk-
|
|
210
|
+
} from "./token-goat-chunk-YCKCFYTM.mjs";
|
|
198
211
|
import {
|
|
199
212
|
init_define_import_meta_env
|
|
200
213
|
} from "./token-goat-chunk-A37V4PBF.mjs";
|
|
@@ -244,6 +257,120 @@ function stripUnsafeSuggestions(text) {
|
|
|
244
257
|
}
|
|
245
258
|
}
|
|
246
259
|
|
|
260
|
+
// src/vscode_duplicate.ts
|
|
261
|
+
init_define_import_meta_env();
|
|
262
|
+
import * as fs from "node:fs";
|
|
263
|
+
import * as os from "node:os";
|
|
264
|
+
import * as path from "node:path";
|
|
265
|
+
var VSCODE_HOOKS_DIR_ENV = "TOKEN_GOAT_VSCODE_HOOKS_DIR";
|
|
266
|
+
var MARKER_MAX_AGE_MS = 60 * 60 * 1e3;
|
|
267
|
+
var PRUNE_INTERVAL_MS = 5 * 60 * 1e3;
|
|
268
|
+
function markerDir() {
|
|
269
|
+
return path.join(dataDir(), "vscode-dedupe");
|
|
270
|
+
}
|
|
271
|
+
function foldDir(p) {
|
|
272
|
+
const forward = p.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
273
|
+
return process.platform === "win32" ? forward.toLowerCase() : forward;
|
|
274
|
+
}
|
|
275
|
+
function shimHooksDir() {
|
|
276
|
+
const raw = process.env[VSCODE_HOOKS_DIR_ENV];
|
|
277
|
+
return typeof raw === "string" && raw !== "" ? raw : void 0;
|
|
278
|
+
}
|
|
279
|
+
function userHooksDir() {
|
|
280
|
+
return path.join(os.homedir(), ".copilot", "hooks");
|
|
281
|
+
}
|
|
282
|
+
function userScopeCopyIsRedundant(event) {
|
|
283
|
+
const hooksDir = shimHooksDir();
|
|
284
|
+
if (hooksDir === void 0) return false;
|
|
285
|
+
if (foldDir(hooksDir) !== foldDir(userHooksDir())) return false;
|
|
286
|
+
const cwd = getCwd(event);
|
|
287
|
+
if (cwd === void 0 || cwd === "") return false;
|
|
288
|
+
const projectHooksDir = path.join(cwd, ".github", "hooks");
|
|
289
|
+
if (!vscodePathAllowed(projectHooksDir, cwd)) return false;
|
|
290
|
+
if (!readCopilotHooksOwners(projectHooksDir).has("vscode")) return false;
|
|
291
|
+
return hasCreatedConfig(path.join(projectHooksDir, "token-goat.json"));
|
|
292
|
+
}
|
|
293
|
+
function logStandDown(reason, event) {
|
|
294
|
+
if (process.env["TOKEN_GOAT_LOG"] === void 0 || process.env["TOKEN_GOAT_LOG"] === "") return;
|
|
295
|
+
try {
|
|
296
|
+
process.stderr.write(displaySafeText(`token-goat: vscode hook stood down (${reason}) event=${event.eventName} cwd=${getCwd(event) ?? "<none>"}`) + "\n");
|
|
297
|
+
} catch {
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
function markerName(sessionId, eventName, timestamp) {
|
|
301
|
+
const raw = `${sessionId}\0${eventName}\0${timestamp}`;
|
|
302
|
+
let h1 = 2166136261;
|
|
303
|
+
let h2 = 16777619;
|
|
304
|
+
for (let i = 0; i < raw.length; i++) {
|
|
305
|
+
h1 = Math.imul(h1 ^ raw.charCodeAt(i), 16777619) >>> 0;
|
|
306
|
+
h2 = Math.imul(h2 + raw.charCodeAt(i), 2246822507) >>> 0;
|
|
307
|
+
}
|
|
308
|
+
return `${h1.toString(16).padStart(8, "0")}${h2.toString(16).padStart(8, "0")}`;
|
|
309
|
+
}
|
|
310
|
+
function pruneMarkers(dir) {
|
|
311
|
+
const stamp = path.join(dir, ".pruned");
|
|
312
|
+
try {
|
|
313
|
+
const last = fs.statSync(stamp).mtimeMs;
|
|
314
|
+
if (Date.now() - last < PRUNE_INTERVAL_MS) return;
|
|
315
|
+
} catch {
|
|
316
|
+
}
|
|
317
|
+
try {
|
|
318
|
+
atomicWriteText(stamp, "");
|
|
319
|
+
} catch {
|
|
320
|
+
}
|
|
321
|
+
const cutoff = Date.now() - MARKER_MAX_AGE_MS;
|
|
322
|
+
let entries;
|
|
323
|
+
try {
|
|
324
|
+
entries = fs.readdirSync(dir);
|
|
325
|
+
} catch {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
for (const entry of entries) {
|
|
329
|
+
if (entry === ".pruned") continue;
|
|
330
|
+
const full = path.join(dir, entry);
|
|
331
|
+
try {
|
|
332
|
+
if (fs.statSync(full).mtimeMs < cutoff) fs.rmSync(full, { force: true });
|
|
333
|
+
} catch {
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
function alreadyClaimed(sessionId, eventName, timestamp) {
|
|
338
|
+
const dir = markerDir();
|
|
339
|
+
try {
|
|
340
|
+
ensureDirSync(dir);
|
|
341
|
+
} catch {
|
|
342
|
+
return false;
|
|
343
|
+
}
|
|
344
|
+
const marker = path.join(dir, markerName(sessionId, eventName, timestamp));
|
|
345
|
+
try {
|
|
346
|
+
fs.closeSync(fs.openSync(marker, "wx"));
|
|
347
|
+
} catch (e) {
|
|
348
|
+
if (e.code === "EEXIST") return true;
|
|
349
|
+
return false;
|
|
350
|
+
}
|
|
351
|
+
pruneMarkers(dir);
|
|
352
|
+
return false;
|
|
353
|
+
}
|
|
354
|
+
function shouldSuppressDuplicateVscodeHook(event, harness) {
|
|
355
|
+
if (harness !== "vscode") return false;
|
|
356
|
+
try {
|
|
357
|
+
if (userScopeCopyIsRedundant(event)) {
|
|
358
|
+
logStandDown("a project-scope install owns this workspace", event);
|
|
359
|
+
return true;
|
|
360
|
+
}
|
|
361
|
+
if (getFilePath(event) !== void 0) return false;
|
|
362
|
+
const sessionId = event.sessionId;
|
|
363
|
+
if (typeof sessionId !== "string" || sessionId === "") return false;
|
|
364
|
+
const timestamp = event.raw["timestamp"];
|
|
365
|
+
if (typeof timestamp !== "string" || timestamp === "") return false;
|
|
366
|
+
if (!alreadyClaimed(sessionId, event.eventName, timestamp)) return false;
|
|
367
|
+
logStandDown("another copy already claimed this event", event);
|
|
368
|
+
return true;
|
|
369
|
+
} catch {
|
|
370
|
+
return false;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
247
374
|
// src/hooks_grep.ts
|
|
248
375
|
init_define_import_meta_env();
|
|
249
376
|
function grepIntInput(toolInput, key) {
|
|
@@ -258,7 +385,7 @@ function grepIntInput(toolInput, key) {
|
|
|
258
385
|
function grepSignature(toolInput) {
|
|
259
386
|
const pattern = toolInput["pattern"];
|
|
260
387
|
if (typeof pattern !== "string" || pattern === "") return null;
|
|
261
|
-
const
|
|
388
|
+
const path4 = typeof toolInput["path"] === "string" ? toolInput["path"] : "";
|
|
262
389
|
const outputMode = typeof toolInput["output_mode"] === "string" ? toolInput["output_mode"] : "files_with_matches";
|
|
263
390
|
const glob = typeof toolInput["glob"] === "string" ? toolInput["glob"] : "";
|
|
264
391
|
const type = typeof toolInput["type"] === "string" ? toolInput["type"] : "";
|
|
@@ -273,7 +400,7 @@ function grepSignature(toolInput) {
|
|
|
273
400
|
const offset = grepIntInput(toolInput, "offset");
|
|
274
401
|
return JSON.stringify([
|
|
275
402
|
pattern,
|
|
276
|
-
|
|
403
|
+
path4,
|
|
277
404
|
outputMode,
|
|
278
405
|
glob,
|
|
279
406
|
type,
|
|
@@ -356,7 +483,6 @@ function foldGrepContentHandler(event) {
|
|
|
356
483
|
}
|
|
357
484
|
}
|
|
358
485
|
var DOC_EXT_RE = /\.(?:md|mdx|rst|txt)$/i;
|
|
359
|
-
var SOURCE_EXT_RE = /\.(?:java|py|ts|tsx|js|jsx|go|rb|rs|cpp|cc|cxx|c|h|hpp|kt|swift|cs|php|scala|clj|css|scss|sass|less)$/i;
|
|
360
486
|
var STRUCTURAL_DOC_PATTERN_RE = /^(?:\^)?#+\s*/;
|
|
361
487
|
var STRUCTURAL_SOURCE_PATTERN_RE = /^(?:\^|\s)*(?:def|class|function|async\s+def|async\s+function|export\s+(?:default\s+)?(?:class|function|interface|type|const|enum)|func|fn|struct|interface|impl|type)\b/i;
|
|
362
488
|
function extractGrepStructuralSearch(toolInput) {
|
|
@@ -375,7 +501,7 @@ function extractGrepStructuralSearch(toolInput) {
|
|
|
375
501
|
if (DOC_EXT_RE.test(rawPath) && STRUCTURAL_DOC_PATTERN_RE.test(pattern)) {
|
|
376
502
|
return { filePath: rawPath, isDoc: true, isSource: false };
|
|
377
503
|
}
|
|
378
|
-
if (
|
|
504
|
+
if (languageHasFlag(detectLanguage(rawPath), "grepSource") && STRUCTURAL_SOURCE_PATTERN_RE.test(pattern)) {
|
|
379
505
|
return { filePath: rawPath, isDoc: false, isSource: true };
|
|
380
506
|
}
|
|
381
507
|
return null;
|
|
@@ -418,8 +544,8 @@ function isBroadCatchAllGlob(pattern, pathArg) {
|
|
|
418
544
|
function globSignature(toolInput) {
|
|
419
545
|
const pattern = toolInput["pattern"];
|
|
420
546
|
if (typeof pattern !== "string" || pattern === "") return null;
|
|
421
|
-
const
|
|
422
|
-
return JSON.stringify([pattern,
|
|
547
|
+
const path4 = typeof toolInput["path"] === "string" ? toolInput["path"] : "";
|
|
548
|
+
return JSON.stringify([pattern, path4]);
|
|
423
549
|
}
|
|
424
550
|
var { post: postGlobHandler, pre: preGlobDedupHandler } = makeDedupHintHandlers({
|
|
425
551
|
toolName: "Glob",
|
|
@@ -451,8 +577,8 @@ registerHook("post_tool_use", postGlobHandler, { toolName: "Glob" });
|
|
|
451
577
|
|
|
452
578
|
// src/hooks_edit.ts
|
|
453
579
|
init_define_import_meta_env();
|
|
454
|
-
import * as
|
|
455
|
-
import { statSync } from "node:fs";
|
|
580
|
+
import * as path2 from "node:path";
|
|
581
|
+
import { statSync as statSync2 } from "node:fs";
|
|
456
582
|
function postEditHandlerInner(event) {
|
|
457
583
|
const filePath = getFilePath(event);
|
|
458
584
|
if (filePath === void 0) return passOutput();
|
|
@@ -479,11 +605,11 @@ function postEditHandlerInner(event) {
|
|
|
479
605
|
if (loadConfig().hints.stable_doc_compacts) {
|
|
480
606
|
markCompactStale(compactPathFor(normalized));
|
|
481
607
|
}
|
|
482
|
-
const editedBasename =
|
|
608
|
+
const editedBasename = path2.basename(normalized);
|
|
483
609
|
if (/\.(md|mdx|markdown|rst)$/i.test(editedBasename)) {
|
|
484
610
|
let editedSize = Infinity;
|
|
485
611
|
try {
|
|
486
|
-
editedSize =
|
|
612
|
+
editedSize = statSync2(normalized).size;
|
|
487
613
|
} catch {
|
|
488
614
|
}
|
|
489
615
|
if (meetsSavingsFloor(editedSize)) {
|
|
@@ -505,7 +631,7 @@ registerHook("post_tool_use", postEditHandler, { toolName: "NotebookEdit" });
|
|
|
505
631
|
|
|
506
632
|
// src/hooks_write.ts
|
|
507
633
|
init_define_import_meta_env();
|
|
508
|
-
import { readFileSync, statSync as
|
|
634
|
+
import { readFileSync, statSync as statSync3 } from "node:fs";
|
|
509
635
|
var MAX_LINES_FOR_DIFF = 4e3;
|
|
510
636
|
var MAX_OLD_FILE_BYTES_FOR_DIFF = 4 * 1024 * 1024;
|
|
511
637
|
function splitLines(text) {
|
|
@@ -533,11 +659,12 @@ function preWriteRewriteHandler(event) {
|
|
|
533
659
|
if (getToolName(event) !== "Write") return passOutput();
|
|
534
660
|
const filePath = getFilePath(event);
|
|
535
661
|
if (filePath === void 0) return passOutput();
|
|
662
|
+
if (preToolPathDeclined(event, filePath)) return passOutput();
|
|
536
663
|
const newContent = getToolInput(event)["content"];
|
|
537
664
|
if (typeof newContent !== "string") return passOutput();
|
|
538
665
|
let stat;
|
|
539
666
|
try {
|
|
540
|
-
stat =
|
|
667
|
+
stat = statSync3(filePath);
|
|
541
668
|
} catch {
|
|
542
669
|
return passOutput();
|
|
543
670
|
}
|
|
@@ -600,13 +727,15 @@ function mergeManifestFiles(parent, siblingFiles) {
|
|
|
600
727
|
byPath.set(key, f);
|
|
601
728
|
continue;
|
|
602
729
|
}
|
|
730
|
+
const mergedSymbols = [.../* @__PURE__ */ new Set([...prev.symbols_read ?? [], ...f.symbols_read ?? []])];
|
|
603
731
|
byPath.set(key, {
|
|
604
732
|
path: prev.path,
|
|
605
733
|
readCount: Math.max(prev.readCount, f.readCount),
|
|
606
734
|
lastReadAt: Math.max(prev.lastReadAt, f.lastReadAt),
|
|
607
735
|
wasEdited: prev.wasEdited || f.wasEdited,
|
|
608
736
|
sizeBytes: f.lastReadAt >= prev.lastReadAt ? f.sizeBytes : prev.sizeBytes,
|
|
609
|
-
...prev.wasTruncated || f.wasTruncated ? { wasTruncated: true } : {}
|
|
737
|
+
...prev.wasTruncated || f.wasTruncated ? { wasTruncated: true } : {},
|
|
738
|
+
...mergedSymbols.length > 0 ? { symbols_read: mergedSymbols } : {}
|
|
610
739
|
});
|
|
611
740
|
}
|
|
612
741
|
return Array.from(byPath.values());
|
|
@@ -693,14 +822,14 @@ function buildSafeToDiscardSection(files) {
|
|
|
693
822
|
if (id === void 0) continue;
|
|
694
823
|
const entry = getBashOutput(id);
|
|
695
824
|
if (entry === null) continue;
|
|
696
|
-
const flatCommand = entry.command.replace(/[\t\r\n]+/g, " ");
|
|
825
|
+
const flatCommand = neutralizeSpokenMarkers(entry.command.replace(/[\t\r\n]+/g, " "));
|
|
697
826
|
rerunRows.push("- `" + flatCommand + "` \u2014 an older run of this exact command was superseded; recall the surviving copy with `bash-output " + id + " --full`");
|
|
698
827
|
}
|
|
699
828
|
const supersededReadRows = [];
|
|
700
829
|
for (const f of files) {
|
|
701
830
|
if (f.readCount > 1 || f.wasEdited) {
|
|
702
831
|
const reason = f.wasEdited ? "edited after being read" : "re-read " + f.readCount + "x";
|
|
703
|
-
supersededReadRows.push("- " + f.path + " (" + reason + " \u2014 only the latest content already in context is current)");
|
|
832
|
+
supersededReadRows.push("- " + displaySafePath(f.path) + " (" + reason + " \u2014 only the latest content already in context is current)");
|
|
704
833
|
}
|
|
705
834
|
}
|
|
706
835
|
const cachedOutputRows = [];
|
|
@@ -708,7 +837,7 @@ function buildSafeToDiscardSection(files) {
|
|
|
708
837
|
if (rerunHashSet.has(hash)) continue;
|
|
709
838
|
const entry = getBashOutput(id);
|
|
710
839
|
if (entry === null) continue;
|
|
711
|
-
const flatCommand = entry.command.replace(/[\t\r\n]+/g, " ");
|
|
840
|
+
const flatCommand = neutralizeSpokenMarkers(entry.command.replace(/[\t\r\n]+/g, " "));
|
|
712
841
|
cachedOutputRows.push("- `" + flatCommand + "` \u2014 recallable via `bash-output " + id + " --full`");
|
|
713
842
|
}
|
|
714
843
|
const total = rerunRows.length + supersededReadRows.length + cachedOutputRows.length;
|
|
@@ -798,7 +927,7 @@ init_define_import_meta_env();
|
|
|
798
927
|
var TRACKED_SKILL = "token-goat";
|
|
799
928
|
var MAX_COMMANDS_SHOWN = 8;
|
|
800
929
|
async function currentCommandNames() {
|
|
801
|
-
const { buildProgram } = await import("./token-goat-chunk-
|
|
930
|
+
const { buildProgram } = await import("./token-goat-chunk-HDL77BN3.mjs");
|
|
802
931
|
return flattenCommandNames(buildCommandManifest(buildProgram()));
|
|
803
932
|
}
|
|
804
933
|
async function recordSkillVersionSnapshot(sessionId, skillName) {
|
|
@@ -852,7 +981,7 @@ async function checkSkillVersionDrift(sessionId) {
|
|
|
852
981
|
|
|
853
982
|
// src/pending_context.ts
|
|
854
983
|
init_define_import_meta_env();
|
|
855
|
-
import { readFileSync as readFileSync2, rmSync, writeFileSync
|
|
984
|
+
import { readFileSync as readFileSync2, rmSync as rmSync2, writeFileSync } from "node:fs";
|
|
856
985
|
import { dirname } from "node:path";
|
|
857
986
|
var PENDING_SUFFIX = ".pending-context.txt";
|
|
858
987
|
var MAX_PENDING_CONTEXT_BYTES = 4096;
|
|
@@ -866,7 +995,7 @@ function queuePendingContext(sessionId, text) {
|
|
|
866
995
|
const merged = existing === null ? trimmed : `${existing}
|
|
867
996
|
${trimmed}`;
|
|
868
997
|
const capped = merged.length <= MAX_PENDING_CONTEXT_BYTES ? merged : merged.slice(merged.length - MAX_PENDING_CONTEXT_BYTES);
|
|
869
|
-
|
|
998
|
+
ensureDirSync(dirname(target));
|
|
870
999
|
writeFileSync(target, capped, "utf8");
|
|
871
1000
|
} catch {
|
|
872
1001
|
}
|
|
@@ -876,7 +1005,7 @@ function drainPendingContext(sessionId) {
|
|
|
876
1005
|
if (target === null) return null;
|
|
877
1006
|
const text = readPending(target);
|
|
878
1007
|
try {
|
|
879
|
-
|
|
1008
|
+
rmSync2(target, { force: true });
|
|
880
1009
|
} catch {
|
|
881
1010
|
}
|
|
882
1011
|
return text;
|
|
@@ -1016,6 +1145,19 @@ async function userPromptSubmitHandler(event) {
|
|
|
1016
1145
|
}
|
|
1017
1146
|
var CLAIMED_CHANGE_VERBS_RE = /\b(fix(?:ed|es|ing)?|implement(?:ed|s|ing)?|add(?:ed|s|ing)?|creat(?:e|ed|es|ing)|writ(?:e|ten|es|ing)|refactor(?:ed|s|ing)?|updat(?:e|ed|es|ing)|chang(?:e|ed|es|ing)|edit(?:ed|s|ing)?|modif(?:y|ied|ies|ying)|delet(?:e|ed|es|ing)|remov(?:e|ed|es|ing)|patch(?:ed|es|ing)?|resolv(?:e|ed|es|ing)|replac(?:e|ed|es|ing)|improv(?:e|ed|es|ing)|appl(?:y|ied|ies|ying))\b/i;
|
|
1018
1147
|
var CLAIMED_COMMIT_VERBS_RE = /\b(commit(?:ted|s|ting)?|push(?:ed|es|ing)?)\b/i;
|
|
1148
|
+
var NEGATION_RE = /\b(no|not|n't|never|none|nothing|without)\b/i;
|
|
1149
|
+
function claimsUnnegatedChange(message) {
|
|
1150
|
+
const clauses = message.split(/[.;\n]+/);
|
|
1151
|
+
return clauses.some((clause) => {
|
|
1152
|
+
const match = CLAIMED_CHANGE_VERBS_RE.exec(clause);
|
|
1153
|
+
if (!match) {
|
|
1154
|
+
return false;
|
|
1155
|
+
}
|
|
1156
|
+
const wordsBefore = clause.slice(0, match.index).trim().split(/\s+/).filter(Boolean);
|
|
1157
|
+
const nearbyWords = wordsBefore.slice(-4).join(" ");
|
|
1158
|
+
return !NEGATION_RE.test(nearbyWords);
|
|
1159
|
+
});
|
|
1160
|
+
}
|
|
1019
1161
|
function subagentStopHandler(event) {
|
|
1020
1162
|
try {
|
|
1021
1163
|
if (!event.sessionId) {
|
|
@@ -1031,7 +1173,7 @@ function subagentStopHandler(event) {
|
|
|
1031
1173
|
const gitOutput = result.stdout.trim();
|
|
1032
1174
|
if (!gitOutput) {
|
|
1033
1175
|
const lastAssistantMessage = event.raw["last_assistant_message"] || "";
|
|
1034
|
-
const claimsChanges =
|
|
1176
|
+
const claimsChanges = claimsUnnegatedChange(lastAssistantMessage);
|
|
1035
1177
|
const claimsCommitted = CLAIMED_COMMIT_VERBS_RE.test(lastAssistantMessage);
|
|
1036
1178
|
if (claimsChanges && !claimsCommitted) {
|
|
1037
1179
|
console.warn(
|
|
@@ -1073,7 +1215,7 @@ function reconcileNote(cwd, indexed) {
|
|
|
1073
1215
|
try {
|
|
1074
1216
|
const budgetMs = envInt(ENV_KEYS.RECONCILE_BUDGET_MS, DEFAULT_RECONCILE_BUDGET_MS, 0, 6e4);
|
|
1075
1217
|
const result = reconcileProject({ cwd, budgetMs });
|
|
1076
|
-
if (isReconcileClean(result)) return null;
|
|
1218
|
+
if (isReconcileClean(result) && !result.budgetExhausted) return null;
|
|
1077
1219
|
const parts = [];
|
|
1078
1220
|
if (result.changed.length > 0) parts.push(`${result.changed.length} changed`);
|
|
1079
1221
|
if (result.added.length > 0) parts.push(`${result.added.length} new`);
|
|
@@ -1081,6 +1223,9 @@ function reconcileNote(cwd, indexed) {
|
|
|
1081
1223
|
const breakdown = parts.length > 1 ? ` (${parts.join(", ")})` : "";
|
|
1082
1224
|
const truncated = result.budgetExhausted ? ` (sweep stopped at its time budget with ${countNoun(result.unscanned, "file")} unchecked, so there may be more)` : "";
|
|
1083
1225
|
const total = result.changed.length + result.added.length + result.removed.length;
|
|
1226
|
+
if (total === 0) {
|
|
1227
|
+
return `token-goat: index drift check${truncated} found nothing in the part it had time to scan. Symbol lookups may be briefly stale.`;
|
|
1228
|
+
}
|
|
1084
1229
|
return `token-goat: reindexing ${countNoun(total, "file")} that changed outside this session${breakdown}${truncated}. Symbol lookups may be briefly stale.`;
|
|
1085
1230
|
} catch {
|
|
1086
1231
|
return null;
|
|
@@ -1693,11 +1838,11 @@ function mapInner(r, f) {
|
|
|
1693
1838
|
function mapOuter(r, f) {
|
|
1694
1839
|
return r.matched ? f(r) : r;
|
|
1695
1840
|
}
|
|
1696
|
-
function ab(pa, pb,
|
|
1697
|
-
return (data, i) => mapOuter(pa(data, i), (ma) => mapInner(pb(data, ma.position), (vb, j) =>
|
|
1841
|
+
function ab(pa, pb, join3) {
|
|
1842
|
+
return (data, i) => mapOuter(pa(data, i), (ma) => mapInner(pb(data, ma.position), (vb, j) => join3(ma.value, vb, data, i, j)));
|
|
1698
1843
|
}
|
|
1699
|
-
function abc(pa, pb, pc,
|
|
1700
|
-
return (data, i) => mapOuter(pa(data, i), (ma) => mapOuter(pb(data, ma.position), (mb) => mapInner(pc(data, mb.position), (vc, j) =>
|
|
1844
|
+
function abc(pa, pb, pc, join3) {
|
|
1845
|
+
return (data, i) => mapOuter(pa(data, i), (ma) => mapOuter(pb(data, ma.position), (mb) => mapInner(pc(data, mb.position), (vc, j) => join3(ma.value, mb.value, vc, data, i, j))));
|
|
1701
1846
|
}
|
|
1702
1847
|
function ahead(p) {
|
|
1703
1848
|
return (data, i) => mapOuter(p(data, i), (m1) => ({
|
|
@@ -5674,8 +5819,8 @@ function trimCharacterEnd(str, char) {
|
|
|
5674
5819
|
function unicodeEscape(str) {
|
|
5675
5820
|
return str.replace(/[\s\S]/g, (c) => "\\u" + c.charCodeAt().toString(16).padStart(4, "0"));
|
|
5676
5821
|
}
|
|
5677
|
-
function get(obj,
|
|
5678
|
-
for (const key of
|
|
5822
|
+
function get(obj, path4) {
|
|
5823
|
+
for (const key of path4) {
|
|
5679
5824
|
if (!obj) {
|
|
5680
5825
|
return void 0;
|
|
5681
5826
|
}
|
|
@@ -6876,8 +7021,8 @@ function withBrackets(str, brackets) {
|
|
|
6876
7021
|
const rbr = typeof brackets[1] === "string" ? brackets[1] : "]";
|
|
6877
7022
|
return lbr + str + rbr;
|
|
6878
7023
|
}
|
|
6879
|
-
function pathRewrite(
|
|
6880
|
-
const modifiedPath = typeof rewriter === "function" ? rewriter(
|
|
7024
|
+
function pathRewrite(path4, rewriter, baseUrl, metadata, elem) {
|
|
7025
|
+
const modifiedPath = typeof rewriter === "function" ? rewriter(path4, metadata, elem) : path4;
|
|
6881
7026
|
return modifiedPath[0] === "/" && baseUrl ? trimCharacterEnd(baseUrl, "/") + modifiedPath : modifiedPath;
|
|
6882
7027
|
}
|
|
6883
7028
|
function formatImage(elem, walk, builder, formatOptions) {
|
|
@@ -7201,9 +7346,9 @@ function handleDeprecatedOptions(options) {
|
|
|
7201
7346
|
options.selectors.push(...tagDefinitions);
|
|
7202
7347
|
options.selectors = mergeDuplicatesPreferLast(options.selectors, ((s) => s.selector));
|
|
7203
7348
|
}
|
|
7204
|
-
function set(obj,
|
|
7205
|
-
const valueKey =
|
|
7206
|
-
for (const key of
|
|
7349
|
+
function set(obj, path4, value) {
|
|
7350
|
+
const valueKey = path4.pop();
|
|
7351
|
+
for (const key of path4) {
|
|
7207
7352
|
let nested = obj[key];
|
|
7208
7353
|
if (!nested) {
|
|
7209
7354
|
nested = {};
|
|
@@ -7493,7 +7638,7 @@ registerHook("post_tool_use", postSkillHandler, { toolName: "Skill" });
|
|
|
7493
7638
|
|
|
7494
7639
|
// src/hooks_bash.ts
|
|
7495
7640
|
init_define_import_meta_env();
|
|
7496
|
-
import { statSync as
|
|
7641
|
+
import { statSync as statSync4, existsSync, openSync as openSync2, readSync, closeSync as closeSync2 } from "node:fs";
|
|
7497
7642
|
var CD_PREFIX_RE = /^(?:cd\s+(?:"[^"]*"|'[^']*'|\S+)[ \t]*(?:&&|;|\r?\n)\s*)+/;
|
|
7498
7643
|
function stripCdPrefix(cmd) {
|
|
7499
7644
|
const stripped = cmd.replace(CD_PREFIX_RE, "");
|
|
@@ -7747,7 +7892,7 @@ function workingTreeStatusPaths(gitDir) {
|
|
|
7747
7892
|
function enqueueRewrittenPath(absPath) {
|
|
7748
7893
|
if (/(?:^|[/\\])(?:NUL|nul)$/.test(absPath) || absPath.replace(/\\/g, "/").endsWith("/dev/null")) return;
|
|
7749
7894
|
try {
|
|
7750
|
-
if (!
|
|
7895
|
+
if (!statSync4(absPath).isFile()) return;
|
|
7751
7896
|
} catch {
|
|
7752
7897
|
return;
|
|
7753
7898
|
}
|
|
@@ -7772,7 +7917,11 @@ function enqueueNonHeadMovingRewrites(cmd, rawCmd, cwd) {
|
|
|
7772
7917
|
}
|
|
7773
7918
|
function isTempPath(fp) {
|
|
7774
7919
|
const norm = fp.replace(/\\/g, "/");
|
|
7775
|
-
return /^\/tmp\//i.test(norm) || /\/var\/folders\//i.test(norm) ||
|
|
7920
|
+
return /^\/tmp\//i.test(norm) || /\/var\/folders\//i.test(norm) || // Anchored to a local drive root on purpose. Unanchored, this clause said yes to
|
|
7921
|
+
// `//host/share/AppData/Local/Temp/x.md`, and the caller's answer to yes is to stat the path --
|
|
7922
|
+
// which on Windows opens an SMB session to whatever host a command named. A temp directory is
|
|
7923
|
+
// somewhere on a local volume; nothing that starts with two separators is one.
|
|
7924
|
+
/^[a-z]:\/(?:[^/]+\/)*AppData\/Local\/Temp\//i.test(norm) || norm.startsWith("/c/Users/") && norm.includes("/AppData/Local/Temp/") || isUnderSystemTemp(fp);
|
|
7776
7925
|
}
|
|
7777
7926
|
function isOrchestratorStateFile(filePath) {
|
|
7778
7927
|
const basename3 = (filePath.includes("/") ? filePath.split("/").at(-1) : filePath.split("\\").at(-1)) ?? filePath;
|
|
@@ -7832,12 +7981,16 @@ var PS_FILE_METHOD_RE = /\[(?:System\.)?IO\.File\]::(?:ReadAllText|ReadAllLines|
|
|
|
7832
7981
|
var PS_TEMP_READ_FLOOD_BYTES = 16 * 1024;
|
|
7833
7982
|
function isLargeFileOnDisk(filePath, floor) {
|
|
7834
7983
|
try {
|
|
7835
|
-
return
|
|
7984
|
+
return statSync4(filePath).size >= floor;
|
|
7836
7985
|
} catch {
|
|
7837
7986
|
return false;
|
|
7838
7987
|
}
|
|
7839
7988
|
}
|
|
7840
|
-
function
|
|
7989
|
+
function commandPathIsTouchable(filePath, event) {
|
|
7990
|
+
if (event === void 0) return !escapesOntoNetworkThroughLinks(filePath);
|
|
7991
|
+
return !preToolPathDeclined(event, filePath);
|
|
7992
|
+
}
|
|
7993
|
+
function extractPowerShellWrappedGetContent(cmd, event) {
|
|
7841
7994
|
const w = POWERSHELL_WRAP_RE.exec(cmd);
|
|
7842
7995
|
if (!w) return null;
|
|
7843
7996
|
const inner = (w[1] ?? w[2] ?? "").trim();
|
|
@@ -7848,10 +8001,13 @@ function extractPowerShellWrappedGetContent(cmd) {
|
|
|
7848
8001
|
if (filePath === void 0) return null;
|
|
7849
8002
|
const flags = classifyFileExtensions(filePath);
|
|
7850
8003
|
if (flags === null) return null;
|
|
7851
|
-
if (isTempPath(filePath)
|
|
8004
|
+
if (isTempPath(filePath)) {
|
|
8005
|
+
if (!commandPathIsTouchable(filePath, event)) return null;
|
|
8006
|
+
if (!isLargeFileOnDisk(filePath, PS_TEMP_READ_FLOOD_BYTES)) return null;
|
|
8007
|
+
}
|
|
7852
8008
|
return { filePath, ...flags };
|
|
7853
8009
|
}
|
|
7854
|
-
function extractPowerShellFileMethodRead(cmd) {
|
|
8010
|
+
function extractPowerShellFileMethodRead(cmd, event) {
|
|
7855
8011
|
let inner = cmd.trim();
|
|
7856
8012
|
const w = POWERSHELL_WRAP_RE.exec(inner);
|
|
7857
8013
|
if (w) {
|
|
@@ -7861,13 +8017,16 @@ function extractPowerShellFileMethodRead(cmd) {
|
|
|
7861
8017
|
if (!m?.[1]) return null;
|
|
7862
8018
|
const filePath = m[1];
|
|
7863
8019
|
if (isOrchestratorStateFile(filePath)) return null;
|
|
7864
|
-
if (isTempPath(filePath)
|
|
8020
|
+
if (isTempPath(filePath)) {
|
|
8021
|
+
if (!commandPathIsTouchable(filePath, event)) return null;
|
|
8022
|
+
if (!isLargeFileOnDisk(filePath, PS_TEMP_READ_FLOOD_BYTES)) return null;
|
|
8023
|
+
}
|
|
7865
8024
|
const flags = classifyFileExtensions(filePath);
|
|
7866
8025
|
if (flags === null) {
|
|
7867
8026
|
const { isDoc, isConfig, isSql } = classifyDocConfig(filePath);
|
|
7868
|
-
return { filePath, isDoc, isConfig, isSql };
|
|
8027
|
+
return { filePath, isDoc, isEnv: false, isConfig, isSql };
|
|
7869
8028
|
}
|
|
7870
|
-
return { filePath, isDoc: flags.isDoc, isConfig: flags.isConfig, isSql: flags.isSql };
|
|
8029
|
+
return { filePath, isDoc: flags.isDoc, isEnv: flags.isEnv, isConfig: flags.isConfig, isSql: flags.isSql };
|
|
7871
8030
|
}
|
|
7872
8031
|
function extractRgSymbolSearch(cmd) {
|
|
7873
8032
|
if (!/^(?:rg|grep)\s+/.test(cmd)) return null;
|
|
@@ -8190,36 +8349,6 @@ function extractLineRangeReadsCompound(cmd) {
|
|
|
8190
8349
|
}
|
|
8191
8350
|
return [...merged.values()];
|
|
8192
8351
|
}
|
|
8193
|
-
var SYMBOL_BEARING_LANGUAGES = /* @__PURE__ */ new Set([
|
|
8194
|
-
"python",
|
|
8195
|
-
"typescript",
|
|
8196
|
-
"javascript",
|
|
8197
|
-
"rust",
|
|
8198
|
-
"go",
|
|
8199
|
-
"c",
|
|
8200
|
-
"cpp",
|
|
8201
|
-
"ruby",
|
|
8202
|
-
"java",
|
|
8203
|
-
"csharp",
|
|
8204
|
-
"php",
|
|
8205
|
-
"kotlin",
|
|
8206
|
-
"swift",
|
|
8207
|
-
"scala",
|
|
8208
|
-
"lua",
|
|
8209
|
-
"elixir",
|
|
8210
|
-
"dart",
|
|
8211
|
-
"zig",
|
|
8212
|
-
"r",
|
|
8213
|
-
"sql",
|
|
8214
|
-
"graphql",
|
|
8215
|
-
"proto",
|
|
8216
|
-
"terraform",
|
|
8217
|
-
"bash",
|
|
8218
|
-
"powershell",
|
|
8219
|
-
"apex",
|
|
8220
|
-
"salesforce_metadata",
|
|
8221
|
-
"salesforce_markup"
|
|
8222
|
-
]);
|
|
8223
8352
|
function sedRangeHint(filePath, ranges, tool) {
|
|
8224
8353
|
const lang = detectLanguage(filePath);
|
|
8225
8354
|
const rangeReads = ranges.map(([s, e]) => '`token-goat read "' + filePath + "@" + s + "-" + e + '"`');
|
|
@@ -8231,7 +8360,7 @@ function sedRangeHint(filePath, ranges, tool) {
|
|
|
8231
8360
|
if (lang === "toml" || lang === "json" || lang === "yaml" || lang === "ini") {
|
|
8232
8361
|
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.";
|
|
8233
8362
|
}
|
|
8234
|
-
if (
|
|
8363
|
+
if (languageHasFlag(lang, "symbolBearing")) {
|
|
8235
8364
|
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.";
|
|
8236
8365
|
}
|
|
8237
8366
|
return prefix + "Use " + allReads + " to read exactly those lines.";
|
|
@@ -8308,7 +8437,7 @@ function extractGetContentTail(cmd) {
|
|
|
8308
8437
|
if (n <= 10) return null;
|
|
8309
8438
|
const getnMatch = /^(Get-Content|gc)\s+/i.exec(cmd);
|
|
8310
8439
|
if (!getnMatch) return null;
|
|
8311
|
-
const afterCmd = cmd.slice(getnMatch[0].length);
|
|
8440
|
+
const afterCmd = cmd.slice(getnMatch[0].length).replace(/-Path\s+/i, "");
|
|
8312
8441
|
const beforeTail = afterCmd.split(/-Tail/i)[0]?.trim() ?? "";
|
|
8313
8442
|
const afterTail = afterCmd.split(/-Tail\s+\d+/i)[1]?.trim() ?? "";
|
|
8314
8443
|
const filePath = (beforeTail || afterTail).replace(/^["']|["']$/g, "");
|
|
@@ -8321,7 +8450,7 @@ function extractGetContentTail(cmd) {
|
|
|
8321
8450
|
function extractGetContentSelectFirst(cmd) {
|
|
8322
8451
|
const m = /^(Get-Content|gc)\s+([^|]+)\s*\|\s*(Select-Object|select)\s+(-First\s+(\d+))/i.exec(cmd);
|
|
8323
8452
|
if (!m) return null;
|
|
8324
|
-
const filePath = (m[2]?.trim() ?? "").replace(/^["']|["']$/g, "");
|
|
8453
|
+
const filePath = (m[2]?.trim() ?? "").replace(/^-Path\s+/i, "").replace(/^["']|["']$/g, "");
|
|
8325
8454
|
const n = parseInt(m[5] ?? "0", 10);
|
|
8326
8455
|
if (n <= 10) return null;
|
|
8327
8456
|
if (!filePath) return null;
|
|
@@ -8333,7 +8462,7 @@ function extractGetContentSelectFirst(cmd) {
|
|
|
8333
8462
|
function taskOutputIsJsonlTranscript(outPath) {
|
|
8334
8463
|
let fd = null;
|
|
8335
8464
|
try {
|
|
8336
|
-
fd =
|
|
8465
|
+
fd = openSync2(normalizePath(outPath), "r");
|
|
8337
8466
|
const buf = Buffer.alloc(64);
|
|
8338
8467
|
const read = readSync(fd, buf, 0, buf.length, 0);
|
|
8339
8468
|
return buf.subarray(0, read).toString("utf-8").trim().startsWith("{");
|
|
@@ -8342,7 +8471,7 @@ function taskOutputIsJsonlTranscript(outPath) {
|
|
|
8342
8471
|
} finally {
|
|
8343
8472
|
if (fd !== null) {
|
|
8344
8473
|
try {
|
|
8345
|
-
|
|
8474
|
+
closeSync2(fd);
|
|
8346
8475
|
} catch {
|
|
8347
8476
|
}
|
|
8348
8477
|
}
|
|
@@ -8698,14 +8827,14 @@ function shellQuoteSingle(s) {
|
|
|
8698
8827
|
}
|
|
8699
8828
|
function isCompressibleSingleCommand(cmd) {
|
|
8700
8829
|
if (!cmd || cmd.length > 65536) return false;
|
|
8701
|
-
if (["
|
|
8702
|
-
if (cmd
|
|
8703
|
-
if (/[<>]/.test(cmd)) return false;
|
|
8830
|
+
if (["$(", "`"].some((op) => cmd.includes(op))) return false;
|
|
8831
|
+
if (hasUnquotedOperator(cmd, ["&&", "||", "|", ";", "<", ">"])) return false;
|
|
8704
8832
|
if (hasBareBackgroundOrNewline(cmd)) return false;
|
|
8705
8833
|
return true;
|
|
8706
8834
|
}
|
|
8707
8835
|
function maybeCompressRewrite(event, rawCmd, cmd) {
|
|
8708
8836
|
if (process.env["TOKEN_GOAT_BASH_COMPRESS"] === "0") return null;
|
|
8837
|
+
if (event.raw["_tg_harness"] === "vscode") return null;
|
|
8709
8838
|
let cfg;
|
|
8710
8839
|
try {
|
|
8711
8840
|
cfg = loadConfig().bash_compress;
|
|
@@ -8833,18 +8962,24 @@ function elideServedShellLines(cmd, output, priorIds, unknownLineNumbers = false
|
|
|
8833
8962
|
}
|
|
8834
8963
|
const cuts = planServedElisions(rows, bodies);
|
|
8835
8964
|
if (cuts.length === 0) return null;
|
|
8836
|
-
const
|
|
8965
|
+
const spans = [];
|
|
8966
|
+
const push = (text, own) => {
|
|
8967
|
+
const sep = spans.length === 0 ? "" : "\n";
|
|
8968
|
+
const prev = spans[spans.length - 1];
|
|
8969
|
+
if (prev !== void 0 && prev.own === true === own) spans[spans.length - 1] = { text: prev.text + sep + text, own };
|
|
8970
|
+
else spans.push({ text: sep + text, own });
|
|
8971
|
+
};
|
|
8837
8972
|
let at = 0;
|
|
8838
8973
|
for (const cut of cuts) {
|
|
8839
|
-
for (let i = at; i < cut.start; i++)
|
|
8974
|
+
for (let i = at; i < cut.start; i++) push(rows[i]?.raw ?? "", false);
|
|
8840
8975
|
const first2 = rows[cut.start];
|
|
8841
8976
|
const last = rows[cut.start + cut.len - 1];
|
|
8842
8977
|
if (first2 === void 0 || last === void 0) return null;
|
|
8843
|
-
|
|
8978
|
+
push(servedRunNotice(first2.no, last.no, cut.id, cut.len), true);
|
|
8844
8979
|
at = cut.start + cut.len;
|
|
8845
8980
|
}
|
|
8846
|
-
for (let i = at; i < rows.length; i++)
|
|
8847
|
-
return
|
|
8981
|
+
for (let i = at; i < rows.length; i++) push(rows[i]?.raw ?? "", false);
|
|
8982
|
+
return spans;
|
|
8848
8983
|
}
|
|
8849
8984
|
function foldShellReadBodies(cmd, output, fileKey, cwd) {
|
|
8850
8985
|
if (!foldingEnabled()) return null;
|
|
@@ -8876,7 +9011,7 @@ function foldShellReadStructure(cmd, filePath, output, fileKey, cwd) {
|
|
|
8876
9011
|
const originalBytes = Buffer.byteLength(output, "utf-8");
|
|
8877
9012
|
let onDisk;
|
|
8878
9013
|
try {
|
|
8879
|
-
onDisk =
|
|
9014
|
+
onDisk = statSync4(fileKey).size;
|
|
8880
9015
|
} catch {
|
|
8881
9016
|
return null;
|
|
8882
9017
|
}
|
|
@@ -8892,6 +9027,23 @@ function foldShellReadStructure(cmd, filePath, output, fileKey, cwd) {
|
|
|
8892
9027
|
if (!isStructuralRewriteAccepted(originalBytes, Buffer.byteLength(text, "utf-8"), fold.ratioCap)) return null;
|
|
8893
9028
|
return { text, kind: fold.kind === "read:markdown_outline" ? "bash_compress:markdown-outline" : "bash_compress:source-skeleton", detail: fold.detail };
|
|
8894
9029
|
}
|
|
9030
|
+
var REWRITE_CLIP_NOTE = "\n[token-goat: rewrite clipped to the harness delivery cap; the pointer above recalls the untouched output]";
|
|
9031
|
+
function fenceRewriteWithinCap(spans) {
|
|
9032
|
+
const joined = spans.map((s) => s.text).join("");
|
|
9033
|
+
const fenced = fenceUntrustedSpans(spans, UNTRUSTED_TOOL_TAG);
|
|
9034
|
+
if (fenced === joined) return joined;
|
|
9035
|
+
const overhead = Buffer.byteLength(fenced, "utf-8") - Buffer.byteLength(joined, "utf-8") + Buffer.byteLength(REWRITE_CLIP_NOTE, "utf-8");
|
|
9036
|
+
const cut = clipToDeliveryCap(joined, overhead);
|
|
9037
|
+
if (!cut.clipped) return fenced;
|
|
9038
|
+
const kept = [];
|
|
9039
|
+
let left2 = cut.text.length;
|
|
9040
|
+
for (const s of spans) {
|
|
9041
|
+
if (left2 <= 0) break;
|
|
9042
|
+
kept.push(s.text.length <= left2 ? s : { ...s, text: s.text.slice(0, left2) });
|
|
9043
|
+
left2 -= s.text.length;
|
|
9044
|
+
}
|
|
9045
|
+
return fenceUntrustedSpans(kept, UNTRUSTED_TOOL_TAG) + REWRITE_CLIP_NOTE;
|
|
9046
|
+
}
|
|
8895
9047
|
async function maybeCollapseIdenticalRead(cmd, rawCmd, output, exitCode, cwd, cacheMinBytes) {
|
|
8896
9048
|
if (process.env["TOKEN_GOAT_BASH_COMPRESS"] === "0") return null;
|
|
8897
9049
|
if (exitCode !== null && exitCode !== 0) return null;
|
|
@@ -8918,7 +9070,8 @@ async function maybeCollapseIdenticalRead(cmd, rawCmd, output, exitCode, cwd, ca
|
|
|
8918
9070
|
const priced = (text) => isRewriteWorthwhile({ originalBytes, rewrittenBytes: Buffer.byteLength(text, "utf-8"), noticeBytes: 0, minNetSavingsBytes: resolveMinNetSavingsBytes() });
|
|
8919
9071
|
let rewrite = null;
|
|
8920
9072
|
if (elided !== null) {
|
|
8921
|
-
|
|
9073
|
+
const fenced = fenceRewriteWithinCap(elided);
|
|
9074
|
+
if (priced(fenced)) rewrite = { text: fenced, reason: "already-served file lines withheld", kind: "bash_compress:served-elide" };
|
|
8922
9075
|
} else {
|
|
8923
9076
|
const structural = foldShellReadStructure(cmd, filePath, output, fileKey, cwd);
|
|
8924
9077
|
if (structural !== null && priced(structural.text)) {
|
|
@@ -8926,7 +9079,10 @@ async function maybeCollapseIdenticalRead(cmd, rawCmd, output, exitCode, cwd, ca
|
|
|
8926
9079
|
}
|
|
8927
9080
|
if (rewrite === null) {
|
|
8928
9081
|
const folded = foldShellReadBodies(cmd, output, fileKey, cwd);
|
|
8929
|
-
if (folded !== null
|
|
9082
|
+
if (folded !== null) {
|
|
9083
|
+
const fenced = fenceRewriteWithinCap([{ text: folded.text }]);
|
|
9084
|
+
if (priced(fenced)) rewrite = { text: fenced, reason: "code bodies folded", kind: "bash_compress:body-fold", detail: foldDetail(fileKey, folded.folds) };
|
|
9085
|
+
}
|
|
8930
9086
|
}
|
|
8931
9087
|
}
|
|
8932
9088
|
const storedId = await storeBashOutput(cmd, rewrite?.text ?? output, exitCode ?? 0, cwd);
|
|
@@ -8950,8 +9106,9 @@ async function maybeElideServedGenericOutput(cmd, output, exitCode, cwd, cacheMi
|
|
|
8950
9106
|
let rewrittenText = null;
|
|
8951
9107
|
if (priorIds.length > 0) {
|
|
8952
9108
|
const elided = elideServedShellLines(cmd, output, priorIds, true);
|
|
8953
|
-
|
|
8954
|
-
|
|
9109
|
+
const fenced = elided === null ? null : fenceRewriteWithinCap(elided);
|
|
9110
|
+
if (fenced !== null && isRewriteWorthwhile({ originalBytes, rewrittenBytes: Buffer.byteLength(fenced, "utf-8"), noticeBytes: 0, minNetSavingsBytes: resolveMinNetSavingsBytes() })) {
|
|
9111
|
+
rewrittenText = fenced;
|
|
8955
9112
|
}
|
|
8956
9113
|
}
|
|
8957
9114
|
const storedId = await storeBashOutput(cmd, rewrittenText ?? output, exitCode ?? 0, cwd);
|
|
@@ -9037,17 +9194,10 @@ function maybeFoldCurlHtml(cmd, output, id) {
|
|
|
9037
9194
|
`;
|
|
9038
9195
|
const noticeBytes = Buffer.byteLength(notice, "utf-8");
|
|
9039
9196
|
const clipNote = "\n[token-goat: cleaned text clipped to the harness delivery cap; the notice at the top of this output recalls the full raw markup]";
|
|
9040
|
-
const cap = bashOutputCapBytes();
|
|
9041
9197
|
const fenceOverhead = Buffer.byteLength(fenceUntrusted(cleaned, UNTRUSTED_TOOL_TAG), "utf-8") - Buffer.byteLength(cleaned, "utf-8");
|
|
9042
|
-
const
|
|
9043
|
-
|
|
9044
|
-
|
|
9045
|
-
if (room !== null && room > 0 && Buffer.byteLength(cleaned, "utf-8") > room) {
|
|
9046
|
-
const sliced = Buffer.from(cleaned, "utf-8").subarray(0, room).toString("utf-8");
|
|
9047
|
-
const lastNewline = sliced.lastIndexOf("\n");
|
|
9048
|
-
body = lastNewline > 0 ? sliced.slice(0, lastNewline) : sliced;
|
|
9049
|
-
clipped = clipNote;
|
|
9050
|
-
}
|
|
9198
|
+
const cut = clipToDeliveryCap(cleaned, noticeBytes + fenceOverhead + Buffer.byteLength(clipNote, "utf-8"));
|
|
9199
|
+
const body = cut.text;
|
|
9200
|
+
const clipped = cut.clipped ? clipNote : "";
|
|
9051
9201
|
const fenced = fenceUntrusted(body, UNTRUSTED_TOOL_TAG) + clipped;
|
|
9052
9202
|
if (!isRewriteWorthwhile({
|
|
9053
9203
|
originalBytes,
|
|
@@ -9307,6 +9457,20 @@ function preBashHandlerInner(event) {
|
|
|
9307
9457
|
}
|
|
9308
9458
|
return contextOutput(hints.join(" "));
|
|
9309
9459
|
}
|
|
9460
|
+
const gcTailResult = extractGetContentTail(cmd);
|
|
9461
|
+
if (gcTailResult !== null) {
|
|
9462
|
+
const { filePath, isDoc, isConfig, isSql } = gcTailResult;
|
|
9463
|
+
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
|
|
9464
|
+
recordStat("session_hint", 0, 0);
|
|
9465
|
+
return contextOutput("`Get-Content -Tail` bypasses read hooks. " + surgicalHintForConfigDoc(hintPath, isConfig, isDoc, isSql));
|
|
9466
|
+
}
|
|
9467
|
+
const gcSelectResult = extractGetContentSelectFirst(cmd);
|
|
9468
|
+
if (gcSelectResult !== null) {
|
|
9469
|
+
const { filePath, isDoc, isConfig, isSql, n } = gcSelectResult;
|
|
9470
|
+
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
|
|
9471
|
+
recordStat("session_hint", 0, 0);
|
|
9472
|
+
return contextOutput(leadingLinesHint("`Select-Object -First` bypasses read hooks. ", hintPath, 1, n, { isConfig, isDoc, isSql }, preHookCwd));
|
|
9473
|
+
}
|
|
9310
9474
|
const catJsonPipe = extractCatJsonPipe(cmd);
|
|
9311
9475
|
if (catJsonPipe !== null) {
|
|
9312
9476
|
const { filePath } = catJsonPipe;
|
|
@@ -9341,7 +9505,7 @@ function preBashHandlerInner(event) {
|
|
|
9341
9505
|
const msg = "`" + cmd0 + "` on multiple files loads them all into context. Read each surgically instead:\n" + perPath.join("\n");
|
|
9342
9506
|
return cdStripped ? contextOutput(msg) : denyOutput(msg);
|
|
9343
9507
|
}
|
|
9344
|
-
const psGetContentResult = extractPowerShellWrappedGetContent(cmd);
|
|
9508
|
+
const psGetContentResult = extractPowerShellWrappedGetContent(cmd, event);
|
|
9345
9509
|
if (psGetContentResult !== null) {
|
|
9346
9510
|
const { filePath, isDoc, isEnv, isConfig, isSql } = psGetContentResult;
|
|
9347
9511
|
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
|
|
@@ -9402,20 +9566,6 @@ function preBashHandlerInner(event) {
|
|
|
9402
9566
|
recordStat("session_hint", 0, 0);
|
|
9403
9567
|
return contextOutput(leadingLinesHint("`head` bypasses read hooks. ", hintPath, 1, n, { isConfig, isDoc, isSql }, preHookCwd));
|
|
9404
9568
|
}
|
|
9405
|
-
const gcTailResult = extractGetContentTail(cmd);
|
|
9406
|
-
if (gcTailResult !== null) {
|
|
9407
|
-
const { filePath, isDoc, isConfig, isSql } = gcTailResult;
|
|
9408
|
-
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
|
|
9409
|
-
recordStat("session_hint", 0, 0);
|
|
9410
|
-
return contextOutput("`Get-Content -Tail` bypasses read hooks. " + surgicalHintForConfigDoc(hintPath, isConfig, isDoc, isSql));
|
|
9411
|
-
}
|
|
9412
|
-
const gcSelectResult = extractGetContentSelectFirst(cmd);
|
|
9413
|
-
if (gcSelectResult !== null) {
|
|
9414
|
-
const { filePath, isDoc, isConfig, isSql, n } = gcSelectResult;
|
|
9415
|
-
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
|
|
9416
|
-
recordStat("session_hint", 0, 0);
|
|
9417
|
-
return contextOutput(leadingLinesHint("`Select-Object -First` bypasses read hooks. ", hintPath, 1, n, { isConfig, isDoc, isSql }, preHookCwd));
|
|
9418
|
-
}
|
|
9419
9569
|
const nodeRead = extractNodeFileRead(cmd);
|
|
9420
9570
|
if (nodeRead !== null) {
|
|
9421
9571
|
const { filePath, isDoc, isConfig, isSql } = nodeRead;
|
|
@@ -9429,16 +9579,16 @@ function preBashHandlerInner(event) {
|
|
|
9429
9579
|
recordStat("session_hint", 0, 0);
|
|
9430
9580
|
return cdStripped ? contextOutput(lead + hint) : denyOutput(lead + hint);
|
|
9431
9581
|
}
|
|
9432
|
-
const psMethodRead = extractPowerShellFileMethodRead(cmd);
|
|
9582
|
+
const psMethodRead = extractPowerShellFileMethodRead(cmd, event);
|
|
9433
9583
|
if (psMethodRead !== null) {
|
|
9434
|
-
const { filePath, isDoc, isConfig, isSql } = psMethodRead;
|
|
9584
|
+
const { filePath, isDoc, isEnv, isConfig, isSql } = psMethodRead;
|
|
9435
9585
|
const hintPath = displaySafePath(cdStripped ? resolveCdHintPath(rawCmd, filePath, hintCwd) : filePath);
|
|
9436
9586
|
const lead = "PowerShell `[IO.File]::ReadAllText()` bypasses read hooks. ";
|
|
9437
9587
|
if (isSql) {
|
|
9438
9588
|
recordStat("session_hint", 0, 0);
|
|
9439
9589
|
return contextOutput(lead + 'Use `token-goat section "' + hintPath + '::table_name"` to pull one CREATE TABLE / CREATE TYPE block.');
|
|
9440
9590
|
}
|
|
9441
|
-
const hint =
|
|
9591
|
+
const hint = surgicalHintFor(hintPath, isEnv, isConfig, isDoc);
|
|
9442
9592
|
recordStat("session_hint", 0, 0);
|
|
9443
9593
|
return cdStripped ? contextOutput(lead + hint) : denyOutput(lead + hint);
|
|
9444
9594
|
}
|
|
@@ -9499,10 +9649,10 @@ function preBashHandlerInner(event) {
|
|
|
9499
9649
|
const curlDl = extractCurlDownload(cmd);
|
|
9500
9650
|
if (curlDl !== null) {
|
|
9501
9651
|
const prevPath = getCurlDownloadPath(curlDl.url);
|
|
9502
|
-
const prevResolvedPath = prevPath !== null ? resolveIndexPath(prevPath, preHookCwd ?? process.cwd()) : null;
|
|
9652
|
+
const prevResolvedPath = prevPath !== null && commandPathIsTouchable(prevPath, event) ? resolveIndexPath(prevPath, preHookCwd ?? process.cwd()) : null;
|
|
9503
9653
|
if (prevPath !== null && prevResolvedPath !== null && !existsSync(prevResolvedPath)) {
|
|
9504
9654
|
clearCurlDownload(curlDl.url);
|
|
9505
|
-
} else if (prevPath !== null && prevResolvedPath !== null &&
|
|
9655
|
+
} else if (prevPath !== null && prevResolvedPath !== null && statSync4(prevResolvedPath).size >= loadConfig().hints.bash_dedup_min_bytes) {
|
|
9506
9656
|
recordStat("session_hint", 0, 0);
|
|
9507
9657
|
return denyOutput(
|
|
9508
9658
|
"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.'
|
|
@@ -9636,55 +9786,55 @@ function buildGhApiHint(cmd, stdout, exitCode) {
|
|
|
9636
9786
|
return hints.length > 0 ? hints.join(" ") : null;
|
|
9637
9787
|
}
|
|
9638
9788
|
function recordBashFileReadsForSessionCache(cmd, cwd) {
|
|
9639
|
-
const
|
|
9789
|
+
const resolve2 = (p) => resolveIndexPath(p, cwd ?? process.cwd());
|
|
9640
9790
|
const gcTail = extractGetContentTail(cmd);
|
|
9641
9791
|
if (gcTail !== null) {
|
|
9642
|
-
markFileTruncated(
|
|
9792
|
+
markFileTruncated(resolve2(gcTail.filePath));
|
|
9643
9793
|
return;
|
|
9644
9794
|
}
|
|
9645
9795
|
const tail = extractTailFile(cmd);
|
|
9646
9796
|
if (tail !== null) {
|
|
9647
|
-
markFileTruncated(
|
|
9797
|
+
markFileTruncated(resolve2(tail.filePath));
|
|
9648
9798
|
return;
|
|
9649
9799
|
}
|
|
9650
9800
|
const gcSelect = extractGetContentSelectFirst(cmd);
|
|
9651
9801
|
if (gcSelect !== null) {
|
|
9652
|
-
recordFileLineRange(
|
|
9802
|
+
recordFileLineRange(resolve2(gcSelect.filePath), 1, gcSelect.n);
|
|
9653
9803
|
return;
|
|
9654
9804
|
}
|
|
9655
9805
|
const head = extractHeadFile(cmd);
|
|
9656
9806
|
if (head !== null) {
|
|
9657
|
-
recordFileLineRange(
|
|
9807
|
+
recordFileLineRange(resolve2(head.filePath), 1, head.n);
|
|
9658
9808
|
return;
|
|
9659
9809
|
}
|
|
9660
9810
|
const cat = extractCatFile(cmd);
|
|
9661
9811
|
if (cat !== null) {
|
|
9662
|
-
recordFileRead(
|
|
9812
|
+
recordFileRead(resolve2(cat.filePath));
|
|
9663
9813
|
return;
|
|
9664
9814
|
}
|
|
9665
9815
|
const catMulti = extractCatFilesMulti(cmd);
|
|
9666
9816
|
if (catMulti !== null) {
|
|
9667
|
-
for (const r of catMulti) recordFileRead(
|
|
9817
|
+
for (const r of catMulti) recordFileRead(resolve2(r.filePath));
|
|
9668
9818
|
return;
|
|
9669
9819
|
}
|
|
9670
9820
|
const wslCat = extractWslCatFile(cmd);
|
|
9671
9821
|
if (wslCat !== null) {
|
|
9672
|
-
recordFileRead(
|
|
9822
|
+
recordFileRead(resolve2(wslCat.filePath));
|
|
9673
9823
|
return;
|
|
9674
9824
|
}
|
|
9675
9825
|
const nodeRead = extractNodeFileRead(cmd);
|
|
9676
9826
|
if (nodeRead !== null) {
|
|
9677
|
-
recordFileRead(
|
|
9827
|
+
recordFileRead(resolve2(nodeRead.filePath));
|
|
9678
9828
|
return;
|
|
9679
9829
|
}
|
|
9680
9830
|
const psMethod = extractPowerShellFileMethodRead(cmd);
|
|
9681
9831
|
if (psMethod !== null) {
|
|
9682
|
-
recordFileRead(
|
|
9832
|
+
recordFileRead(resolve2(psMethod.filePath));
|
|
9683
9833
|
return;
|
|
9684
9834
|
}
|
|
9685
9835
|
const pyRead = extractPythonFileRead(cmd);
|
|
9686
9836
|
if (pyRead !== null && !pyRead.isOutputFile) {
|
|
9687
|
-
recordFileRead(
|
|
9837
|
+
recordFileRead(resolve2(pyRead.filePath));
|
|
9688
9838
|
return;
|
|
9689
9839
|
}
|
|
9690
9840
|
}
|
|
@@ -9994,7 +10144,7 @@ registerHook("post_tool_use", postTaskOutputHandler, { toolName: "TaskOutput" })
|
|
|
9994
10144
|
|
|
9995
10145
|
// src/hooks_tool_failure.ts
|
|
9996
10146
|
init_define_import_meta_env();
|
|
9997
|
-
import { existsSync as existsSync2,
|
|
10147
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3, statSync as statSync5, writeFileSync as writeFileSync2 } from "node:fs";
|
|
9998
10148
|
import { dirname as dirname2, relative } from "node:path";
|
|
9999
10149
|
var FAILURE_SUFFIX = ".tool-failures.json";
|
|
10000
10150
|
var MAX_TRACKED_FAILURES = 64;
|
|
@@ -10036,7 +10186,7 @@ function readLedger(target) {
|
|
|
10036
10186
|
}
|
|
10037
10187
|
function writeLedger(target, ledger) {
|
|
10038
10188
|
try {
|
|
10039
|
-
|
|
10189
|
+
ensureDirSync(dirname2(target));
|
|
10040
10190
|
writeFileSync2(target, JSON.stringify(ledger), "utf8");
|
|
10041
10191
|
} catch {
|
|
10042
10192
|
}
|
|
@@ -10063,7 +10213,7 @@ function diagnoseEditFailure(event, errorText) {
|
|
|
10063
10213
|
const absPath = normalizePath(filePath);
|
|
10064
10214
|
if (!existsSync2(absPath)) return null;
|
|
10065
10215
|
try {
|
|
10066
|
-
const stat =
|
|
10216
|
+
const stat = statSync5(absPath);
|
|
10067
10217
|
if (stat.size > MAX_EDIT_DIAGNOSE_BYTES) return null;
|
|
10068
10218
|
const fileContent = readFileSync3(absPath, "utf8");
|
|
10069
10219
|
const relDisplay = displaySafeText(relative(process.cwd(), absPath).replace(/\\/g, "/") || absPath);
|
|
@@ -10118,7 +10268,7 @@ function postToolUseFailureHandler(event) {
|
|
|
10118
10268
|
const target = ledgerPath(event.sessionId);
|
|
10119
10269
|
if (target === null) return passOutput();
|
|
10120
10270
|
const toolName = getToolName(event);
|
|
10121
|
-
const signature = failureSignature(toolName, errorText);
|
|
10271
|
+
const signature = failureSignature(toolName, redactSecrets(errorText).text);
|
|
10122
10272
|
const ledger = readLedger(target);
|
|
10123
10273
|
const priorState = ledger.seen[signature];
|
|
10124
10274
|
const editDiagnostic = diagnoseEditFailure(event, errorText);
|
|
@@ -10222,7 +10372,7 @@ function mcpOutputId(sessionId, hash) {
|
|
|
10222
10372
|
}
|
|
10223
10373
|
function mcpInputPreview(toolInput) {
|
|
10224
10374
|
try {
|
|
10225
|
-
return JSON.stringify(toolInput).slice(0, 120);
|
|
10375
|
+
return redactSecrets(JSON.stringify(toolInput)).text.slice(0, 120);
|
|
10226
10376
|
} catch {
|
|
10227
10377
|
return "";
|
|
10228
10378
|
}
|
|
@@ -10611,7 +10761,7 @@ function postMcpHandler(event) {
|
|
|
10611
10761
|
id = storeMcpOutput(event.sessionId, toolName, toolInput, resultText);
|
|
10612
10762
|
}
|
|
10613
10763
|
if (resultText.length >= MCP_COMPRESS_MIN_BYTES && process.env["TOKEN_GOAT_MCP_COMPRESS"] !== "0") {
|
|
10614
|
-
const compressed = compressMcpResultWithPacks(toolName,
|
|
10764
|
+
const compressed = compressMcpResultWithPacks(toolName, redactedResult.text) ?? compressMcpResult(redactedResult.text);
|
|
10615
10765
|
if (compressed !== null) {
|
|
10616
10766
|
if (id === null) id = storeMcpOutput(event.sessionId, toolName, toolInput, resultText);
|
|
10617
10767
|
if (id !== null) {
|
|
@@ -10840,9 +10990,9 @@ registerHook("post_tool_use", postBrowserImageHandler, { toolPattern: "^mcp__" }
|
|
|
10840
10990
|
// src/hooks_agent_spawn.ts
|
|
10841
10991
|
init_define_import_meta_env();
|
|
10842
10992
|
import { createHash as createHash2 } from "node:crypto";
|
|
10843
|
-
import * as
|
|
10844
|
-
import * as
|
|
10845
|
-
import * as
|
|
10993
|
+
import * as fs2 from "node:fs";
|
|
10994
|
+
import * as os2 from "node:os";
|
|
10995
|
+
import * as path3 from "node:path";
|
|
10846
10996
|
var BRIEFING_TARGET_TOKENS = 550;
|
|
10847
10997
|
function buildSubagentBriefing() {
|
|
10848
10998
|
try {
|
|
@@ -10928,7 +11078,7 @@ function preAgentHandler(event) {
|
|
|
10928
11078
|
const briefing = buildSubagentBriefing();
|
|
10929
11079
|
const advisory = duplicateOf ? `
|
|
10930
11080
|
|
|
10931
|
-
|
|
11081
|
+
[token-goat] A similar subagent spawn already appears to be outstanding this session (prompt starts: "${neutralizeSpokenMarkers(truncateForWarning(duplicateOf, 80))}"). This is advisory only -- proceeding is fine if intentional.` : "";
|
|
10932
11082
|
if (!briefing && !advisory) return passOutput();
|
|
10933
11083
|
const updatedPrompt = prompt + briefing + advisory;
|
|
10934
11084
|
const updatedInput = { ...toolInput, prompt: updatedPrompt };
|
|
@@ -11067,53 +11217,75 @@ function parseAgentDefinition(text, fallbackName) {
|
|
|
11067
11217
|
return { name: name2, restricted: false };
|
|
11068
11218
|
}
|
|
11069
11219
|
function findRestrictedAgentNames(roots) {
|
|
11070
|
-
const
|
|
11220
|
+
const projectAgentsRoot = path3.join(process.cwd(), ".claude", "agents");
|
|
11221
|
+
const scanRoots = roots ?? [path3.join(os2.homedir(), ".claude", "agents"), projectAgentsRoot];
|
|
11071
11222
|
const names = /* @__PURE__ */ new Set();
|
|
11072
11223
|
const visited = /* @__PURE__ */ new Set();
|
|
11073
11224
|
let filesSeen = 0;
|
|
11074
|
-
const walk = (dir, depth) => {
|
|
11225
|
+
const walk = (dir, depth, confineTo) => {
|
|
11075
11226
|
if (depth > ROSTER_WALK_MAX_DEPTH || filesSeen >= ROSTER_WALK_MAX_FILES) return;
|
|
11076
11227
|
let real;
|
|
11077
11228
|
try {
|
|
11078
|
-
real =
|
|
11229
|
+
real = fs2.realpathSync(dir);
|
|
11079
11230
|
} catch {
|
|
11080
11231
|
return;
|
|
11081
11232
|
}
|
|
11233
|
+
if (confineTo !== null && !isInsideRoot(real, confineTo)) return;
|
|
11082
11234
|
const key = process.platform === "win32" ? real.toLocaleLowerCase() : real;
|
|
11083
11235
|
if (visited.has(key)) return;
|
|
11084
11236
|
visited.add(key);
|
|
11085
11237
|
let entries;
|
|
11086
11238
|
try {
|
|
11087
|
-
entries =
|
|
11239
|
+
entries = fs2.readdirSync(dir, { withFileTypes: true });
|
|
11088
11240
|
} catch {
|
|
11089
11241
|
return;
|
|
11090
11242
|
}
|
|
11091
11243
|
for (const entry of entries) {
|
|
11092
11244
|
if (filesSeen >= ROSTER_WALK_MAX_FILES) return;
|
|
11093
|
-
const full =
|
|
11245
|
+
const full = path3.join(dir, entry.name);
|
|
11094
11246
|
let stat;
|
|
11095
11247
|
try {
|
|
11096
|
-
stat =
|
|
11248
|
+
stat = fs2.statSync(full);
|
|
11097
11249
|
} catch {
|
|
11098
11250
|
continue;
|
|
11099
11251
|
}
|
|
11100
11252
|
if (stat.isDirectory()) {
|
|
11101
|
-
walk(full, depth + 1);
|
|
11253
|
+
walk(full, depth + 1, confineTo);
|
|
11102
11254
|
continue;
|
|
11103
11255
|
}
|
|
11104
11256
|
if (!stat.isFile() || !entry.name.toLowerCase().endsWith(".md")) continue;
|
|
11105
11257
|
filesSeen++;
|
|
11106
11258
|
let text;
|
|
11107
11259
|
try {
|
|
11108
|
-
text =
|
|
11260
|
+
text = fs2.readFileSync(full, "utf-8");
|
|
11109
11261
|
} catch {
|
|
11110
11262
|
continue;
|
|
11111
11263
|
}
|
|
11112
|
-
const parsed = parseAgentDefinition(text,
|
|
11264
|
+
const parsed = parseAgentDefinition(text, path3.basename(entry.name, path3.extname(entry.name)));
|
|
11113
11265
|
if (parsed !== null && parsed.restricted) names.add(parsed.name);
|
|
11114
11266
|
}
|
|
11115
11267
|
};
|
|
11116
|
-
for (const root of scanRoots)
|
|
11268
|
+
for (const root of scanRoots) {
|
|
11269
|
+
const isProjectRoot = roots === void 0 && path3.resolve(root) === path3.resolve(projectAgentsRoot);
|
|
11270
|
+
if (!isProjectRoot) {
|
|
11271
|
+
walk(root, 0, null);
|
|
11272
|
+
continue;
|
|
11273
|
+
}
|
|
11274
|
+
let rootIsLink;
|
|
11275
|
+
try {
|
|
11276
|
+
rootIsLink = fs2.lstatSync(root).isSymbolicLink();
|
|
11277
|
+
} catch {
|
|
11278
|
+
continue;
|
|
11279
|
+
}
|
|
11280
|
+
if (rootIsLink) continue;
|
|
11281
|
+
let confineTo;
|
|
11282
|
+
try {
|
|
11283
|
+
confineTo = fs2.realpathSync(root);
|
|
11284
|
+
} catch {
|
|
11285
|
+
continue;
|
|
11286
|
+
}
|
|
11287
|
+
walk(root, 0, confineTo);
|
|
11288
|
+
}
|
|
11117
11289
|
return Array.from(names).sort();
|
|
11118
11290
|
}
|
|
11119
11291
|
function buildUnrestrictedSpawnAdvisory(toolInput) {
|
|
@@ -11216,6 +11388,7 @@ function harnessForNormalization() {
|
|
|
11216
11388
|
if (detected === "kimi") return "kimi";
|
|
11217
11389
|
if (detected === "qwen") return "qwen";
|
|
11218
11390
|
if (detected === "copilot_cli") return "copilot_cli";
|
|
11391
|
+
if (detected === "vscode") return "vscode";
|
|
11219
11392
|
return "claude";
|
|
11220
11393
|
}
|
|
11221
11394
|
function safeSuggestions(output) {
|
|
@@ -11231,6 +11404,7 @@ async function relayInProcess(eventName, rawPayload) {
|
|
|
11231
11404
|
const harness = detectHarness();
|
|
11232
11405
|
const payload = eventName === "pre_tool_use" || eventName === "post_tool_use" ? normalizePayload(rawPayload, harnessForNormalization()) : rawPayload;
|
|
11233
11406
|
const event = buildEvent(eventName, payload);
|
|
11407
|
+
if (shouldSuppressDuplicateVscodeHook(event, harness)) return "{}";
|
|
11234
11408
|
if (!process.env["CLAUDE_CODE_SESSION_ID"] && event.sessionId) {
|
|
11235
11409
|
process.env["CLAUDE_CODE_SESSION_ID"] = event.sessionId;
|
|
11236
11410
|
}
|