token-goat 2.9.17 → 2.9.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/{token-goat-chunk-HRUGS6CO.mjs → token-goat-chunk-2GTB5RBZ.mjs} +365 -26
- package/dist/{token-goat-chunk-W2GUUQAQ.mjs → token-goat-chunk-3W5P673V.mjs} +1 -1
- package/dist/{token-goat-chunk-5WB7GDH4.mjs → token-goat-chunk-62VJ47WN.mjs} +6 -6
- package/dist/{token-goat-chunk-C26ZBGBC.mjs → token-goat-chunk-7DGTVBWP.mjs} +58 -44
- package/dist/{token-goat-chunk-GPCCYQSS.mjs → token-goat-chunk-AVRD7SBO.mjs} +60 -8
- package/dist/{token-goat-chunk-66224KCZ.mjs → token-goat-chunk-BTKDZW7F.mjs} +1 -1
- package/dist/{token-goat-chunk-LMFO66YD.mjs → token-goat-chunk-DA43OE7L.mjs} +1 -1
- package/dist/{token-goat-chunk-YFGEUIEO.mjs → token-goat-chunk-DA6TIDLI.mjs} +21 -16
- package/dist/{token-goat-chunk-CPHVXKNU.mjs → token-goat-chunk-DHIXE6GV.mjs} +2 -2
- package/dist/{token-goat-chunk-73GXYYZR.mjs → token-goat-chunk-EP7TCWHM.mjs} +304 -46
- package/dist/{token-goat-chunk-EKGVCAES.mjs → token-goat-chunk-EXLLLIC3.mjs} +4 -4
- package/dist/{token-goat-chunk-CICJFVIH.mjs → token-goat-chunk-F24NE3FO.mjs} +3 -3
- package/dist/{token-goat-chunk-THBLW56G.mjs → token-goat-chunk-FJZ5MZK7.mjs} +3 -3
- package/dist/{token-goat-chunk-PKNOHVWP.mjs → token-goat-chunk-FQYMZASI.mjs} +632 -350
- package/dist/{token-goat-chunk-RFFUCQK7.mjs → token-goat-chunk-FX5DHKGF.mjs} +5 -5
- package/dist/{token-goat-chunk-RB24DBBS.mjs → token-goat-chunk-GYC34EGE.mjs} +7 -8
- package/dist/{token-goat-chunk-2M33Q3O4.mjs → token-goat-chunk-HPMMLV37.mjs} +369 -120
- package/dist/{token-goat-chunk-BK3RPPTK.mjs → token-goat-chunk-IBTFGZBX.mjs} +19 -14
- package/dist/{token-goat-chunk-MWSVUKVV.mjs → token-goat-chunk-JNIFSVWM.mjs} +36 -10
- package/dist/{token-goat-chunk-KZPO4DWC.mjs → token-goat-chunk-KWM6KBPA.mjs} +2 -2
- package/dist/{token-goat-chunk-G75WIVFM.mjs → token-goat-chunk-L2UOJBV4.mjs} +21 -20
- package/dist/{token-goat-chunk-2XA5ZW4T.mjs → token-goat-chunk-LACH3WKS.mjs} +4 -4
- package/dist/token-goat-chunk-MU6I4IDI.mjs +337 -0
- package/dist/{token-goat-chunk-SWRKVA3J.mjs → token-goat-chunk-NJKNAC37.mjs} +1 -1
- package/dist/{token-goat-chunk-ERTXEKB6.mjs → token-goat-chunk-NMTKNYGF.mjs} +2 -0
- package/dist/{token-goat-chunk-CKIJWTRQ.mjs → token-goat-chunk-NYFYIRG3.mjs} +12 -12
- package/dist/{token-goat-chunk-4EXFN2AW.mjs → token-goat-chunk-OZRSREKR.mjs} +2 -2
- package/dist/token-goat-chunk-P46RTOAQ.mjs +27 -0
- package/dist/{token-goat-chunk-IDHWKNKZ.mjs → token-goat-chunk-PIAESKVU.mjs} +139 -23
- package/dist/{token-goat-chunk-26YUO7MP.mjs → token-goat-chunk-S7RFIX4Y.mjs} +180 -50
- package/dist/{token-goat-chunk-2VASS5VR.mjs → token-goat-chunk-SOKAPOOC.mjs} +23 -4
- package/dist/token-goat-chunk-VJQBYSXG.mjs +20 -0
- package/dist/{token-goat-chunk-7VQMOBPQ.mjs → token-goat-chunk-WKO5TVNA.mjs} +948 -618
- package/dist/{token-goat-chunk-UBZCON6N.mjs → token-goat-chunk-ZGQXRYKE.mjs} +29 -5
- package/dist/token-goat-hook.mjs +12 -12
- package/dist/token-goat.core.mjs +21 -20
- package/docs/cli.md +4 -3
- package/docs/security.md +1 -1
- package/package.json +1 -1
- package/dist/token-goat-chunk-7EGKXBWT.mjs +0 -173
- package/dist/token-goat-chunk-GLOM5W5C.mjs +0 -93
|
@@ -10,25 +10,25 @@ import {
|
|
|
10
10
|
resolveMinNetSavingsBytes,
|
|
11
11
|
storeBashOutputSync,
|
|
12
12
|
stripAnsiCodes
|
|
13
|
-
} from "./token-goat-chunk-
|
|
13
|
+
} from "./token-goat-chunk-7DGTVBWP.mjs";
|
|
14
14
|
import {
|
|
15
15
|
DEFAULT_MAX_AGE_MS,
|
|
16
16
|
DEFAULT_MAX_COUNT,
|
|
17
17
|
SOURCE_HINT,
|
|
18
18
|
countRedactionPlaceholders,
|
|
19
|
+
hasPreciseSecret,
|
|
19
20
|
recordStat,
|
|
20
21
|
recordUnmappedTool,
|
|
21
22
|
redactSecrets,
|
|
22
23
|
savedTokensFromBytes,
|
|
23
24
|
summarize,
|
|
24
25
|
sweepCacheRoots
|
|
25
|
-
} from "./token-goat-chunk-
|
|
26
|
+
} from "./token-goat-chunk-S7RFIX4Y.mjs";
|
|
26
27
|
import {
|
|
27
28
|
parse
|
|
28
29
|
} from "./token-goat-chunk-3BTK54F3.mjs";
|
|
29
30
|
import {
|
|
30
31
|
MARKDOWN_SIZE_THRESHOLD,
|
|
31
|
-
PARSER_FINGERPRINT,
|
|
32
32
|
deleteFileEmbeddings,
|
|
33
33
|
deleteFileRows,
|
|
34
34
|
eachUnfencedLine,
|
|
@@ -48,17 +48,20 @@ import {
|
|
|
48
48
|
isAvailable,
|
|
49
49
|
isEmbedFresh,
|
|
50
50
|
isEmbeddableDocument,
|
|
51
|
+
isImagePath,
|
|
52
|
+
isNonTextAsset,
|
|
51
53
|
isParseSkipEligible,
|
|
52
54
|
isTreeSitterAvailable,
|
|
53
55
|
loadRegexExtractors,
|
|
54
|
-
parseSourceSymbolsTreeSitterOnly
|
|
55
|
-
|
|
56
|
+
parseSourceSymbolsTreeSitterOnly,
|
|
57
|
+
parserFingerprintForLanguage
|
|
58
|
+
} from "./token-goat-chunk-EP7TCWHM.mjs";
|
|
56
59
|
import {
|
|
57
60
|
getFileEntry,
|
|
58
61
|
pathEqClause,
|
|
59
62
|
projectScopeClause,
|
|
60
63
|
querySymbols
|
|
61
|
-
} from "./token-goat-chunk-
|
|
64
|
+
} from "./token-goat-chunk-IBTFGZBX.mjs";
|
|
62
65
|
import {
|
|
63
66
|
detectLanguage,
|
|
64
67
|
detectLanguageOfFile,
|
|
@@ -66,7 +69,7 @@ import {
|
|
|
66
69
|
getDb,
|
|
67
70
|
languageHasFlag,
|
|
68
71
|
redactIfDotenv
|
|
69
|
-
} from "./token-goat-chunk-
|
|
72
|
+
} from "./token-goat-chunk-JNIFSVWM.mjs";
|
|
70
73
|
import {
|
|
71
74
|
detectHarness,
|
|
72
75
|
fileIsAbsent,
|
|
@@ -80,7 +83,7 @@ import {
|
|
|
80
83
|
makeProjectAt,
|
|
81
84
|
resolveProjectRoot,
|
|
82
85
|
shortFingerprint
|
|
83
|
-
} from "./token-goat-chunk-
|
|
86
|
+
} from "./token-goat-chunk-SOKAPOOC.mjs";
|
|
84
87
|
import {
|
|
85
88
|
registerReset
|
|
86
89
|
} from "./token-goat-chunk-EEIDFMEM.mjs";
|
|
@@ -88,6 +91,7 @@ import {
|
|
|
88
91
|
IDENTICAL_READ_MIN_BODY_BYTES,
|
|
89
92
|
LOCK_WAIT_MS_HARDENED,
|
|
90
93
|
PER_FILE_COUNTERFACTUAL_CEILING,
|
|
94
|
+
VERSION,
|
|
91
95
|
atomicWriteBytes,
|
|
92
96
|
atomicWriteText,
|
|
93
97
|
backupFile,
|
|
@@ -105,6 +109,7 @@ import {
|
|
|
105
109
|
hookPowershellCommand,
|
|
106
110
|
isCodeFenceDelimiter,
|
|
107
111
|
isInsideRoot,
|
|
112
|
+
isNodeModulesPath,
|
|
108
113
|
isTestFile,
|
|
109
114
|
isUnderBlockedRoot,
|
|
110
115
|
isWithinQuietHours,
|
|
@@ -126,8 +131,9 @@ import {
|
|
|
126
131
|
withFileLock,
|
|
127
132
|
withInstallScope,
|
|
128
133
|
writeIfDifferent
|
|
129
|
-
} from "./token-goat-chunk-
|
|
134
|
+
} from "./token-goat-chunk-ZGQXRYKE.mjs";
|
|
130
135
|
import {
|
|
136
|
+
TOOL_RESULTS_ID_CHARS,
|
|
131
137
|
displaySafeJson,
|
|
132
138
|
displaySafePath,
|
|
133
139
|
displaySafeText,
|
|
@@ -139,7 +145,7 @@ import {
|
|
|
139
145
|
resolveIndexPath,
|
|
140
146
|
scanForInjectionPatterns,
|
|
141
147
|
toDisplayPath
|
|
142
|
-
} from "./token-goat-chunk-
|
|
148
|
+
} from "./token-goat-chunk-NMTKNYGF.mjs";
|
|
143
149
|
import {
|
|
144
150
|
applyExifOrientation,
|
|
145
151
|
calculateFitInside,
|
|
@@ -156,6 +162,16 @@ import {
|
|
|
156
162
|
init_define_import_meta_env
|
|
157
163
|
} from "./token-goat-chunk-A37V4PBF.mjs";
|
|
158
164
|
|
|
165
|
+
// src/claude_config_dir.ts
|
|
166
|
+
init_define_import_meta_env();
|
|
167
|
+
import * as os from "node:os";
|
|
168
|
+
import * as path from "node:path";
|
|
169
|
+
function claudeConfigDir(homeDir = os.homedir()) {
|
|
170
|
+
const override = process.env["CLAUDE_CONFIG_DIR"];
|
|
171
|
+
if (override !== void 0 && override !== "") return override;
|
|
172
|
+
return path.join(homeDir, ".claude");
|
|
173
|
+
}
|
|
174
|
+
|
|
159
175
|
// src/index_health.ts
|
|
160
176
|
init_define_import_meta_env();
|
|
161
177
|
import * as fs2 from "fs";
|
|
@@ -163,14 +179,14 @@ import * as fs2 from "fs";
|
|
|
163
179
|
// src/walk_mode.ts
|
|
164
180
|
init_define_import_meta_env();
|
|
165
181
|
import * as fs from "fs";
|
|
166
|
-
import * as
|
|
182
|
+
import * as path2 from "path";
|
|
167
183
|
function detectWalkMode(cwd) {
|
|
168
184
|
const project = findProject(cwd);
|
|
169
185
|
if (project?.marker === ".git") return "git";
|
|
170
186
|
let cur = cwd;
|
|
171
187
|
while (true) {
|
|
172
|
-
if (fs.existsSync(
|
|
173
|
-
const parent =
|
|
188
|
+
if (fs.existsSync(path2.join(cur, ".git"))) return "git";
|
|
189
|
+
const parent = path2.dirname(cur);
|
|
174
190
|
if (parent === cur) break;
|
|
175
191
|
cur = parent;
|
|
176
192
|
}
|
|
@@ -206,17 +222,17 @@ function getEmbeddingCoverage(dbPath, rootDir) {
|
|
|
206
222
|
embeddedFiles: countScoped("SELECT COUNT(DISTINCT file_path) as c FROM chunks", "file_path")
|
|
207
223
|
};
|
|
208
224
|
}
|
|
209
|
-
function getParserFreshness(dbPath,
|
|
225
|
+
function getParserFreshness(dbPath, expectedFor, rootDir) {
|
|
210
226
|
const db = getDb(dbPath);
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
}
|
|
219
|
-
return { indexedFiles
|
|
227
|
+
const scope = rootDir === void 0 ? void 0 : projectScopeClause("path");
|
|
228
|
+
const rows = db.prepare(`SELECT language, parser_sha, COUNT(*) as c FROM files${scope === void 0 ? "" : ` WHERE ${scope.clause}`} GROUP BY language, parser_sha`).all(...scope === void 0 || rootDir === void 0 ? [] : scope.params(rootDir));
|
|
229
|
+
let indexedFiles = 0;
|
|
230
|
+
let currentFiles = 0;
|
|
231
|
+
for (const row of rows) {
|
|
232
|
+
indexedFiles += row.c;
|
|
233
|
+
if (row.parser_sha !== null && row.parser_sha === expectedFor(row.language ?? "unknown")) currentFiles += row.c;
|
|
234
|
+
}
|
|
235
|
+
return { indexedFiles, currentFiles };
|
|
220
236
|
}
|
|
221
237
|
function isIndexEmptyForProject(dbPath, rootDir) {
|
|
222
238
|
if (!fs2.existsSync(dbPath)) return true;
|
|
@@ -237,8 +253,8 @@ function emptyIndexMessage(rootDir) {
|
|
|
237
253
|
// src/bridges/visualstudio_install.ts
|
|
238
254
|
init_define_import_meta_env();
|
|
239
255
|
import * as fs4 from "node:fs";
|
|
240
|
-
import * as
|
|
241
|
-
import * as
|
|
256
|
+
import * as os2 from "node:os";
|
|
257
|
+
import * as path4 from "node:path";
|
|
242
258
|
|
|
243
259
|
// src/bridges/guidance_block.ts
|
|
244
260
|
init_define_import_meta_env();
|
|
@@ -256,6 +272,7 @@ function buildGuidanceBody(fallbackToolClause, opts = {}) {
|
|
|
256
272
|
"Exemptions (gate passes, read directly): the file is under ~200 lines and you need all of it; it was never indexed (new, untracked, or generated this turn); it is a genuinely opaque binary (not an image); the target has no symbol handle (e.g. a literal mid-function).",
|
|
257
273
|
"",
|
|
258
274
|
"Failure shapes to catch yourself in, and the command that replaces each:",
|
|
275
|
+
'- not knowing which command answers a question you can already state in words \u2192 `answer "<question>"`, which resolves the subject in the index, runs the right command, prints `via: <command>`, and refuses rather than guess',
|
|
259
276
|
'- a shell text search with context flags to find a function body \u2192 `read "file::symbol"`',
|
|
260
277
|
'- paging one function with view/view_range \u2192 `read "file::symbol"`',
|
|
261
278
|
'- reading a symbol plus chasing its callers and containing doc section as separate reads \u2192 `brief "file::symbol"`',
|
|
@@ -269,7 +286,7 @@ function buildGuidanceBody(fallbackToolClause, opts = {}) {
|
|
|
269
286
|
"- opening a screenshot, diagram, or scan to read the text in it \u2192 `image-text file`",
|
|
270
287
|
"- opening a PDF or Office document \u2192 inspect its format first, then read a narrow slice: PDF `pdf-meta`/`pdf-outline` then `pdf-locate` to find the pages and `pdf-extract` only those; Word `docx-outline` then `docx-tables`/`docx-text`; PowerPoint `pptx-outline` then `pptx-slide`/`pptx-notes`; Excel `xlsx-sheets` then `xlsx-columns`/`xlsx-head`/`xlsx-range`/`xlsx-query`",
|
|
271
288
|
"",
|
|
272
|
-
'Commands: `symbol NAME`, `read "file::symbol"`, `brief "file::symbol"`, `section "file::Heading"`, `semantic "description"`, `outline file`/`skeleton file`, `map --compact`, `refs file::symbol --callers`, `changed --symbol`, `config-get file KEY`, `json-query file \'a.b.c\'`/`yaml-query`/`xml-query`, `json-outline file`/`yaml-outline`/`xml-outline`, `bash-output`/`web-output`/`mcp-output`, ' + (gdrive ? "`gdrive-sections <file-id>`, " : "") + "`image-meta file`/`image-text file`, `pdf-meta`/`pdf-outline`/`pdf-locate`/`pdf-extract`, `docx-outline`/`docx-tables`/`docx-text`, `pptx-outline`/`pptx-slide`/`pptx-notes`/`pptx-text`, `xlsx-sheets`/`xlsx-columns`/`xlsx-head`/`xlsx-range`/`xlsx-query`.",
|
|
289
|
+
'Commands: `answer "<question>"`, `symbol NAME`, `read "file::symbol"`, `brief "file::symbol"`, `section "file::Heading"`, `semantic "description"`, `outline file`/`skeleton file`, `map --compact`, `refs file::symbol --callers`, `changed --symbol`, `config-get file KEY`, `json-query file \'a.b.c\'`/`yaml-query`/`xml-query`, `json-outline file`/`yaml-outline`/`xml-outline`, `bash-output`/`web-output`/`mcp-output`, ' + (gdrive ? "`gdrive-sections <file-id>`, " : "") + "`image-meta file`/`image-text file`, `pdf-meta`/`pdf-outline`/`pdf-locate`/`pdf-extract`, `docx-outline`/`docx-tables`/`docx-text`, `pptx-outline`/`pptx-slide`/`pptx-notes`/`pptx-text`, `xlsx-sheets`/`xlsx-columns`/`xlsx-head`/`xlsx-range`/`xlsx-query`.",
|
|
273
290
|
"",
|
|
274
291
|
"Sub-agent briefs must carry this gate verbatim: a sub-agent inherits none of this context and its reads spend the same token budget.",
|
|
275
292
|
"",
|
|
@@ -286,7 +303,7 @@ function buildGuidanceBlock(h) {
|
|
|
286
303
|
// src/bridges/mcp_servers_json.ts
|
|
287
304
|
init_define_import_meta_env();
|
|
288
305
|
import * as fs3 from "node:fs";
|
|
289
|
-
import * as
|
|
306
|
+
import * as path3 from "node:path";
|
|
290
307
|
import { createRequire } from "node:module";
|
|
291
308
|
import { fileURLToPath } from "node:url";
|
|
292
309
|
var jsoncParser;
|
|
@@ -295,10 +312,10 @@ function jsonc() {
|
|
|
295
312
|
return jsoncParser;
|
|
296
313
|
}
|
|
297
314
|
function bundledCliPath() {
|
|
298
|
-
const here =
|
|
299
|
-
const bundled =
|
|
315
|
+
const here = path3.dirname(fileURLToPath(import.meta.url));
|
|
316
|
+
const bundled = path3.join(here, "token-goat.mjs");
|
|
300
317
|
if (fs3.existsSync(bundled)) return bundled;
|
|
301
|
-
return
|
|
318
|
+
return path3.resolve(here, "..", "..", "dist", "token-goat.mjs");
|
|
302
319
|
}
|
|
303
320
|
function managedServer() {
|
|
304
321
|
return {
|
|
@@ -311,7 +328,7 @@ function isManagedServer(value) {
|
|
|
311
328
|
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
312
329
|
const entry = value;
|
|
313
330
|
const args = entry["args"];
|
|
314
|
-
return entry["type"] === "stdio" && Array.isArray(args) && args.length === 2 && typeof args[0] === "string" &&
|
|
331
|
+
return entry["type"] === "stdio" && Array.isArray(args) && args.length === 2 && typeof args[0] === "string" && path3.basename(args[0]).toLowerCase() === "token-goat.mjs" && args[1] === "mcp-serve";
|
|
315
332
|
}
|
|
316
333
|
function readServersJson(filePath, label) {
|
|
317
334
|
const text = fs3.existsSync(filePath) ? fs3.readFileSync(filePath, "utf8") : "{}\n";
|
|
@@ -357,6 +374,16 @@ function holdsOnlyManagedServer(value) {
|
|
|
357
374
|
const names = Object.keys(servers);
|
|
358
375
|
return names.length === 1 && names[0] === "token-goat" && isManagedServer(servers["token-goat"]);
|
|
359
376
|
}
|
|
377
|
+
function isResidueServersJson(text, rootKey = "servers") {
|
|
378
|
+
const trimmed = text.trim();
|
|
379
|
+
if (trimmed === "") return true;
|
|
380
|
+
const parsed = parseObject(trimmed);
|
|
381
|
+
if (parsed === null) return false;
|
|
382
|
+
const keys = Object.keys(parsed);
|
|
383
|
+
if (keys.length !== 1 || keys[0] !== rootKey) return false;
|
|
384
|
+
const servers = parsed[rootKey];
|
|
385
|
+
return servers !== null && typeof servers === "object" && !Array.isArray(servers) && Object.keys(servers).length === 0;
|
|
386
|
+
}
|
|
360
387
|
function dropEmptyServers(text, rootKey = "servers") {
|
|
361
388
|
const parsed = parseObject(text);
|
|
362
389
|
if (parsed === null) return text;
|
|
@@ -399,10 +426,10 @@ var SIBLING_GATE_MARKERS = [
|
|
|
399
426
|
["<!-- token-goat-begin -->", "<!-- token-goat-end -->"]
|
|
400
427
|
];
|
|
401
428
|
function visualStudioUserMcpPath() {
|
|
402
|
-
return
|
|
429
|
+
return path4.join(os2.homedir(), ".mcp.json");
|
|
403
430
|
}
|
|
404
431
|
function visualStudioProjectMcpPath(projectRoot = process.cwd()) {
|
|
405
|
-
return
|
|
432
|
+
return path4.join(path4.resolve(projectRoot), ".mcp.json");
|
|
406
433
|
}
|
|
407
434
|
function visualStudioMcpPath(opts = {}) {
|
|
408
435
|
return opts.project === true ? visualStudioProjectMcpPath(opts.projectRoot) : visualStudioUserMcpPath();
|
|
@@ -411,7 +438,7 @@ function otherScopeMcpPath(opts) {
|
|
|
411
438
|
return opts.project === true ? visualStudioUserMcpPath() : visualStudioProjectMcpPath(opts.projectRoot);
|
|
412
439
|
}
|
|
413
440
|
function visualStudioInstructionsPath(opts = {}) {
|
|
414
|
-
return opts.project === true ?
|
|
441
|
+
return opts.project === true ? path4.join(path4.resolve(opts.projectRoot ?? process.cwd()), ".github", "copilot-instructions.md") : path4.join(os2.homedir(), "copilot-instructions.md");
|
|
415
442
|
}
|
|
416
443
|
function visualStudioOtherScopeHasManagedServer(opts = {}) {
|
|
417
444
|
return hasManagedServer(otherScopeMcpPath(opts), LABEL);
|
|
@@ -420,7 +447,7 @@ function isVisualStudioInstalled(opts = {}) {
|
|
|
420
447
|
return hasManagedServer(visualStudioMcpPath(opts), LABEL);
|
|
421
448
|
}
|
|
422
449
|
function visualStudioSolutionVscodeMcpPath(projectRoot = process.cwd()) {
|
|
423
|
-
return
|
|
450
|
+
return path4.join(path4.resolve(projectRoot), ".vscode", "mcp.json");
|
|
424
451
|
}
|
|
425
452
|
function visualStudioManagedRegistrations(projectRoot = process.cwd()) {
|
|
426
453
|
return [visualStudioUserMcpPath(), visualStudioProjectMcpPath(projectRoot), visualStudioSolutionVscodeMcpPath(projectRoot)].filter((p) => hasManagedServer(p, LABEL));
|
|
@@ -498,7 +525,7 @@ function installVisualStudioScoped(opts) {
|
|
|
498
525
|
}
|
|
499
526
|
const next = ensureMcpServersKey(setTokenGoatServer(config.text, managedServer()));
|
|
500
527
|
if (config.text !== next) {
|
|
501
|
-
ensureDirSync(
|
|
528
|
+
ensureDirSync(path4.dirname(mcpPath));
|
|
502
529
|
backupFile(mcpPath);
|
|
503
530
|
atomicWriteText(mcpPath, next);
|
|
504
531
|
if (!mcpExisted || ownedAlready) recordCreatedConfig(mcpPath);
|
|
@@ -536,8 +563,8 @@ function uninstallVisualStudioScoped(opts) {
|
|
|
536
563
|
// src/bridges/copilot_cli_install.ts
|
|
537
564
|
init_define_import_meta_env();
|
|
538
565
|
import * as fs5 from "node:fs";
|
|
539
|
-
import * as
|
|
540
|
-
import * as
|
|
566
|
+
import * as os3 from "node:os";
|
|
567
|
+
import * as path5 from "node:path";
|
|
541
568
|
|
|
542
569
|
// src/bridges/copilot_cli.ts
|
|
543
570
|
init_define_import_meta_env();
|
|
@@ -1255,43 +1282,43 @@ var COPILOT_CLI_HOOK_EVENTS = [
|
|
|
1255
1282
|
];
|
|
1256
1283
|
function copilotCliUserRoot() {
|
|
1257
1284
|
const override = process.env["COPILOT_HOME"];
|
|
1258
|
-
if (override !== void 0 && override.trim() !== "") return
|
|
1259
|
-
return
|
|
1285
|
+
if (override !== void 0 && override.trim() !== "") return path5.resolve(override);
|
|
1286
|
+
return path5.join(os3.homedir(), ".copilot");
|
|
1260
1287
|
}
|
|
1261
1288
|
function copilotCliUserHooksDir() {
|
|
1262
|
-
return
|
|
1289
|
+
return path5.join(copilotCliUserRoot(), "hooks");
|
|
1263
1290
|
}
|
|
1264
1291
|
function copilotCliProjectHooksDir() {
|
|
1265
|
-
return
|
|
1292
|
+
return path5.join(process.cwd(), ".github", "hooks");
|
|
1266
1293
|
}
|
|
1267
1294
|
function copilotCliCacheRoot() {
|
|
1268
1295
|
const override = process.env["COPILOT_CACHE_HOME"];
|
|
1269
|
-
if (override !== void 0 && override.trim() !== "") return
|
|
1270
|
-
const home =
|
|
1271
|
-
if (process.platform === "darwin") return
|
|
1296
|
+
if (override !== void 0 && override.trim() !== "") return path5.resolve(override);
|
|
1297
|
+
const home = os3.homedir();
|
|
1298
|
+
if (process.platform === "darwin") return path5.join(home, "Library", "Caches", "copilot");
|
|
1272
1299
|
if (process.platform === "win32") {
|
|
1273
1300
|
const local = process.env["LOCALAPPDATA"];
|
|
1274
|
-
const base2 = local !== void 0 && local.trim() !== "" ? local :
|
|
1275
|
-
return
|
|
1301
|
+
const base2 = local !== void 0 && local.trim() !== "" ? local : path5.join(home, ".cache");
|
|
1302
|
+
return path5.join(base2, "copilot");
|
|
1276
1303
|
}
|
|
1277
1304
|
const xdg = process.env["XDG_CACHE_HOME"];
|
|
1278
|
-
const base = xdg !== void 0 && xdg.trim() !== "" ? xdg :
|
|
1279
|
-
return
|
|
1305
|
+
const base = xdg !== void 0 && xdg.trim() !== "" ? xdg : path5.join(home, ".cache");
|
|
1306
|
+
return path5.join(base, "copilot");
|
|
1280
1307
|
}
|
|
1281
1308
|
function copilotCliMcpToolsDir() {
|
|
1282
|
-
return
|
|
1309
|
+
return path5.join(copilotCliCacheRoot(), "mcp-tools");
|
|
1283
1310
|
}
|
|
1284
1311
|
function copilotCliHooksDir(opts = {}) {
|
|
1285
1312
|
return opts.local === true ? copilotCliProjectHooksDir() : copilotCliUserHooksDir();
|
|
1286
1313
|
}
|
|
1287
1314
|
function copilotCliConfigPath(opts = {}) {
|
|
1288
|
-
return
|
|
1315
|
+
return path5.join(copilotCliHooksDir(opts), HOOKS_CONFIG_FILE);
|
|
1289
1316
|
}
|
|
1290
1317
|
function copilotCliScriptPath(opts = {}) {
|
|
1291
|
-
return
|
|
1318
|
+
return path5.join(copilotCliHooksDir(opts), HOOKS_SCRIPT_FILE);
|
|
1292
1319
|
}
|
|
1293
1320
|
function copilotCliInstructionsPath(opts = {}) {
|
|
1294
|
-
return
|
|
1321
|
+
return path5.join(path5.dirname(copilotCliHooksDir(opts)), "copilot-instructions.md");
|
|
1295
1322
|
}
|
|
1296
1323
|
var COPILOT_INSTRUCTIONS_BEGIN = "<!-- token-goat-begin -->";
|
|
1297
1324
|
var COPILOT_INSTRUCTIONS_END = "<!-- token-goat-end -->";
|
|
@@ -1349,10 +1376,10 @@ var HOOKS_CONFIG_FILE = "token-goat.json";
|
|
|
1349
1376
|
var HOOKS_SCRIPT_FILE = "token-goat-shim.js";
|
|
1350
1377
|
var HOOKS_OWNERS_FILE = "token-goat.owners";
|
|
1351
1378
|
function copilotHooksOwnersPath(hooksDir) {
|
|
1352
|
-
return
|
|
1379
|
+
return path5.join(hooksDir, HOOKS_OWNERS_FILE);
|
|
1353
1380
|
}
|
|
1354
1381
|
function copilotHooksFilePaths(hooksDir) {
|
|
1355
|
-
return [
|
|
1382
|
+
return [path5.join(hooksDir, HOOKS_CONFIG_FILE), path5.join(hooksDir, HOOKS_SCRIPT_FILE), copilotHooksOwnersPath(hooksDir)];
|
|
1356
1383
|
}
|
|
1357
1384
|
function readCopilotHooksOwners(hooksDir) {
|
|
1358
1385
|
const owners = /* @__PURE__ */ new Set();
|
|
@@ -1360,7 +1387,7 @@ function readCopilotHooksOwners(hooksDir) {
|
|
|
1360
1387
|
try {
|
|
1361
1388
|
text = fs5.readFileSync(copilotHooksOwnersPath(hooksDir), "utf8");
|
|
1362
1389
|
} catch {
|
|
1363
|
-
if (fs5.existsSync(
|
|
1390
|
+
if (fs5.existsSync(path5.join(hooksDir, HOOKS_CONFIG_FILE))) owners.add("copilot");
|
|
1364
1391
|
return owners;
|
|
1365
1392
|
}
|
|
1366
1393
|
for (const line of text.split(/\r?\n/)) {
|
|
@@ -1370,8 +1397,8 @@ function readCopilotHooksOwners(hooksDir) {
|
|
|
1370
1397
|
return owners;
|
|
1371
1398
|
}
|
|
1372
1399
|
function installCopilotHooksFile(hooksDir, owner) {
|
|
1373
|
-
const configPath =
|
|
1374
|
-
const scriptPath =
|
|
1400
|
+
const configPath = path5.join(hooksDir, HOOKS_CONFIG_FILE);
|
|
1401
|
+
const scriptPath = path5.join(hooksDir, HOOKS_SCRIPT_FILE);
|
|
1375
1402
|
const owners = readCopilotHooksOwners(hooksDir);
|
|
1376
1403
|
const scriptChanged = writeIfDifferent(scriptPath, COPILOT_CLI_HOOK_SCRIPT);
|
|
1377
1404
|
const desiredText = JSON.stringify(buildConfig(scriptPath), null, 2) + "\n";
|
|
@@ -1391,10 +1418,10 @@ function releaseCopilotHooksFile(hooksDir, owner, keepBackups = false) {
|
|
|
1391
1418
|
writeIfDifferent(ownersPath, [...owners].sort().join("\n") + "\n");
|
|
1392
1419
|
return true;
|
|
1393
1420
|
}
|
|
1394
|
-
const configRemoved = removeFileInScope(
|
|
1395
|
-
if (!keepBackups) removeCreatedBackups(
|
|
1396
|
-
takeCreatedConfig(
|
|
1397
|
-
const scriptRemoved = removeFileInScope(
|
|
1421
|
+
const configRemoved = removeFileInScope(path5.join(hooksDir, HOOKS_CONFIG_FILE));
|
|
1422
|
+
if (!keepBackups) removeCreatedBackups(path5.join(hooksDir, HOOKS_CONFIG_FILE));
|
|
1423
|
+
takeCreatedConfig(path5.join(hooksDir, HOOKS_CONFIG_FILE));
|
|
1424
|
+
const scriptRemoved = removeFileInScope(path5.join(hooksDir, HOOKS_SCRIPT_FILE));
|
|
1398
1425
|
const ownersRemoved = removeFileInScope(ownersPath);
|
|
1399
1426
|
return configRemoved || scriptRemoved || ownersRemoved;
|
|
1400
1427
|
}
|
|
@@ -1453,7 +1480,7 @@ init_define_import_meta_env();
|
|
|
1453
1480
|
// src/copilot_mcp_tools.ts
|
|
1454
1481
|
init_define_import_meta_env();
|
|
1455
1482
|
import fs6 from "node:fs";
|
|
1456
|
-
import
|
|
1483
|
+
import path6 from "node:path";
|
|
1457
1484
|
|
|
1458
1485
|
// src/overflow_guard.ts
|
|
1459
1486
|
init_define_import_meta_env();
|
|
@@ -1552,7 +1579,7 @@ function readCopilotMcpTools(dir = copilotCliMcpToolsDir()) {
|
|
|
1552
1579
|
if (!entry.toLowerCase().endsWith(".json")) continue;
|
|
1553
1580
|
let parsed;
|
|
1554
1581
|
try {
|
|
1555
|
-
parsed = JSON.parse(fs6.readFileSync(
|
|
1582
|
+
parsed = JSON.parse(fs6.readFileSync(path6.join(dir, entry), "utf8"));
|
|
1556
1583
|
} catch {
|
|
1557
1584
|
unreadable += 1;
|
|
1558
1585
|
continue;
|
|
@@ -1970,15 +1997,15 @@ function extractToolResponseField(raw, keys) {
|
|
|
1970
1997
|
if (typeof resp === "string") return resp;
|
|
1971
1998
|
if (resp !== null && typeof resp === "object") {
|
|
1972
1999
|
const r = resp;
|
|
1973
|
-
const
|
|
1974
|
-
if (
|
|
2000
|
+
const path26 = resolveToolResponseFieldPath(r, keys);
|
|
2001
|
+
if (path26 !== null) return readAtPath(r, path26);
|
|
1975
2002
|
}
|
|
1976
2003
|
return "";
|
|
1977
2004
|
}
|
|
1978
2005
|
var NESTED_TOOL_RESPONSE_PATHS = [["file", "content"]];
|
|
1979
|
-
function readAtPath(resp,
|
|
2006
|
+
function readAtPath(resp, path26) {
|
|
1980
2007
|
let cur = resp;
|
|
1981
|
-
for (const seg of
|
|
2008
|
+
for (const seg of path26) {
|
|
1982
2009
|
if (cur === null || typeof cur !== "object") return "";
|
|
1983
2010
|
cur = cur[seg];
|
|
1984
2011
|
}
|
|
@@ -1988,25 +2015,25 @@ function resolveToolResponseFieldPath(resp, keys) {
|
|
|
1988
2015
|
for (const key of keys) {
|
|
1989
2016
|
if (typeof resp[key] === "string" && resp[key] !== "") return [key];
|
|
1990
2017
|
}
|
|
1991
|
-
for (const
|
|
1992
|
-
if (readAtPath(resp,
|
|
2018
|
+
for (const path26 of NESTED_TOOL_RESPONSE_PATHS) {
|
|
2019
|
+
if (readAtPath(resp, path26) !== "") return path26;
|
|
1993
2020
|
}
|
|
1994
2021
|
return null;
|
|
1995
2022
|
}
|
|
1996
2023
|
function replaceToolResponseField(resp, keys, newText) {
|
|
1997
|
-
const
|
|
1998
|
-
if (
|
|
2024
|
+
const path26 = resolveToolResponseFieldPath(resp, keys);
|
|
2025
|
+
if (path26 === null) return null;
|
|
1999
2026
|
const root = { ...resp };
|
|
2000
2027
|
let cur = root;
|
|
2001
|
-
for (let i = 0; i <
|
|
2002
|
-
const seg =
|
|
2028
|
+
for (let i = 0; i < path26.length - 1; i++) {
|
|
2029
|
+
const seg = path26[i];
|
|
2003
2030
|
const child = cur[seg];
|
|
2004
2031
|
if (child === null || typeof child !== "object") return null;
|
|
2005
2032
|
const cloned = { ...child };
|
|
2006
2033
|
cur[seg] = cloned;
|
|
2007
2034
|
cur = cloned;
|
|
2008
2035
|
}
|
|
2009
|
-
cur[
|
|
2036
|
+
cur[path26[path26.length - 1]] = newText;
|
|
2010
2037
|
return root;
|
|
2011
2038
|
}
|
|
2012
2039
|
var OUTPUT_FIRST_TOOL_RESPONSE_KEYS = ["output", "content", "text", "body", "stdout", "stderr"];
|
|
@@ -2093,8 +2120,8 @@ function passOutput() {
|
|
|
2093
2120
|
function denyOutput(message) {
|
|
2094
2121
|
return { hookType: "deny", message: `[tg] ${neutralizeOutsideFences(message)}` };
|
|
2095
2122
|
}
|
|
2096
|
-
function contextOutput(context) {
|
|
2097
|
-
return { hookType: "context", context };
|
|
2123
|
+
function contextOutput(context, correlators) {
|
|
2124
|
+
return correlators === void 0 ? { hookType: "context", context } : { hookType: "context", context, correlators };
|
|
2098
2125
|
}
|
|
2099
2126
|
function emitRewrite(updatedOutput, detail, savings, redaction = "count-here") {
|
|
2100
2127
|
if (detectHarness() === "vscode") return passOutput();
|
|
@@ -2104,7 +2131,8 @@ function emitRewrite(updatedOutput, detail, savings, redaction = "count-here") {
|
|
|
2104
2131
|
}
|
|
2105
2132
|
if (savings !== void 0) {
|
|
2106
2133
|
const bytesSaved = savings.originalBytes - Buffer.byteLength(updatedOutput, "utf-8");
|
|
2107
|
-
|
|
2134
|
+
const credited = Math.max(0, bytesSaved);
|
|
2135
|
+
recordStat(savings.kind, credited, savedTokensFromBytes(credited), void 0, savings.detail);
|
|
2108
2136
|
}
|
|
2109
2137
|
return { hookType: "rewriteOutput", updatedOutput };
|
|
2110
2138
|
}
|
|
@@ -2163,8 +2191,8 @@ function makeDedupHintHandlers(opts) {
|
|
|
2163
2191
|
init_define_import_meta_env();
|
|
2164
2192
|
import * as crypto from "node:crypto";
|
|
2165
2193
|
import * as fs7 from "node:fs";
|
|
2166
|
-
import * as
|
|
2167
|
-
import * as
|
|
2194
|
+
import * as os4 from "node:os";
|
|
2195
|
+
import * as path7 from "node:path";
|
|
2168
2196
|
function vscodeToolName(event) {
|
|
2169
2197
|
const name = event?.raw[VSCODE_TOOL_NAME_KEY];
|
|
2170
2198
|
return typeof name === "string" ? name : void 0;
|
|
@@ -2174,10 +2202,10 @@ var MATERIALIZED_MAX_AGE_MS = 60 * 60 * 1e3;
|
|
|
2174
2202
|
function pruneMaterialized() {
|
|
2175
2203
|
const now = Date.now();
|
|
2176
2204
|
try {
|
|
2177
|
-
const dir =
|
|
2205
|
+
const dir = os4.tmpdir();
|
|
2178
2206
|
for (const file of fs7.readdirSync(dir)) {
|
|
2179
2207
|
if (!file.startsWith(SHRINK_PREFIX)) continue;
|
|
2180
|
-
const full =
|
|
2208
|
+
const full = path7.join(dir, file);
|
|
2181
2209
|
try {
|
|
2182
2210
|
const st = fs7.statSync(full);
|
|
2183
2211
|
if (st.isFile() && now - st.mtimeMs > MATERIALIZED_MAX_AGE_MS) fs7.unlinkSync(full);
|
|
@@ -2194,7 +2222,7 @@ function materializeShrunkImageFile(context) {
|
|
|
2194
2222
|
if (!match) return void 0;
|
|
2195
2223
|
try {
|
|
2196
2224
|
pruneMaterialized();
|
|
2197
|
-
const file =
|
|
2225
|
+
const file = path7.join(os4.tmpdir(), `${SHRINK_PREFIX}${process.pid}-${Date.now()}-${crypto.randomUUID()}.${match[1]}`);
|
|
2198
2226
|
fs7.writeFileSync(file, Buffer.from(match[2], "base64"), { mode: 384 });
|
|
2199
2227
|
return file;
|
|
2200
2228
|
} catch {
|
|
@@ -2272,7 +2300,15 @@ function noteUnrecognizedTool(event, list) {
|
|
|
2272
2300
|
const toolName = event.toolName;
|
|
2273
2301
|
if (typeof toolName !== "string" || toolName === "") return;
|
|
2274
2302
|
const named = [];
|
|
2275
|
-
for (const { toolName: want } of list) {
|
|
2303
|
+
for (const { toolName: want, toolPattern } of list) {
|
|
2304
|
+
if (toolPattern !== void 0) {
|
|
2305
|
+
let matches = false;
|
|
2306
|
+
try {
|
|
2307
|
+
matches = new RegExp(toolPattern).test(toolName);
|
|
2308
|
+
} catch {
|
|
2309
|
+
}
|
|
2310
|
+
if (matches) return;
|
|
2311
|
+
}
|
|
2276
2312
|
if (want === void 0) continue;
|
|
2277
2313
|
if (want === toolName) return;
|
|
2278
2314
|
named.push(want);
|
|
@@ -2319,11 +2355,7 @@ var CLAUDE_CODE_EVENT_NAMES = {
|
|
|
2319
2355
|
user_prompt_submit: "UserPromptSubmit",
|
|
2320
2356
|
subagent_stop: "SubagentStop",
|
|
2321
2357
|
session_start: "SessionStart",
|
|
2322
|
-
// Claude Code has no separate failure event -- a failed tool arrives on PostToolUse there,
|
|
2323
|
-
// and only Copilot splits it out. This entry exists because the map is exhaustive over
|
|
2324
|
-
// HookEventName, and it is a spelling for the response envelope rather than a claim that
|
|
2325
|
-
// Claude Code will ever send this event. Nothing iterates this map to build install config,
|
|
2326
|
-
// so naming an event Claude Code does not have cannot register one.
|
|
2358
|
+
// Claude Code has no separate failure event -- a failed tool arrives on PostToolUse there, and only Copilot splits it out. This entry exists because the map is exhaustive over HookEventName, and it is a spelling for the response envelope rather than a claim that Claude Code will ever send this event. Nothing iterates this map to build install config, so naming an event Claude Code does not have cannot register one.
|
|
2327
2359
|
post_tool_use_failure: "PostToolUseFailure"
|
|
2328
2360
|
};
|
|
2329
2361
|
var EVENTS_WITHOUT_ADDITIONAL_CONTEXT = /* @__PURE__ */ new Set([
|
|
@@ -2400,6 +2432,8 @@ var _curlDownloads = /* @__PURE__ */ new Map();
|
|
|
2400
2432
|
var _curlDownloadsAtLoad = /* @__PURE__ */ new Map();
|
|
2401
2433
|
var _fileLineRanges = /* @__PURE__ */ new Map();
|
|
2402
2434
|
var _fileServedOutputs = /* @__PURE__ */ new Map();
|
|
2435
|
+
var _fileLineRangesAtLoad = /* @__PURE__ */ new Map();
|
|
2436
|
+
var _fileServedOutputsAtLoad = /* @__PURE__ */ new Map();
|
|
2403
2437
|
var _compactedAt = 0;
|
|
2404
2438
|
var _cliReads = /* @__PURE__ */ new Set();
|
|
2405
2439
|
var _pendingLargeFileHints = /* @__PURE__ */ new Map();
|
|
@@ -2478,7 +2512,6 @@ function recordFileEdit(filePath) {
|
|
|
2478
2512
|
const normalized = normalizePath(filePath);
|
|
2479
2513
|
const key = resolveFilesKey(normalized);
|
|
2480
2514
|
_fileLineRanges.delete(foldPath(normalized));
|
|
2481
|
-
_fileServedOutputs.delete(foldPath(normalized));
|
|
2482
2515
|
const prev = _files.get(key);
|
|
2483
2516
|
if (prev === void 0) {
|
|
2484
2517
|
_files.set(key, {
|
|
@@ -2668,6 +2701,20 @@ function consumedCurlDownloadKeys() {
|
|
|
2668
2701
|
}
|
|
2669
2702
|
return consumed;
|
|
2670
2703
|
}
|
|
2704
|
+
function consumedFileLineRangeKeys() {
|
|
2705
|
+
const consumed = [];
|
|
2706
|
+
for (const key of _fileLineRangesAtLoad.keys()) {
|
|
2707
|
+
if (!_fileLineRanges.has(key)) consumed.push(key);
|
|
2708
|
+
}
|
|
2709
|
+
return consumed;
|
|
2710
|
+
}
|
|
2711
|
+
function consumedFileServedOutputKeys() {
|
|
2712
|
+
const consumed = [];
|
|
2713
|
+
for (const key of _fileServedOutputsAtLoad.keys()) {
|
|
2714
|
+
if (!_fileServedOutputs.has(key)) consumed.push(key);
|
|
2715
|
+
}
|
|
2716
|
+
return consumed;
|
|
2717
|
+
}
|
|
2671
2718
|
var MAX_RANGES_PER_FILE = 64;
|
|
2672
2719
|
function statIdentity(absPath) {
|
|
2673
2720
|
try {
|
|
@@ -2748,9 +2795,9 @@ function getSessionId() {
|
|
|
2748
2795
|
_sessionId = fromEnv !== void 0 && fromEnv !== "" ? fromEnv : generateSessionId();
|
|
2749
2796
|
return _sessionId;
|
|
2750
2797
|
}
|
|
2751
|
-
function setTranscriptPath(
|
|
2752
|
-
if (
|
|
2753
|
-
_transcriptPath = { path:
|
|
2798
|
+
function setTranscriptPath(path26, sessionId) {
|
|
2799
|
+
if (path26 === "") return;
|
|
2800
|
+
_transcriptPath = { path: path26, sessionId };
|
|
2754
2801
|
}
|
|
2755
2802
|
function getTranscriptPath2() {
|
|
2756
2803
|
if (_transcriptPath === null) return void 0;
|
|
@@ -2792,7 +2839,9 @@ function importSessionState(s) {
|
|
|
2792
2839
|
_curlDownloads = new Map(s.curlDownloads);
|
|
2793
2840
|
_curlDownloadsAtLoad = new Map(_curlDownloads);
|
|
2794
2841
|
_fileLineRanges = new Map(s.fileLineRanges ?? []);
|
|
2842
|
+
_fileLineRangesAtLoad = new Map(_fileLineRanges);
|
|
2795
2843
|
_fileServedOutputs = new Map(s.fileServedOutputs ?? []);
|
|
2844
|
+
_fileServedOutputsAtLoad = new Map(_fileServedOutputs);
|
|
2796
2845
|
_cliReads = new Set(s.cliReads ?? []);
|
|
2797
2846
|
_pendingLargeFileHints = new Map(s.pendingLargeFileHints ?? []);
|
|
2798
2847
|
_pendingLargeFileHintsAtLoad = new Map(_pendingLargeFileHints);
|
|
@@ -2816,7 +2865,9 @@ registerReset(() => {
|
|
|
2816
2865
|
_curlDownloads = /* @__PURE__ */ new Map();
|
|
2817
2866
|
_curlDownloadsAtLoad = /* @__PURE__ */ new Map();
|
|
2818
2867
|
_fileLineRanges = /* @__PURE__ */ new Map();
|
|
2868
|
+
_fileLineRangesAtLoad = /* @__PURE__ */ new Map();
|
|
2819
2869
|
_fileServedOutputs = /* @__PURE__ */ new Map();
|
|
2870
|
+
_fileServedOutputsAtLoad = /* @__PURE__ */ new Map();
|
|
2820
2871
|
_cliReads = /* @__PURE__ */ new Set();
|
|
2821
2872
|
_pendingLargeFileHints = /* @__PURE__ */ new Map();
|
|
2822
2873
|
_pendingLargeFileHintsAtLoad = /* @__PURE__ */ new Map();
|
|
@@ -2835,9 +2886,18 @@ registerReset(() => {
|
|
|
2835
2886
|
init_define_import_meta_env();
|
|
2836
2887
|
import { createHash } from "node:crypto";
|
|
2837
2888
|
import * as fs9 from "node:fs";
|
|
2838
|
-
import * as
|
|
2839
|
-
|
|
2889
|
+
import * as path9 from "node:path";
|
|
2890
|
+
|
|
2891
|
+
// src/sessions_dir.ts
|
|
2892
|
+
init_define_import_meta_env();
|
|
2893
|
+
import * as path8 from "node:path";
|
|
2840
2894
|
var SESSIONS_SUBDIR = "sessions";
|
|
2895
|
+
function sessionsDir() {
|
|
2896
|
+
return path8.join(tokenGoatHome(), SESSIONS_SUBDIR);
|
|
2897
|
+
}
|
|
2898
|
+
|
|
2899
|
+
// src/session_store.ts
|
|
2900
|
+
var MAX_FILES = 500;
|
|
2841
2901
|
var AGENT_SALT_MARKER = sanitizeIdForFilename(":agent:");
|
|
2842
2902
|
var AGENT_SALT_SEPARATOR = ":agent:";
|
|
2843
2903
|
var AGENT_DIGEST_CHARS = 12;
|
|
@@ -2860,10 +2920,10 @@ function sessionSidecarPath(sessionId, suffix) {
|
|
|
2860
2920
|
const safe = sessionFileStem(sessionId);
|
|
2861
2921
|
if (!safe) return null;
|
|
2862
2922
|
if (suffix.includes("/") || suffix.includes("\\") || suffix.includes("..")) return null;
|
|
2863
|
-
const dir =
|
|
2864
|
-
const candidate =
|
|
2923
|
+
const dir = sessionsDir();
|
|
2924
|
+
const candidate = path9.join(dir, `${safe}${suffix}`);
|
|
2865
2925
|
try {
|
|
2866
|
-
const rel =
|
|
2926
|
+
const rel = path9.relative(dir, candidate);
|
|
2867
2927
|
if (rel.startsWith("..")) return null;
|
|
2868
2928
|
} catch {
|
|
2869
2929
|
return null;
|
|
@@ -3108,10 +3168,14 @@ function mergeSessionState(disk, mem) {
|
|
|
3108
3168
|
byPath.set(key, prev ? mergeFileEntry(prev, e) : e);
|
|
3109
3169
|
}
|
|
3110
3170
|
const compactedAt = Math.max(disk.compactedAt ?? 0, mem.compactedAt ?? 0);
|
|
3111
|
-
const
|
|
3171
|
+
const rawDiskRanges = (disk.compactedAt ?? 0) === compactedAt ? disk.fileLineRanges ?? [] : [];
|
|
3112
3172
|
const memRanges = (mem.compactedAt ?? 0) === compactedAt ? mem.fileLineRanges ?? [] : [];
|
|
3113
|
-
const
|
|
3173
|
+
const rawDiskServed = (disk.compactedAt ?? 0) === compactedAt ? disk.fileServedOutputs ?? [] : [];
|
|
3114
3174
|
const memServed = (mem.compactedAt ?? 0) === compactedAt ? mem.fileServedOutputs ?? [] : [];
|
|
3175
|
+
const clearedRangeFiles = new Set(consumedFileLineRangeKeys());
|
|
3176
|
+
const clearedServedFiles = new Set(consumedFileServedOutputKeys());
|
|
3177
|
+
const diskRanges = rawDiskRanges.filter(([path26]) => !clearedRangeFiles.has(path26));
|
|
3178
|
+
const diskServed = rawDiskServed.filter(([path26]) => !clearedServedFiles.has(path26));
|
|
3115
3179
|
return {
|
|
3116
3180
|
files: Array.from(byPath.values()),
|
|
3117
3181
|
hintsShown: Array.from(/* @__PURE__ */ new Set([...disk.hintsShown, ...mem.hintsShown])),
|
|
@@ -3128,10 +3192,7 @@ function mergeSessionState(disk, mem) {
|
|
|
3128
3192
|
grepQueries: mergePairs(disk.grepQueries ?? [], mem.grepQueries ?? []),
|
|
3129
3193
|
globQueries: mergePairs(disk.globQueries ?? [], mem.globQueries ?? []),
|
|
3130
3194
|
outstandingAgentSpawns: mergeOutstandingAgentSpawns(disk.outstandingAgentSpawns ?? [], mem.outstandingAgentSpawns ?? []),
|
|
3131
|
-
// An accumulating collection, so union rather than pick a winner: two hook processes can
|
|
3132
|
-
// each see a screenshot the other never did. Disk first, then mem, so the order stays
|
|
3133
|
-
// oldest-to-newest and the cap evicts the oldest -- the same policy recordSeenImage applies
|
|
3134
|
-
// in memory, applied again here because a union of two capped lists can exceed the cap.
|
|
3195
|
+
// An accumulating collection, so union rather than pick a winner: two hook processes can each see a screenshot the other never did. Disk first, then mem, so the order stays oldest-to-newest and the cap evicts the oldest -- the same policy recordSeenImage applies in memory, applied again here because a union of two capped lists can exceed the cap.
|
|
3135
3196
|
seenImageHashes: mergeSeenImageHashes(disk.seenImageHashes ?? [], mem.seenImageHashes ?? []),
|
|
3136
3197
|
// Last-seen scalar, not an accumulating collection: prefer mem's value (this process's freshest observation) over disk's, since a newer write always supersedes an older one.
|
|
3137
3198
|
...mem.lastTabContextDigest !== void 0 ? { lastTabContextDigest: mem.lastTabContextDigest } : disk.lastTabContextDigest !== void 0 ? { lastTabContextDigest: disk.lastTabContextDigest } : {},
|
|
@@ -3165,13 +3226,13 @@ function listSiblingSessionStates(sessionId) {
|
|
|
3165
3226
|
if (!sessionId) return [];
|
|
3166
3227
|
const prefix = saltedStemPrefix(sessionId);
|
|
3167
3228
|
if (prefix === AGENT_SALT_MARKER) return [];
|
|
3168
|
-
const dir =
|
|
3229
|
+
const dir = sessionsDir();
|
|
3169
3230
|
const out = [];
|
|
3170
3231
|
try {
|
|
3171
3232
|
if (!fs9.existsSync(dir)) return out;
|
|
3172
3233
|
for (const file of fs9.readdirSync(dir)) {
|
|
3173
3234
|
if (!file.endsWith(".json") || !file.startsWith(prefix)) continue;
|
|
3174
|
-
const state = readDiskState(
|
|
3235
|
+
const state = readDiskState(path9.join(dir, file));
|
|
3175
3236
|
if (state !== null) out.push(state);
|
|
3176
3237
|
}
|
|
3177
3238
|
} catch {
|
|
@@ -3190,7 +3251,7 @@ function saveSessionState(sessionId) {
|
|
|
3190
3251
|
const p = sessionPath(sessionId);
|
|
3191
3252
|
if (!p) return;
|
|
3192
3253
|
try {
|
|
3193
|
-
const dir =
|
|
3254
|
+
const dir = path9.dirname(p);
|
|
3194
3255
|
if (!fs9.existsSync(dir)) ensureDirSync(dir);
|
|
3195
3256
|
const mem = exportSessionState();
|
|
3196
3257
|
const writeMerged = () => {
|
|
@@ -3215,7 +3276,6 @@ function saveSessionState(sessionId) {
|
|
|
3215
3276
|
init_define_import_meta_env();
|
|
3216
3277
|
import * as fs10 from "fs/promises";
|
|
3217
3278
|
import { resolve as resolve4 } from "path";
|
|
3218
|
-
import { homedir as homedir3 } from "os";
|
|
3219
3279
|
import { readdirSync as readdirSync3, readFileSync as readFileSync5, existsSync as existsSync7, statSync as statSync3, unlinkSync as unlinkSync2 } from "node:fs";
|
|
3220
3280
|
var COMPACT_END_MARKER = "<!-- COMPACT_END -->";
|
|
3221
3281
|
var SKILLS_OUTPUT_SUBDIR = "skills";
|
|
@@ -3234,7 +3294,7 @@ function skillOutputsDir() {
|
|
|
3234
3294
|
}
|
|
3235
3295
|
function skillsSourceDir() {
|
|
3236
3296
|
if (_skillsSourceDirOverride) return _skillsSourceDirOverride;
|
|
3237
|
-
return resolve4(
|
|
3297
|
+
return resolve4(claudeConfigDir(), "skills");
|
|
3238
3298
|
}
|
|
3239
3299
|
async function ensureSkillsDir() {
|
|
3240
3300
|
try {
|
|
@@ -3718,7 +3778,7 @@ async function getSkillFilePath(skillName2) {
|
|
|
3718
3778
|
}
|
|
3719
3779
|
}
|
|
3720
3780
|
async function resolvePluginSkillPath(pluginName, skillSlug) {
|
|
3721
|
-
const manifestPath = _pluginsManifestPathOverride ?? resolve4(
|
|
3781
|
+
const manifestPath = _pluginsManifestPathOverride ?? resolve4(claudeConfigDir(), "plugins", "installed_plugins.json");
|
|
3722
3782
|
let raw;
|
|
3723
3783
|
try {
|
|
3724
3784
|
raw = await fs10.readFile(manifestPath, "utf8");
|
|
@@ -3824,14 +3884,14 @@ function pruneSkillOutputs(maxCount = DEFAULT_MAX_COUNT, maxAgeMs = DEFAULT_MAX_
|
|
|
3824
3884
|
init_define_import_meta_env();
|
|
3825
3885
|
import { createHash as createHash2 } from "node:crypto";
|
|
3826
3886
|
import * as fs11 from "node:fs";
|
|
3827
|
-
import * as
|
|
3887
|
+
import * as path11 from "node:path";
|
|
3828
3888
|
|
|
3829
3889
|
// src/vscode_path_gate.ts
|
|
3830
3890
|
init_define_import_meta_env();
|
|
3831
|
-
import * as
|
|
3891
|
+
import * as path10 from "node:path";
|
|
3832
3892
|
function resolvesToUncPath(target, cwd) {
|
|
3833
3893
|
try {
|
|
3834
|
-
return isUncOrDevicePath(
|
|
3894
|
+
return isUncOrDevicePath(path10.resolve(cwd ?? process.cwd(), target));
|
|
3835
3895
|
} catch {
|
|
3836
3896
|
return false;
|
|
3837
3897
|
}
|
|
@@ -3839,8 +3899,8 @@ function resolvesToUncPath(target, cwd) {
|
|
|
3839
3899
|
function vscodePathAllowed(filePath, workspace) {
|
|
3840
3900
|
if (isUncOrDevicePath(filePath)) return false;
|
|
3841
3901
|
if (workspace === void 0 || isUncOrDevicePath(workspace)) return false;
|
|
3842
|
-
const resolvedRoot =
|
|
3843
|
-
const resolvedTarget =
|
|
3902
|
+
const resolvedRoot = path10.resolve(workspace);
|
|
3903
|
+
const resolvedTarget = path10.resolve(workspace, filePath);
|
|
3844
3904
|
const root = foldPathForContainment(resolvedRoot.replace(/\\/g, "/"));
|
|
3845
3905
|
const target = foldPathForContainment(resolvedTarget.replace(/\\/g, "/"));
|
|
3846
3906
|
if (target !== root && !target.startsWith(root.endsWith("/") ? root : root + "/")) return false;
|
|
@@ -3858,19 +3918,6 @@ function preToolPathDeclined(event, target) {
|
|
|
3858
3918
|
}
|
|
3859
3919
|
|
|
3860
3920
|
// src/image_shrink.ts
|
|
3861
|
-
var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
3862
|
-
".png",
|
|
3863
|
-
".jpg",
|
|
3864
|
-
".jpeg",
|
|
3865
|
-
".gif",
|
|
3866
|
-
".webp",
|
|
3867
|
-
".bmp",
|
|
3868
|
-
".tif",
|
|
3869
|
-
".tiff",
|
|
3870
|
-
".avif",
|
|
3871
|
-
".heic",
|
|
3872
|
-
".heif"
|
|
3873
|
-
]);
|
|
3874
3921
|
var DEFAULT_MAX_DIMENSION = 1568;
|
|
3875
3922
|
var VISION_PATCH_PX = 28;
|
|
3876
3923
|
var VISION_TIER_LIMITS = {
|
|
@@ -3942,9 +3989,6 @@ function formatShrinkSummary(result, subject) {
|
|
|
3942
3989
|
const dataUrl = `data:image/${result.format};base64,${result.data.toString("base64")}`;
|
|
3943
3990
|
return { summary, dataUrl };
|
|
3944
3991
|
}
|
|
3945
|
-
function isImagePath(p) {
|
|
3946
|
-
return IMAGE_EXTENSIONS.has(path9.extname(p).toLowerCase());
|
|
3947
|
-
}
|
|
3948
3992
|
var ImageDecodeError = class extends Error {
|
|
3949
3993
|
};
|
|
3950
3994
|
async function probeImageMeta(input) {
|
|
@@ -4054,7 +4098,7 @@ function statInfo(absPath) {
|
|
|
4054
4098
|
}
|
|
4055
4099
|
}
|
|
4056
4100
|
function imageShrinkCacheDir() {
|
|
4057
|
-
return
|
|
4101
|
+
return path11.join(tokenGoatHome(), "image_shrink_cache");
|
|
4058
4102
|
}
|
|
4059
4103
|
var SHRINK_ENGINE_REVISION = 3;
|
|
4060
4104
|
function shrinkCacheKeyForRevision(revision, originalPath, size, mtimeMs, quality) {
|
|
@@ -4090,7 +4134,7 @@ function findCachedShrink(originalPath, size, mtimeMs, quality) {
|
|
|
4090
4134
|
const format = SHRINK_CACHE_FORMAT.get(m[3] ?? "");
|
|
4091
4135
|
if (format === void 0) continue;
|
|
4092
4136
|
return {
|
|
4093
|
-
filePath:
|
|
4137
|
+
filePath: path11.join(dir, file),
|
|
4094
4138
|
format,
|
|
4095
4139
|
originalWidth: Number(m[1]),
|
|
4096
4140
|
originalHeight: Number(m[2])
|
|
@@ -4105,7 +4149,7 @@ function writeCachedShrink(originalPath, result, mtimeMs, quality) {
|
|
|
4105
4149
|
const key = shrinkCacheKey(originalPath, result.originalBytes, mtimeMs, quality);
|
|
4106
4150
|
const ext = SHRINK_CACHE_EXTENSION.get(result.format);
|
|
4107
4151
|
if (ext === void 0) return;
|
|
4108
|
-
atomicWriteBytes(
|
|
4152
|
+
atomicWriteBytes(path11.join(dir, `token-goat-shrink-${key}-${result.originalWidth}x${result.originalHeight}${ext}`), result.data);
|
|
4109
4153
|
} catch {
|
|
4110
4154
|
}
|
|
4111
4155
|
}
|
|
@@ -4121,7 +4165,7 @@ function pruneShrinkCache() {
|
|
|
4121
4165
|
const cutoff = now - DEFAULT_MAX_AGE_MS;
|
|
4122
4166
|
for (const file of fs11.readdirSync(dir)) {
|
|
4123
4167
|
if (!file.startsWith("token-goat-shrink-")) continue;
|
|
4124
|
-
const full =
|
|
4168
|
+
const full = path11.join(dir, file);
|
|
4125
4169
|
try {
|
|
4126
4170
|
const st = fs11.statSync(full);
|
|
4127
4171
|
if (st.mtimeMs < cutoff) fs11.unlinkSync(full);
|
|
@@ -4133,7 +4177,7 @@ function pruneShrinkCache() {
|
|
|
4133
4177
|
}
|
|
4134
4178
|
var HOST_MATERIALIZED_HARNESSES = /* @__PURE__ */ new Set(["copilot_cli", "pi", "opencode", "openclaw"]);
|
|
4135
4179
|
async function finalizeShrinkResult(result, filePath, event) {
|
|
4136
|
-
const basename11 = displaySafePath(
|
|
4180
|
+
const basename11 = displaySafePath(path11.basename(filePath));
|
|
4137
4181
|
const shrinkSaved = result.originalBytes - result.shrunkBytes;
|
|
4138
4182
|
const tier = loadConfig().image_shrink.vision_tier;
|
|
4139
4183
|
const shrinkTokens = visionTokensSaved(result.originalWidth, result.originalHeight, result.width, result.height, tier);
|
|
@@ -4151,7 +4195,7 @@ ${dataUrl2}`);
|
|
|
4151
4195
|
}
|
|
4152
4196
|
recordStat("image_shrink", shrinkSaved, shrinkTokens, void 0, basename11);
|
|
4153
4197
|
if (loadConfig().image_shrink.ocr_enabled) {
|
|
4154
|
-
const { ocrImage, isTextHeavy, formatOcrSummary } = await import("./token-goat-chunk-
|
|
4198
|
+
const { ocrImage, isTextHeavy, formatOcrSummary } = await import("./token-goat-chunk-62VJ47WN.mjs");
|
|
4155
4199
|
const ocr = await ocrImage(result.data);
|
|
4156
4200
|
if (ocr !== null && isTextHeavy(ocr, loadConfig().image_shrink.ocr_min_confidence)) {
|
|
4157
4201
|
const emitted = formatOcrSummary(ocr, basename11, result.originalBytes);
|
|
@@ -4232,10 +4276,34 @@ async function preReadImageHandler(event) {
|
|
|
4232
4276
|
}
|
|
4233
4277
|
registerHook("pre_tool_use", preReadImageHandler, { toolName: "Read" });
|
|
4234
4278
|
|
|
4279
|
+
// src/embed_backfill.ts
|
|
4280
|
+
init_define_import_meta_env();
|
|
4281
|
+
var BACKFILL_META_KEY = "unembeddable_chunks_pruned_version";
|
|
4282
|
+
function ensureLedger(db) {
|
|
4283
|
+
db.exec("CREATE TABLE IF NOT EXISTS index_migrations (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
|
|
4284
|
+
}
|
|
4285
|
+
function selectUnembeddableChunkFiles(db, maxChunksPerFile) {
|
|
4286
|
+
const rows = db.prepare("SELECT file_path AS filePath, COUNT(*) AS chunks FROM chunks GROUP BY file_path").all();
|
|
4287
|
+
return rows.filter((r) => isNonTextAsset(r.filePath) || maxChunksPerFile > 0 && r.chunks > maxChunksPerFile);
|
|
4288
|
+
}
|
|
4289
|
+
function pruneUnembeddableChunks(db, maxChunksPerFile, deleteForFile) {
|
|
4290
|
+
ensureLedger(db);
|
|
4291
|
+
const row = db.prepare("SELECT value FROM index_migrations WHERE key = ?").get(BACKFILL_META_KEY);
|
|
4292
|
+
if (row?.value === VERSION) return { files: 0, chunks: 0, ran: false };
|
|
4293
|
+
const targets = selectUnembeddableChunkFiles(db, maxChunksPerFile);
|
|
4294
|
+
let chunks = 0;
|
|
4295
|
+
for (const target of targets) {
|
|
4296
|
+
deleteForFile(db, target.filePath);
|
|
4297
|
+
chunks += target.chunks;
|
|
4298
|
+
}
|
|
4299
|
+
db.prepare("INSERT INTO index_migrations (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value").run(BACKFILL_META_KEY, VERSION);
|
|
4300
|
+
return { files: targets.length, chunks, ran: true };
|
|
4301
|
+
}
|
|
4302
|
+
|
|
4235
4303
|
// src/index_prune.ts
|
|
4236
4304
|
init_define_import_meta_env();
|
|
4237
4305
|
import * as fs12 from "node:fs";
|
|
4238
|
-
import * as
|
|
4306
|
+
import * as path12 from "node:path";
|
|
4239
4307
|
function removeFileFromIndex(db, filePath) {
|
|
4240
4308
|
const tx = db.transaction(() => {
|
|
4241
4309
|
deleteFileRows(db, filePath);
|
|
@@ -4367,7 +4435,7 @@ function pruneOrphanedChunks(dbPath = globalDbPath()) {
|
|
|
4367
4435
|
return removed;
|
|
4368
4436
|
}
|
|
4369
4437
|
function recordKnownRoot(filePath, dbPath = globalDbPath()) {
|
|
4370
|
-
const project = findProject(
|
|
4438
|
+
const project = findProject(path12.dirname(filePath));
|
|
4371
4439
|
if (project === null || isTooShallowToPrune(project.root)) return;
|
|
4372
4440
|
const db = getDb(dbPath);
|
|
4373
4441
|
db.prepare(
|
|
@@ -4425,7 +4493,7 @@ function sweepKnownRoots(dbPath = globalDbPath(), opts) {
|
|
|
4425
4493
|
}
|
|
4426
4494
|
var KNOWN_ROOT_RECORD_MIN_INTERVAL_MS = 60 * 60 * 1e3;
|
|
4427
4495
|
function knownRootRecordMarkerPath(dir, filePath) {
|
|
4428
|
-
return
|
|
4496
|
+
return path12.join(dir, `known-root-record-${shortFingerprint(path12.dirname(filePath))}.marker`);
|
|
4429
4497
|
}
|
|
4430
4498
|
var KNOWN_ROOT_MARKER_PREFIX = "known-root-record-";
|
|
4431
4499
|
function sweepExpiredKnownRootMarkers(dir = dataDir()) {
|
|
@@ -4434,7 +4502,7 @@ function sweepExpiredKnownRootMarkers(dir = dataDir()) {
|
|
|
4434
4502
|
const cutoff = Date.now() - KNOWN_ROOT_RECORD_MIN_INTERVAL_MS;
|
|
4435
4503
|
for (const file of fs12.readdirSync(dir)) {
|
|
4436
4504
|
if (!file.startsWith(KNOWN_ROOT_MARKER_PREFIX) || !file.endsWith(".marker")) continue;
|
|
4437
|
-
const full =
|
|
4505
|
+
const full = path12.join(dir, file);
|
|
4438
4506
|
try {
|
|
4439
4507
|
if (fs12.statSync(full).mtimeMs < cutoff) {
|
|
4440
4508
|
fs12.unlinkSync(full);
|
|
@@ -4461,14 +4529,47 @@ function recordKnownRootThrottled(filePath, dir = dataDir(), dbPath = globalDbPa
|
|
|
4461
4529
|
}
|
|
4462
4530
|
recordKnownRoot(filePath, dbPath);
|
|
4463
4531
|
}
|
|
4532
|
+
function findTopIndexedProjects(dbPath = globalDbPath(), limit = 3) {
|
|
4533
|
+
try {
|
|
4534
|
+
const db = getDb(dbPath);
|
|
4535
|
+
const hasRoots = db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='known_roots'").get();
|
|
4536
|
+
if (!hasRoots) return [];
|
|
4537
|
+
const rawRoots = db.prepare("SELECT root FROM known_roots").all().map((r) => r.root);
|
|
4538
|
+
const seen = /* @__PURE__ */ new Map();
|
|
4539
|
+
for (const r of rawRoots) {
|
|
4540
|
+
const norm = normalizePath(r).replace(/\/+$/, "");
|
|
4541
|
+
const lower = foldPath(norm);
|
|
4542
|
+
if (!seen.has(lower)) seen.set(lower, norm);
|
|
4543
|
+
}
|
|
4544
|
+
const results = [];
|
|
4545
|
+
for (const root of seen.values()) {
|
|
4546
|
+
const count = db.prepare("SELECT count(*) as c FROM files WHERE path LIKE ? || '/%' OR path = ?").get(root, root).c;
|
|
4547
|
+
if (count > 0) results.push({ root, fileCount: count });
|
|
4548
|
+
}
|
|
4549
|
+
results.sort((a, b) => b.fileCount - a.fileCount);
|
|
4550
|
+
const unique = [];
|
|
4551
|
+
for (const res of results) {
|
|
4552
|
+
const isDuplicate = unique.some((u) => {
|
|
4553
|
+
const r1 = foldPath(u.root);
|
|
4554
|
+
const r2 = foldPath(res.root);
|
|
4555
|
+
return r1.startsWith(r2 + "/") || r2.startsWith(r1 + "/");
|
|
4556
|
+
});
|
|
4557
|
+
if (!isDuplicate) unique.push(res);
|
|
4558
|
+
if (unique.length >= limit) break;
|
|
4559
|
+
}
|
|
4560
|
+
return unique.slice(0, limit);
|
|
4561
|
+
} catch {
|
|
4562
|
+
return [];
|
|
4563
|
+
}
|
|
4564
|
+
}
|
|
4464
4565
|
|
|
4465
4566
|
// src/process_priority.ts
|
|
4466
4567
|
init_define_import_meta_env();
|
|
4467
|
-
import * as
|
|
4568
|
+
import * as os5 from "node:os";
|
|
4468
4569
|
var PRIORITY_BY_NAME = {
|
|
4469
|
-
normal:
|
|
4470
|
-
below_normal:
|
|
4471
|
-
low:
|
|
4570
|
+
normal: os5.constants.priority.PRIORITY_NORMAL,
|
|
4571
|
+
below_normal: os5.constants.priority.PRIORITY_BELOW_NORMAL,
|
|
4572
|
+
low: os5.constants.priority.PRIORITY_LOW
|
|
4472
4573
|
};
|
|
4473
4574
|
var DEFAULT_PRIORITY_NAME = "below_normal";
|
|
4474
4575
|
function resolveWorkerPriority(name) {
|
|
@@ -4477,7 +4578,7 @@ function resolveWorkerPriority(name) {
|
|
|
4477
4578
|
}
|
|
4478
4579
|
function applyIndexingPriority() {
|
|
4479
4580
|
try {
|
|
4480
|
-
|
|
4581
|
+
os5.setPriority(0, resolveWorkerPriority(loadConfig().worker.priority));
|
|
4481
4582
|
return true;
|
|
4482
4583
|
} catch {
|
|
4483
4584
|
return false;
|
|
@@ -4488,13 +4589,13 @@ function applyIndexingPriority() {
|
|
|
4488
4589
|
init_define_import_meta_env();
|
|
4489
4590
|
import { spawn } from "node:child_process";
|
|
4490
4591
|
import * as fs15 from "node:fs";
|
|
4491
|
-
import * as
|
|
4592
|
+
import * as path15 from "node:path";
|
|
4492
4593
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
4493
4594
|
|
|
4494
4595
|
// src/index_reclaim.ts
|
|
4495
4596
|
init_define_import_meta_env();
|
|
4496
4597
|
import * as fs13 from "node:fs";
|
|
4497
|
-
import * as
|
|
4598
|
+
import * as path13 from "node:path";
|
|
4498
4599
|
var DERIVED_TABLES = ["chunk_vectors", "chunks", "refs", "symbols", "files"];
|
|
4499
4600
|
function indexSizeBytes(dbPath) {
|
|
4500
4601
|
let total = 0;
|
|
@@ -4578,7 +4679,7 @@ function mb(bytes) {
|
|
|
4578
4679
|
}
|
|
4579
4680
|
function cmdReclaimIndex(opts) {
|
|
4580
4681
|
const dbPath = opts.dbPath ?? globalDbPath();
|
|
4581
|
-
if (opts.force !== true && isWorkerRunning(
|
|
4682
|
+
if (opts.force !== true && isWorkerRunning(path13.dirname(dbPath))) {
|
|
4582
4683
|
throw new Error(
|
|
4583
4684
|
"reclaim-index: the worker daemon is running and writing to this index. Stop it first with 'token-goat worker stop', then re-run. Pass --force to proceed anyway (results may be inaccurate if the worker is really live)."
|
|
4584
4685
|
);
|
|
@@ -4612,7 +4713,7 @@ function cmdReclaimIndex(opts) {
|
|
|
4612
4713
|
}
|
|
4613
4714
|
if (result.checkpointBusy) {
|
|
4614
4715
|
process.stdout.write(
|
|
4615
|
-
` note: a concurrent reader blocked WAL truncation, so some space is still held in ${
|
|
4716
|
+
` note: a concurrent reader blocked WAL truncation, so some space is still held in ${path13.basename(dbPath)}-wal and the figures above count the main file only. The next checkpoint releases it
|
|
4616
4717
|
`
|
|
4617
4718
|
);
|
|
4618
4719
|
}
|
|
@@ -4621,7 +4722,7 @@ function cmdReclaimIndex(opts) {
|
|
|
4621
4722
|
// src/snapshots.ts
|
|
4622
4723
|
init_define_import_meta_env();
|
|
4623
4724
|
import * as fs14 from "node:fs";
|
|
4624
|
-
import * as
|
|
4725
|
+
import * as path14 from "node:path";
|
|
4625
4726
|
var MAX_SNAPSHOTS_PER_SESSION = 150;
|
|
4626
4727
|
var MAX_SNAPSHOT_BYTES = 256 * 1024;
|
|
4627
4728
|
var SNAPSHOT_TRUNCATE_BYTES = 50 * 1024;
|
|
@@ -4631,10 +4732,10 @@ var VALID_KINDS = /* @__PURE__ */ new Set([KIND_READ, KIND_PREDICTIVE]);
|
|
|
4631
4732
|
function sessionDir(sessionId) {
|
|
4632
4733
|
if (!sessionId) return null;
|
|
4633
4734
|
const safe = sanitizeIdForFilename(sessionId, 64, "anon");
|
|
4634
|
-
const base =
|
|
4635
|
-
const candidate =
|
|
4735
|
+
const base = path14.join(tokenGoatHome(), "session_snapshots");
|
|
4736
|
+
const candidate = path14.join(base, safe);
|
|
4636
4737
|
try {
|
|
4637
|
-
const rel =
|
|
4738
|
+
const rel = path14.relative(base, candidate);
|
|
4638
4739
|
if (rel.startsWith("..")) return null;
|
|
4639
4740
|
} catch {
|
|
4640
4741
|
return null;
|
|
@@ -4647,7 +4748,7 @@ function pathKey(filePath) {
|
|
|
4647
4748
|
function snapshot_path(sessionId, filePath) {
|
|
4648
4749
|
const d = sessionDir(sessionId);
|
|
4649
4750
|
if (!d) return null;
|
|
4650
|
-
return
|
|
4751
|
+
return path14.join(d, `${pathKey(filePath)}.bin`);
|
|
4651
4752
|
}
|
|
4652
4753
|
function kindSidecarPath(snapshotPath) {
|
|
4653
4754
|
return snapshotPath + ".kind";
|
|
@@ -4655,7 +4756,7 @@ function kindSidecarPath(snapshotPath) {
|
|
|
4655
4756
|
function writeSnapshotKind(sidecarPath, kind) {
|
|
4656
4757
|
try {
|
|
4657
4758
|
const safeKind = VALID_KINDS.has(kind) ? kind : KIND_READ;
|
|
4658
|
-
const dir =
|
|
4759
|
+
const dir = path14.dirname(sidecarPath);
|
|
4659
4760
|
if (!fs14.existsSync(dir)) {
|
|
4660
4761
|
ensureDirSync(dir);
|
|
4661
4762
|
}
|
|
@@ -4670,7 +4771,7 @@ function evictOldest(d, maxCount) {
|
|
|
4670
4771
|
const entries = [];
|
|
4671
4772
|
const files = fs14.readdirSync(d);
|
|
4672
4773
|
for (const file of files) {
|
|
4673
|
-
const fullPath =
|
|
4774
|
+
const fullPath = path14.join(d, file);
|
|
4674
4775
|
if (!file.endsWith(".bin")) continue;
|
|
4675
4776
|
try {
|
|
4676
4777
|
const stat2 = fs14.statSync(fullPath);
|
|
@@ -4731,7 +4832,7 @@ function store(sessionId, filePath, content, opts = {}) {
|
|
|
4731
4832
|
} catch {
|
|
4732
4833
|
}
|
|
4733
4834
|
}
|
|
4734
|
-
const dir =
|
|
4835
|
+
const dir = path14.dirname(p);
|
|
4735
4836
|
if (!fs14.existsSync(dir)) {
|
|
4736
4837
|
ensureDirSync(dir);
|
|
4737
4838
|
}
|
|
@@ -4782,14 +4883,14 @@ function removeEligibleSnapshotFile(fullPath, file, cutoff) {
|
|
|
4782
4883
|
return file.endsWith(".bin");
|
|
4783
4884
|
}
|
|
4784
4885
|
function cleanup_stale(maxAgeHours = 24) {
|
|
4785
|
-
const base =
|
|
4886
|
+
const base = path14.join(tokenGoatHome(), "session_snapshots");
|
|
4786
4887
|
if (!fs14.existsSync(base)) return 0;
|
|
4787
4888
|
const cutoff = Date.now() - maxAgeHours * 3600 * 1e3;
|
|
4788
4889
|
let removed = 0;
|
|
4789
4890
|
try {
|
|
4790
4891
|
const sessionDirs = fs14.readdirSync(base);
|
|
4791
4892
|
for (const sessionDir2 of sessionDirs) {
|
|
4792
|
-
const sessionPath2 =
|
|
4893
|
+
const sessionPath2 = path14.join(base, sessionDir2);
|
|
4793
4894
|
try {
|
|
4794
4895
|
const stat2 = fs14.statSync(sessionPath2);
|
|
4795
4896
|
if (!stat2.isDirectory()) continue;
|
|
@@ -4799,7 +4900,7 @@ function cleanup_stale(maxAgeHours = 24) {
|
|
|
4799
4900
|
try {
|
|
4800
4901
|
const files = fs14.readdirSync(sessionPath2);
|
|
4801
4902
|
for (const file of files) {
|
|
4802
|
-
const fullPath =
|
|
4903
|
+
const fullPath = path14.join(sessionPath2, file);
|
|
4803
4904
|
try {
|
|
4804
4905
|
if (removeEligibleSnapshotFile(fullPath, file, cutoff)) removed++;
|
|
4805
4906
|
} catch {
|
|
@@ -4848,15 +4949,15 @@ function drainingSnapshotStamp(file, content) {
|
|
|
4848
4949
|
var DRAINING_READ_ATTEMPTS = 5;
|
|
4849
4950
|
var DRAINING_READ_RETRY_DELAY_MS = 50;
|
|
4850
4951
|
function listDrainingFiles(queuePath) {
|
|
4851
|
-
const dir =
|
|
4852
|
-
const base = `${
|
|
4952
|
+
const dir = path15.dirname(queuePath);
|
|
4953
|
+
const base = `${path15.basename(queuePath)}.draining`;
|
|
4853
4954
|
let entries;
|
|
4854
4955
|
try {
|
|
4855
4956
|
entries = fs15.readdirSync(dir);
|
|
4856
4957
|
} catch {
|
|
4857
4958
|
return [];
|
|
4858
4959
|
}
|
|
4859
|
-
return entries.filter((name) => name === base || name.startsWith(`${base}.alt-`) && !name.includes(".corrupt-")).sort().map((name) =>
|
|
4960
|
+
return entries.filter((name) => name === base || name.startsWith(`${base}.alt-`) && !name.includes(".corrupt-")).sort().map((name) => path15.join(dir, name));
|
|
4860
4961
|
}
|
|
4861
4962
|
var MAX_TRANSIENT_RETRIES = 5;
|
|
4862
4963
|
function bumpRetryCount(dbPath, absPath) {
|
|
@@ -4884,16 +4985,16 @@ function clearRetryCount(dbPath, absPath) {
|
|
|
4884
4985
|
}
|
|
4885
4986
|
}
|
|
4886
4987
|
function dirtyQueuePathFor(dir) {
|
|
4887
|
-
return
|
|
4988
|
+
return path15.join(dir, "queue", "dirty.txt");
|
|
4888
4989
|
}
|
|
4889
4990
|
function drainHeartbeatPathFor(dir) {
|
|
4890
|
-
return
|
|
4991
|
+
return path15.join(dir, "queue", "drain-heartbeat");
|
|
4891
4992
|
}
|
|
4892
4993
|
function writeDrainHeartbeat(dir, force = false) {
|
|
4893
4994
|
const now = Date.now();
|
|
4894
4995
|
if (!force && now - (heartbeatWriteTimes.get(dir) ?? 0) < WORKER_HEARTBEAT_REFRESH_MS) return;
|
|
4895
4996
|
try {
|
|
4896
|
-
ensureDirSync(
|
|
4997
|
+
ensureDirSync(path15.dirname(drainHeartbeatPathFor(dir)));
|
|
4897
4998
|
fs15.writeFileSync(drainHeartbeatPathFor(dir), `${process.pid}
|
|
4898
4999
|
`);
|
|
4899
5000
|
heartbeatWriteTimes.set(dir, now);
|
|
@@ -4945,10 +5046,10 @@ function parseDirtyQueueLines(raw) {
|
|
|
4945
5046
|
return out;
|
|
4946
5047
|
}
|
|
4947
5048
|
function workerPidPath(dir = dataDir()) {
|
|
4948
|
-
return
|
|
5049
|
+
return path15.join(dir, "worker.pid");
|
|
4949
5050
|
}
|
|
4950
5051
|
function workerStampPath(dir = dataDir()) {
|
|
4951
|
-
return
|
|
5052
|
+
return path15.join(dir, "worker.stamp");
|
|
4952
5053
|
}
|
|
4953
5054
|
function getDirtyPathsFor(dir) {
|
|
4954
5055
|
let raw;
|
|
@@ -4960,7 +5061,7 @@ function getDirtyPathsFor(dir) {
|
|
|
4960
5061
|
return parseDirtyQueueLines(raw);
|
|
4961
5062
|
}
|
|
4962
5063
|
function workerErrorLogPath(dir) {
|
|
4963
|
-
return
|
|
5064
|
+
return path15.join(dir, "worker-errors.log");
|
|
4964
5065
|
}
|
|
4965
5066
|
var WORKER_ERROR_LOG_MAX_BYTES = 5 * 1024 * 1024;
|
|
4966
5067
|
var CORRUPT_QUARANTINE_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
@@ -4978,11 +5079,11 @@ function cleanupWorkerStateFiles(dir) {
|
|
|
4978
5079
|
} catch {
|
|
4979
5080
|
}
|
|
4980
5081
|
try {
|
|
4981
|
-
const queueDir =
|
|
5082
|
+
const queueDir = path15.dirname(dirtyQueuePathFor(dir));
|
|
4982
5083
|
const cutoff = Date.now() - CORRUPT_QUARANTINE_MAX_AGE_MS;
|
|
4983
5084
|
for (const file of fs15.readdirSync(queueDir)) {
|
|
4984
5085
|
if (!file.includes(".corrupt-")) continue;
|
|
4985
|
-
const full =
|
|
5086
|
+
const full = path15.join(queueDir, file);
|
|
4986
5087
|
try {
|
|
4987
5088
|
if (fs15.statSync(full).mtimeMs < cutoff) fs15.unlinkSync(full);
|
|
4988
5089
|
} catch {
|
|
@@ -5018,7 +5119,7 @@ registerReset(() => {
|
|
|
5018
5119
|
function embedFileSerialized(absPath, dbPath, sha) {
|
|
5019
5120
|
const key = foldPath(absPath);
|
|
5020
5121
|
const prior = inFlightEmbeddings.get(key);
|
|
5021
|
-
const dir =
|
|
5122
|
+
const dir = path15.dirname(dbPath);
|
|
5022
5123
|
const onEmbedError = (err) => {
|
|
5023
5124
|
const message = extractErrorMessage(err);
|
|
5024
5125
|
appendWorkerErrorLog(dir, `${(/* @__PURE__ */ new Date()).toISOString()} indexFileEmbeddings failed for ${absPath}: ${message}
|
|
@@ -5072,7 +5173,7 @@ function logTransientReadFailure(dir, absPath) {
|
|
|
5072
5173
|
);
|
|
5073
5174
|
}
|
|
5074
5175
|
function bumpAndCheckRetry(dir, absPath) {
|
|
5075
|
-
const dbPath =
|
|
5176
|
+
const dbPath = path15.join(dir, "global.db");
|
|
5076
5177
|
const attempts = bumpRetryCount(dbPath, absPath);
|
|
5077
5178
|
if (attempts > MAX_TRANSIENT_RETRIES) {
|
|
5078
5179
|
if (attempts === MAX_TRANSIENT_RETRIES + 1) {
|
|
@@ -5089,7 +5190,7 @@ function bumpAndCheckRetry(dir, absPath) {
|
|
|
5089
5190
|
function appendToDirtyQueue(dir, absPath) {
|
|
5090
5191
|
const queuePath = dirtyQueuePathFor(dir);
|
|
5091
5192
|
try {
|
|
5092
|
-
ensureDirSync(
|
|
5193
|
+
ensureDirSync(path15.dirname(queuePath));
|
|
5093
5194
|
let leadingNewline = "";
|
|
5094
5195
|
try {
|
|
5095
5196
|
const existing = fs15.readFileSync(queuePath, "utf8");
|
|
@@ -5105,7 +5206,7 @@ function requeueDirtyPath(dir, absPath) {
|
|
|
5105
5206
|
if (bumpAndCheckRetry(dir, absPath)) appendToDirtyQueue(dir, absPath);
|
|
5106
5207
|
}
|
|
5107
5208
|
function makeIndexer(dbPath) {
|
|
5108
|
-
const dir =
|
|
5209
|
+
const dir = path15.dirname(dbPath);
|
|
5109
5210
|
return (absPath, sha) => {
|
|
5110
5211
|
try {
|
|
5111
5212
|
const ixCfgForSkip = loadConfig().indexing;
|
|
@@ -5115,22 +5216,29 @@ function makeIndexer(dbPath) {
|
|
|
5115
5216
|
}
|
|
5116
5217
|
const embeddingsEnabled = loadConfig().indexing?.embeddings_enabled ?? true;
|
|
5117
5218
|
const depsAvailable = embeddingsEnabled && embeddingsDepsAvailable(getDb(dbPath));
|
|
5118
|
-
if (depsAvailable)
|
|
5219
|
+
if (depsAvailable) {
|
|
5220
|
+
ensureEmbeddingProvenance(getDb(dbPath));
|
|
5221
|
+
pruneUnembeddableChunks(getDb(dbPath), loadConfig().indexing?.max_chunks_per_file ?? 0, deleteFileEmbeddings);
|
|
5222
|
+
}
|
|
5119
5223
|
const entry = getFileEntry(absPath, dbPath);
|
|
5120
|
-
const
|
|
5224
|
+
const spellingStale = entry !== null && indexedPathSpellingIsStale(entry.filePath, absPath);
|
|
5225
|
+
const parseUnchanged = entry?.sha === sha && entry.parserSha === parserFingerprintForLanguage(entry.language) && !spellingStale;
|
|
5121
5226
|
if (!parseUnchanged) {
|
|
5122
5227
|
indexFileSync(absPath, dbPath);
|
|
5123
5228
|
}
|
|
5124
|
-
const
|
|
5125
|
-
|
|
5229
|
+
const embedEntry = parseUnchanged ? entry : getFileEntry(absPath, dbPath);
|
|
5230
|
+
const embedUnchanged = !spellingStale && isEmbedFresh(
|
|
5231
|
+
embedEntry?.embedSha,
|
|
5126
5232
|
sha,
|
|
5127
5233
|
embeddingsEnabled,
|
|
5128
5234
|
depsAvailable,
|
|
5129
5235
|
// Same partial-config defensiveness as embeddingsEnabled above: 0 matches no stamped oversize marker (config floors this key at 1), so a mocked config re-examines the file rather than assuming it current. See isEmbedFresh.
|
|
5130
|
-
loadConfig().indexing?.large_file_symbol_only_kb ?? 0
|
|
5236
|
+
loadConfig().indexing?.large_file_symbol_only_kb ?? 0,
|
|
5237
|
+
// Same reasoning and same 0 fallback for the chunk-count marker.
|
|
5238
|
+
loadConfig().indexing?.max_chunks_per_file ?? 0
|
|
5131
5239
|
);
|
|
5132
5240
|
if (embedUnchanged) {
|
|
5133
|
-
return false;
|
|
5241
|
+
return parseUnchanged ? false : void 0;
|
|
5134
5242
|
}
|
|
5135
5243
|
return embedFileSerialized(absPath, dbPath, sha);
|
|
5136
5244
|
} catch (err) {
|
|
@@ -5155,7 +5263,7 @@ function processDirtyBatch(paths, index = makeIndexer(globalDbPath()), remove =
|
|
|
5155
5263
|
if (fileIsAbsent(p)) {
|
|
5156
5264
|
try {
|
|
5157
5265
|
remove(p);
|
|
5158
|
-
clearRetryCount(
|
|
5266
|
+
clearRetryCount(path15.join(dir, "global.db"), p);
|
|
5159
5267
|
} catch (err) {
|
|
5160
5268
|
appendWorkerErrorLog(dir, `${(/* @__PURE__ */ new Date()).toISOString()} removeFileFromIndex failed for ${p}: ${extractErrorMessage(err)}
|
|
5161
5269
|
`);
|
|
@@ -5170,7 +5278,7 @@ function processDirtyBatch(paths, index = makeIndexer(globalDbPath()), remove =
|
|
|
5170
5278
|
continue;
|
|
5171
5279
|
}
|
|
5172
5280
|
try {
|
|
5173
|
-
const dirname16 =
|
|
5281
|
+
const dirname16 = path15.dirname(p);
|
|
5174
5282
|
let root;
|
|
5175
5283
|
if (projectRootCache.has(dirname16)) {
|
|
5176
5284
|
root = projectRootCache.get(dirname16) ?? null;
|
|
@@ -5187,7 +5295,7 @@ function processDirtyBatch(paths, index = makeIndexer(globalDbPath()), remove =
|
|
|
5187
5295
|
requeue(dir, p);
|
|
5188
5296
|
continue;
|
|
5189
5297
|
}
|
|
5190
|
-
clearRetryCount(
|
|
5298
|
+
clearRetryCount(path15.join(dir, "global.db"), p);
|
|
5191
5299
|
if (result !== false) indexed += 1;
|
|
5192
5300
|
}
|
|
5193
5301
|
return indexed;
|
|
@@ -5201,7 +5309,7 @@ function sleepSyncMs(ms) {
|
|
|
5201
5309
|
function drainOnce(dir, index, remove) {
|
|
5202
5310
|
const queuePath = dirtyQueuePathFor(dir);
|
|
5203
5311
|
const draining = `${queuePath}.draining`;
|
|
5204
|
-
const dbPath =
|
|
5312
|
+
const dbPath = path15.join(dir, "global.db");
|
|
5205
5313
|
const indexFn = index ?? makeIndexer(dbPath);
|
|
5206
5314
|
const removeFn = remove ?? makeRemover(dbPath);
|
|
5207
5315
|
let processed = 0;
|
|
@@ -5327,7 +5435,7 @@ function isWorkerRunning(dir = dataDir()) {
|
|
|
5327
5435
|
return pidAlive(pid) && hasFreshWorkerHeartbeat(dir, pid);
|
|
5328
5436
|
}
|
|
5329
5437
|
function workerHealthCheckMarkerPath(dir) {
|
|
5330
|
-
return
|
|
5438
|
+
return path15.join(dir, "worker-healthcheck.marker");
|
|
5331
5439
|
}
|
|
5332
5440
|
var WORKER_HEALTHCHECK_MIN_INTERVAL_MS = 5 * 60 * 1e3;
|
|
5333
5441
|
function ensureWorkerAlive(dir = dataDir()) {
|
|
@@ -5423,7 +5531,7 @@ function claimWorkerPidFile(dir, pid) {
|
|
|
5423
5531
|
}
|
|
5424
5532
|
function daemonEntryScript() {
|
|
5425
5533
|
const self = fileURLToPath2(import.meta.url);
|
|
5426
|
-
const launcher =
|
|
5534
|
+
const launcher = path15.join(path15.dirname(self), "token-goat.mjs");
|
|
5427
5535
|
try {
|
|
5428
5536
|
if (fs15.existsSync(launcher)) return launcher;
|
|
5429
5537
|
} catch {
|
|
@@ -5541,7 +5649,7 @@ async function runWorkerLoop(dir, pollIntervalMs, shouldStop = () => false) {
|
|
|
5541
5649
|
}
|
|
5542
5650
|
if (Date.now() - lastKnownRootsSweepMs >= KNOWN_ROOTS_SWEEP_INTERVAL_MS) {
|
|
5543
5651
|
try {
|
|
5544
|
-
const result = sweepKnownRoots(
|
|
5652
|
+
const result = sweepKnownRoots(path15.join(dir, "global.db"));
|
|
5545
5653
|
if (result.flaggedRoots.length > 0) {
|
|
5546
5654
|
appendWorkerErrorLog(
|
|
5547
5655
|
dir,
|
|
@@ -5556,7 +5664,7 @@ async function runWorkerLoop(dir, pollIntervalMs, shouldStop = () => false) {
|
|
|
5556
5664
|
const now = Date.now();
|
|
5557
5665
|
if (now - lastIdleVacuumMs >= IDLE_VACUUM_INTERVAL_MS && now - lastActiveMs >= IDLE_VACUUM_COOLDOWN_MS) {
|
|
5558
5666
|
try {
|
|
5559
|
-
const dbPath =
|
|
5667
|
+
const dbPath = path15.join(dir, "global.db");
|
|
5560
5668
|
if (fs15.existsSync(dbPath)) {
|
|
5561
5669
|
const res = reclaimIndex(dbPath, { rebuild: false });
|
|
5562
5670
|
if (!res.vacuumDeferred) {
|
|
@@ -5602,30 +5710,59 @@ function runDetachedWorkerDaemon() {
|
|
|
5602
5710
|
// src/hooks_index.ts
|
|
5603
5711
|
init_define_import_meta_env();
|
|
5604
5712
|
import * as fs16 from "node:fs";
|
|
5605
|
-
import * as
|
|
5713
|
+
import * as path16 from "node:path";
|
|
5606
5714
|
function dirtyQueuePath() {
|
|
5607
|
-
return
|
|
5715
|
+
return path16.join(dataDir(), "queue", "dirty.txt");
|
|
5608
5716
|
}
|
|
5609
|
-
function
|
|
5717
|
+
function dirtyQueueLeadingNewline(queuePath) {
|
|
5718
|
+
let fd;
|
|
5719
|
+
try {
|
|
5720
|
+
const size = fs16.statSync(queuePath).size;
|
|
5721
|
+
if (size === 0) return "";
|
|
5722
|
+
fd = fs16.openSync(queuePath, "r");
|
|
5723
|
+
const tail = Buffer.allocUnsafe(1);
|
|
5724
|
+
fs16.readSync(fd, tail, 0, 1, size - 1);
|
|
5725
|
+
return tail[0] === 10 ? "" : "\n";
|
|
5726
|
+
} catch {
|
|
5727
|
+
return "";
|
|
5728
|
+
} finally {
|
|
5729
|
+
if (fd !== void 0) {
|
|
5730
|
+
try {
|
|
5731
|
+
fs16.closeSync(fd);
|
|
5732
|
+
} catch {
|
|
5733
|
+
}
|
|
5734
|
+
}
|
|
5735
|
+
}
|
|
5736
|
+
}
|
|
5737
|
+
function appendDirtyPaths(normalizedPaths) {
|
|
5738
|
+
if (normalizedPaths.length === 0) return;
|
|
5610
5739
|
const queuePath = dirtyQueuePath();
|
|
5611
|
-
const dir =
|
|
5740
|
+
const dir = path16.dirname(queuePath);
|
|
5612
5741
|
try {
|
|
5613
5742
|
ensureDirSync(dir);
|
|
5614
5743
|
} catch (e) {
|
|
5615
5744
|
if (e.code !== "EEXIST" || !fs16.existsSync(dir)) throw e;
|
|
5616
5745
|
}
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
fs16.appendFileSync(queuePath, `${leadingNewline}${encodeDirtyQueueLine(normalizedPath)}
|
|
5624
|
-
`);
|
|
5746
|
+
const body = normalizedPaths.map((p) => `${encodeDirtyQueueLine(p)}
|
|
5747
|
+
`).join("");
|
|
5748
|
+
fs16.appendFileSync(queuePath, `${dirtyQueueLeadingNewline(queuePath)}${body}`);
|
|
5749
|
+
}
|
|
5750
|
+
function appendDirtyPath(normalizedPath) {
|
|
5751
|
+
appendDirtyPaths([normalizedPath]);
|
|
5625
5752
|
}
|
|
5626
5753
|
function enqueueDirtyPathSafe(filePath, opts) {
|
|
5754
|
+
enqueueDirtyPathsSafe([filePath], opts);
|
|
5755
|
+
}
|
|
5756
|
+
function enqueueDirtyPathsSafe(filePaths, opts) {
|
|
5627
5757
|
try {
|
|
5628
|
-
|
|
5758
|
+
const resolved = [];
|
|
5759
|
+
for (const filePath of filePaths) {
|
|
5760
|
+
const r = opts?.alreadyResolved === true ? filePath : resolveIndexPath(filePath);
|
|
5761
|
+
if (isUnderSystemTemp(r)) continue;
|
|
5762
|
+
resolved.push(r);
|
|
5763
|
+
}
|
|
5764
|
+
if (resolved.length === 0) return;
|
|
5765
|
+
appendDirtyPaths(resolved);
|
|
5629
5766
|
} catch {
|
|
5630
5767
|
return;
|
|
5631
5768
|
}
|
|
@@ -5647,9 +5784,9 @@ function getDirtyPaths() {
|
|
|
5647
5784
|
function preCompactIndexHandler(_event) {
|
|
5648
5785
|
const paths = getDirtyPaths();
|
|
5649
5786
|
if (paths.length > 0) {
|
|
5650
|
-
const sidecar =
|
|
5787
|
+
const sidecar = path16.join(dataDir(), "queue", "pending.txt");
|
|
5651
5788
|
try {
|
|
5652
|
-
ensureDirSync(
|
|
5789
|
+
ensureDirSync(path16.dirname(sidecar));
|
|
5653
5790
|
atomicWriteBytes(sidecar, Buffer.from(`${paths.join("\n")}
|
|
5654
5791
|
`, "utf8"));
|
|
5655
5792
|
} catch {
|
|
@@ -5662,7 +5799,7 @@ registerHook("pre_compact", preCompactIndexHandler, { advisory: true });
|
|
|
5662
5799
|
// src/hooks_read.ts
|
|
5663
5800
|
init_define_import_meta_env();
|
|
5664
5801
|
import * as fs23 from "node:fs";
|
|
5665
|
-
import * as
|
|
5802
|
+
import * as path21 from "node:path";
|
|
5666
5803
|
|
|
5667
5804
|
// src/hint_stats.ts
|
|
5668
5805
|
init_define_import_meta_env();
|
|
@@ -5688,6 +5825,13 @@ var KNOWN_CORRELATOR_PLACEHOLDERS = /* @__PURE__ */ new Set([
|
|
|
5688
5825
|
"table_name",
|
|
5689
5826
|
"name"
|
|
5690
5827
|
]);
|
|
5828
|
+
function joinCorrelators(correlators) {
|
|
5829
|
+
const kept = correlators.filter((c) => c !== "" && !c.includes("\n"));
|
|
5830
|
+
return kept.length === 0 ? null : [...new Set(kept)].join("\n");
|
|
5831
|
+
}
|
|
5832
|
+
function splitCorrelators(correlator) {
|
|
5833
|
+
return correlator.split("\n").filter((c) => c !== "");
|
|
5834
|
+
}
|
|
5691
5835
|
function extractPathCorrelator(text) {
|
|
5692
5836
|
const m = /(?:[A-Za-z]:[\\/]|\.{1,2}\/|\/)[^\s"'`]+/.exec(text);
|
|
5693
5837
|
if (m === null) return null;
|
|
@@ -5748,12 +5892,15 @@ function resetSuppressionStreak(category) {
|
|
|
5748
5892
|
}
|
|
5749
5893
|
function applyHintTracking(event, output, classify) {
|
|
5750
5894
|
if (output.hookType !== "context") return output;
|
|
5751
|
-
const
|
|
5895
|
+
const classified = classify(output.context);
|
|
5896
|
+
const { category } = classified;
|
|
5897
|
+
const correlator = output.correlators === void 0 ? classified.correlator : joinCorrelators(output.correlators);
|
|
5752
5898
|
const compensateSelfResolve = event.eventName === "pre_tool_use";
|
|
5753
5899
|
if (shouldSuppress(category, event.sessionId)) {
|
|
5754
5900
|
const occasion = bumpSuppressionStreak(category);
|
|
5755
5901
|
const thresholds = loadConfig().hints.backoff_thresholds;
|
|
5756
5902
|
if (!isProbeOccasion(occasion, thresholds)) {
|
|
5903
|
+
logSuppressedDetection(category, event.sessionId, correlator);
|
|
5757
5904
|
return passOutput();
|
|
5758
5905
|
}
|
|
5759
5906
|
logHintEmission(category, event.sessionId, correlator, compensateSelfResolve, output.context.length);
|
|
@@ -5763,15 +5910,23 @@ function applyHintTracking(event, output, classify) {
|
|
|
5763
5910
|
logHintEmission(category, event.sessionId, correlator, compensateSelfResolve, output.context.length);
|
|
5764
5911
|
return output;
|
|
5765
5912
|
}
|
|
5913
|
+
function logSuppressedDetection(category, sessionId, correlator) {
|
|
5914
|
+
try {
|
|
5915
|
+
getDb(globalDbPath()).prepare(
|
|
5916
|
+
`INSERT INTO hint_emissions (category, session_id, harness, correlator, emitted_at, resolved, acted_on, calls_remaining, bytes_emitted, observable, displayed)
|
|
5917
|
+
VALUES (@category, @sessionId, @harness, @correlator, @emittedAt, 1, 0, 0, 0, 0, 0)`
|
|
5918
|
+
).run({ category, sessionId, harness: getHarnessName(), correlator, emittedAt: Date.now() });
|
|
5919
|
+
} catch {
|
|
5920
|
+
}
|
|
5921
|
+
}
|
|
5766
5922
|
function logHintEmission(category, sessionId, correlator, compensateSelfResolve = false, bytesEmitted = null) {
|
|
5767
5923
|
try {
|
|
5768
5924
|
const db = getDb(globalDbPath());
|
|
5769
5925
|
const resolved = correlator === null ? 1 : 0;
|
|
5770
|
-
const actedOn = resolved === 1 && isSuppressionCategory(category) ? 1 : 0;
|
|
5771
5926
|
const window = ACTED_ON_WINDOW + (compensateSelfResolve ? 1 : 0);
|
|
5772
5927
|
db.prepare(
|
|
5773
|
-
`INSERT INTO hint_emissions (category, session_id, harness, correlator, emitted_at, resolved, acted_on, calls_remaining, bytes_emitted)
|
|
5774
|
-
VALUES (@category, @sessionId, @harness, @correlator, @emittedAt, @resolved,
|
|
5928
|
+
`INSERT INTO hint_emissions (category, session_id, harness, correlator, emitted_at, resolved, acted_on, calls_remaining, bytes_emitted, observable)
|
|
5929
|
+
VALUES (@category, @sessionId, @harness, @correlator, @emittedAt, @resolved, 0, @callsRemaining, @bytesEmitted, @observable)`
|
|
5775
5930
|
).run({
|
|
5776
5931
|
category,
|
|
5777
5932
|
sessionId,
|
|
@@ -5779,9 +5934,9 @@ function logHintEmission(category, sessionId, correlator, compensateSelfResolve
|
|
|
5779
5934
|
correlator,
|
|
5780
5935
|
emittedAt: Date.now(),
|
|
5781
5936
|
resolved,
|
|
5782
|
-
actedOn,
|
|
5783
5937
|
callsRemaining: correlator === null ? 0 : window,
|
|
5784
|
-
bytesEmitted
|
|
5938
|
+
bytesEmitted,
|
|
5939
|
+
observable: correlator === null ? 0 : 1
|
|
5785
5940
|
});
|
|
5786
5941
|
} catch {
|
|
5787
5942
|
}
|
|
@@ -5801,7 +5956,8 @@ function commandMentionsCorrelator(command, correlator) {
|
|
|
5801
5956
|
}
|
|
5802
5957
|
var SUPPRESSION_HINT_CATEGORIES = /* @__PURE__ */ new Set([
|
|
5803
5958
|
"read_reread_dedup",
|
|
5804
|
-
"edit_reread_suggest"
|
|
5959
|
+
"edit_reread_suggest",
|
|
5960
|
+
"read_structural_nav"
|
|
5805
5961
|
]);
|
|
5806
5962
|
function isSuppressionCategory(category) {
|
|
5807
5963
|
return SUPPRESSION_HINT_CATEGORIES.has(category);
|
|
@@ -5821,14 +5977,13 @@ function eventTargetText(event) {
|
|
|
5821
5977
|
function isDefiance(correlator, target) {
|
|
5822
5978
|
if (target === "") return false;
|
|
5823
5979
|
if (TOKEN_GOAT_INVOCATION_RE.test(target)) return false;
|
|
5824
|
-
return commandMentionsCorrelator(target,
|
|
5980
|
+
return splitCorrelators(correlator).some((c) => commandMentionsCorrelator(target, c));
|
|
5825
5981
|
}
|
|
5826
5982
|
function isActedOn(category, correlator, command) {
|
|
5827
5983
|
if (!TOKEN_GOAT_INVOCATION_RE.test(command)) return false;
|
|
5828
|
-
|
|
5829
|
-
|
|
5830
|
-
|
|
5831
|
-
return commandMentionsCorrelator(command, correlator);
|
|
5984
|
+
const anyMatch = splitCorrelators(correlator).some((c) => commandMentionsCorrelator(command, c));
|
|
5985
|
+
if (category === "bash_recall") return command.includes("bash-output") && anyMatch;
|
|
5986
|
+
return anyMatch;
|
|
5832
5987
|
}
|
|
5833
5988
|
function resolvePendingHintsForEvent(event) {
|
|
5834
5989
|
try {
|
|
@@ -5838,8 +5993,7 @@ function resolvePendingHintsForEvent(event) {
|
|
|
5838
5993
|
const pending = db.prepare(`SELECT id, category, correlator, calls_remaining FROM hint_emissions WHERE session_id = ? AND resolved = 0`).all(event.sessionId);
|
|
5839
5994
|
for (const row of pending) {
|
|
5840
5995
|
if (!isHintCategory(row.category) || row.correlator === null) {
|
|
5841
|
-
|
|
5842
|
-
db.prepare(`UPDATE hint_emissions SET acted_on = ?, resolved = 1 WHERE id = ?`).run(unobservable, row.id);
|
|
5996
|
+
db.prepare(`UPDATE hint_emissions SET acted_on = 0, resolved = 1, observable = 0 WHERE id = ?`).run(row.id);
|
|
5843
5997
|
continue;
|
|
5844
5998
|
}
|
|
5845
5999
|
if (isSuppressionCategory(row.category)) {
|
|
@@ -5884,11 +6038,26 @@ registerHook(
|
|
|
5884
6038
|
function categoryStats(category) {
|
|
5885
6039
|
const db = getDb(globalDbPath());
|
|
5886
6040
|
const row = db.prepare(
|
|
5887
|
-
|
|
6041
|
+
// Three populations, deliberately not pooled. `emitted` is what was shown AND could be
|
|
6042
|
+
// scored, so it is the only honest denominator for efficacy: including a row nothing could
|
|
6043
|
+
// ever have satisfied would divide a real numerator by partly-imaginary rows. `unobservable`
|
|
6044
|
+
// was shown but carried no pointer -- it spent bytes and earned no verdict. `detected` was
|
|
6045
|
+
// never shown at all (suppressed, or declined by a net-benefit gate) and spent nothing.
|
|
6046
|
+
// bytesEmitted and legacyEmissions span every row, which is correct in each case: the
|
|
6047
|
+
// unobservable rows really did cost the agent, and the never-displayed ones are zero-byte,
|
|
6048
|
+
// so neither distorts the spend figure.
|
|
6049
|
+
// COALESCE on the counts that stay `number`: SUM over no rows is NULL where the COUNT(*)
|
|
6050
|
+
// this replaced was 0, and an aggregate query always returns its row, so the `?? default`
|
|
6051
|
+
// below would not have caught it.
|
|
6052
|
+
`SELECT COALESCE(SUM(CASE WHEN displayed = 1 AND observable = 1 THEN 1 ELSE 0 END), 0) AS emitted,
|
|
6053
|
+
SUM(CASE WHEN displayed = 1 AND observable = 1 THEN acted_on ELSE 0 END) AS actedOn,
|
|
6054
|
+
COALESCE(SUM(CASE WHEN displayed = 1 AND observable = 0 THEN 1 ELSE 0 END), 0) AS unobservable,
|
|
6055
|
+
COALESCE(SUM(CASE WHEN displayed = 0 THEN 1 ELSE 0 END), 0) AS detected,
|
|
6056
|
+
SUM(bytes_emitted) AS bytesEmitted,
|
|
5888
6057
|
SUM(CASE WHEN bytes_emitted IS NULL THEN 1 ELSE 0 END) AS legacyEmissions
|
|
5889
6058
|
FROM hint_emissions WHERE category = ? AND harness = ?`
|
|
5890
6059
|
).get(category, getHarnessName());
|
|
5891
|
-
return row ?? { emitted: 0, actedOn: 0, bytesEmitted: null, legacyEmissions: 0 };
|
|
6060
|
+
return row ?? { emitted: 0, actedOn: 0, unobservable: 0, detected: 0, bytesEmitted: null, legacyEmissions: 0 };
|
|
5892
6061
|
}
|
|
5893
6062
|
function shouldSuppress(category, _sessionId2) {
|
|
5894
6063
|
try {
|
|
@@ -5896,6 +6065,7 @@ function shouldSuppress(category, _sessionId2) {
|
|
|
5896
6065
|
const { emitted, actedOn } = categoryStats(category);
|
|
5897
6066
|
if (emitted < cfg.min_sample_size) return false;
|
|
5898
6067
|
const pct = 100 * (actedOn ?? 0) / emitted;
|
|
6068
|
+
if (isSuppressionCategory(category)) return 100 - pct > cfg.defiance_threshold_pct;
|
|
5899
6069
|
return pct < cfg.suppress_threshold_pct;
|
|
5900
6070
|
} catch {
|
|
5901
6071
|
return false;
|
|
@@ -5916,7 +6086,7 @@ function manualMarks(category) {
|
|
|
5916
6086
|
function getHintStatsSummary() {
|
|
5917
6087
|
const probeThresholds = loadConfig().hints.backoff_thresholds.filter((t) => t > 0);
|
|
5918
6088
|
return HINT_CATEGORIES.map((category) => {
|
|
5919
|
-
const { emitted, actedOn, bytesEmitted, legacyEmissions } = categoryStats(category);
|
|
6089
|
+
const { emitted, actedOn, unobservable, detected, bytesEmitted, legacyEmissions } = categoryStats(category);
|
|
5920
6090
|
const marks = manualMarks(category);
|
|
5921
6091
|
const suppressed = shouldSuppress(category, "");
|
|
5922
6092
|
return {
|
|
@@ -5924,6 +6094,8 @@ function getHintStatsSummary() {
|
|
|
5924
6094
|
emitted,
|
|
5925
6095
|
actedOn: actedOn ?? 0,
|
|
5926
6096
|
efficacyPct: emitted === 0 ? null : Math.round(1e3 * (actedOn ?? 0) / emitted) / 10,
|
|
6097
|
+
unobservable,
|
|
6098
|
+
detected,
|
|
5927
6099
|
suppressed,
|
|
5928
6100
|
suppressionPermanent: suppressed && probeThresholds.length === 0,
|
|
5929
6101
|
manualEffective: marks.effective,
|
|
@@ -6062,7 +6234,7 @@ function planServedElisions(rows, bodies) {
|
|
|
6062
6234
|
// src/compact.ts
|
|
6063
6235
|
init_define_import_meta_env();
|
|
6064
6236
|
import * as fs18 from "node:fs";
|
|
6065
|
-
import * as
|
|
6237
|
+
import * as path17 from "node:path";
|
|
6066
6238
|
|
|
6067
6239
|
// src/resident_context.ts
|
|
6068
6240
|
init_define_import_meta_env();
|
|
@@ -6155,9 +6327,9 @@ function collectInvokedSkills(acc, record) {
|
|
|
6155
6327
|
function skillName(skill) {
|
|
6156
6328
|
const name = skill["name"];
|
|
6157
6329
|
if (typeof name === "string" && name.trim() !== "") return name.trim();
|
|
6158
|
-
const
|
|
6159
|
-
if (typeof
|
|
6160
|
-
const segments =
|
|
6330
|
+
const path26 = skill["path"];
|
|
6331
|
+
if (typeof path26 !== "string") return null;
|
|
6332
|
+
const segments = path26.split(/[\\/]/).filter((part) => part !== "");
|
|
6161
6333
|
const last = segments[segments.length - 1];
|
|
6162
6334
|
return last === void 0 || last === "" ? null : last;
|
|
6163
6335
|
}
|
|
@@ -6499,7 +6671,7 @@ function inferSessionGoal(cache, maxTokens = 80) {
|
|
|
6499
6671
|
const dirCounts = new Counter();
|
|
6500
6672
|
for (const fpath of editedPaths) {
|
|
6501
6673
|
try {
|
|
6502
|
-
let parent =
|
|
6674
|
+
let parent = path17.dirname(fpath);
|
|
6503
6675
|
if (parent === ".") {
|
|
6504
6676
|
parent = "root";
|
|
6505
6677
|
} else if (parent.startsWith("./")) {
|
|
@@ -6577,11 +6749,11 @@ function isNoisePath(inputPath) {
|
|
|
6577
6749
|
}
|
|
6578
6750
|
function findLatestSessionId() {
|
|
6579
6751
|
try {
|
|
6580
|
-
const
|
|
6581
|
-
if (!fs18.existsSync(
|
|
6752
|
+
const sessionsDir2 = path17.join(tokenGoatHome(), "sessions");
|
|
6753
|
+
if (!fs18.existsSync(sessionsDir2)) {
|
|
6582
6754
|
return null;
|
|
6583
6755
|
}
|
|
6584
|
-
const files = fs18.readdirSync(
|
|
6756
|
+
const files = fs18.readdirSync(sessionsDir2);
|
|
6585
6757
|
const jsonFiles = files.filter((f) => f.endsWith(".json") && !f.includes(AGENT_SALT_MARKER));
|
|
6586
6758
|
if (jsonFiles.length === 0) {
|
|
6587
6759
|
return null;
|
|
@@ -6591,9 +6763,9 @@ function findLatestSessionId() {
|
|
|
6591
6763
|
return null;
|
|
6592
6764
|
}
|
|
6593
6765
|
let latestFile = firstFile;
|
|
6594
|
-
let latestMtime = fs18.statSync(
|
|
6766
|
+
let latestMtime = fs18.statSync(path17.join(sessionsDir2, firstFile)).mtimeMs;
|
|
6595
6767
|
for (const file of jsonFiles) {
|
|
6596
|
-
const mtime = fs18.statSync(
|
|
6768
|
+
const mtime = fs18.statSync(path17.join(sessionsDir2, file)).mtimeMs;
|
|
6597
6769
|
if (mtime > latestMtime) {
|
|
6598
6770
|
latestFile = file;
|
|
6599
6771
|
latestMtime = mtime;
|
|
@@ -6615,28 +6787,28 @@ function eventCount(cache) {
|
|
|
6615
6787
|
function writeSessionManifest(projectHash, sessionId, manifestJson) {
|
|
6616
6788
|
const safeSessionId = sessionFileStem(sessionId);
|
|
6617
6789
|
if (!safeSessionId) return;
|
|
6618
|
-
const
|
|
6619
|
-
if (!fs18.existsSync(
|
|
6620
|
-
ensureDirSync(
|
|
6790
|
+
const sessionsDir2 = path17.join(dataDir(), "projects", projectHash, "sessions");
|
|
6791
|
+
if (!fs18.existsSync(sessionsDir2)) {
|
|
6792
|
+
ensureDirSync(sessionsDir2);
|
|
6621
6793
|
}
|
|
6622
|
-
const dest =
|
|
6794
|
+
const dest = path17.join(sessionsDir2, `${safeSessionId}.json`);
|
|
6623
6795
|
atomicWriteText(dest, JSON.stringify(manifestJson));
|
|
6624
6796
|
}
|
|
6625
6797
|
function readAllSessionManifests(projectHash, maxAgeSecs = 3600) {
|
|
6626
|
-
const
|
|
6627
|
-
if (!fs18.existsSync(
|
|
6798
|
+
const sessionsDir2 = path17.join(dataDir(), "projects", projectHash, "sessions");
|
|
6799
|
+
if (!fs18.existsSync(sessionsDir2)) {
|
|
6628
6800
|
return [];
|
|
6629
6801
|
}
|
|
6630
6802
|
const now = Date.now() / 1e3;
|
|
6631
6803
|
const results = [];
|
|
6632
6804
|
try {
|
|
6633
|
-
const files = fs18.readdirSync(
|
|
6805
|
+
const files = fs18.readdirSync(sessionsDir2);
|
|
6634
6806
|
for (const file of files) {
|
|
6635
6807
|
if (!file.endsWith(".json")) {
|
|
6636
6808
|
continue;
|
|
6637
6809
|
}
|
|
6638
6810
|
try {
|
|
6639
|
-
const fullPath =
|
|
6811
|
+
const fullPath = path17.join(sessionsDir2, file);
|
|
6640
6812
|
const stat2 = fs18.statSync(fullPath);
|
|
6641
6813
|
if (now - stat2.mtimeMs / 1e3 > maxAgeSecs) {
|
|
6642
6814
|
try {
|
|
@@ -6829,6 +7001,7 @@ import * as fs19 from "node:fs";
|
|
|
6829
7001
|
|
|
6830
7002
|
// src/hints/file_type_handler.ts
|
|
6831
7003
|
init_define_import_meta_env();
|
|
7004
|
+
var LARGE_SYMBOL_LINE_THRESHOLD = 200;
|
|
6832
7005
|
var FILE_TYPE_THRESHOLDS = {
|
|
6833
7006
|
pdf: 0,
|
|
6834
7007
|
// always intercept (any size)
|
|
@@ -7521,17 +7694,17 @@ function buildPackageManifestHint(options) {
|
|
|
7521
7694
|
return null;
|
|
7522
7695
|
}
|
|
7523
7696
|
}
|
|
7524
|
-
function _sanitizeHintPath(
|
|
7525
|
-
if (typeof
|
|
7697
|
+
function _sanitizeHintPath(path26) {
|
|
7698
|
+
if (typeof path26 !== "string") {
|
|
7526
7699
|
return "???";
|
|
7527
7700
|
}
|
|
7528
|
-
return
|
|
7701
|
+
return path26.replace(/[\x00]/g, "").slice(0, 200);
|
|
7529
7702
|
}
|
|
7530
7703
|
|
|
7531
7704
|
// src/doc_compact.ts
|
|
7532
7705
|
init_define_import_meta_env();
|
|
7533
7706
|
import * as fs20 from "fs";
|
|
7534
|
-
import * as
|
|
7707
|
+
import * as path18 from "path";
|
|
7535
7708
|
var defaultSentencesPerSection = 2;
|
|
7536
7709
|
var headerPrefix = "<!-- token-goat doc-compact source-hash:";
|
|
7537
7710
|
var headerRegex = /^<!-- token-goat doc-compact source-hash:(\S+) source:(.+?) -->\r?$/;
|
|
@@ -7545,14 +7718,14 @@ function sourceHash(filePath) {
|
|
|
7545
7718
|
}
|
|
7546
7719
|
function _compactSlug(absPathStr) {
|
|
7547
7720
|
const h = fingerprintContent(foldPath(absPathStr)).slice(0, 12);
|
|
7548
|
-
const ext =
|
|
7549
|
-
const stem =
|
|
7721
|
+
const ext = path18.extname(absPathStr);
|
|
7722
|
+
const stem = path18.basename(absPathStr, ext);
|
|
7550
7723
|
const safeStem = sanitizeIdForFilename(stem, 32);
|
|
7551
7724
|
return `${h}_${safeStem}`;
|
|
7552
7725
|
}
|
|
7553
7726
|
function compactPathFor(sourcePath) {
|
|
7554
7727
|
const abs = resolveIndexPath(sourcePath);
|
|
7555
|
-
return
|
|
7728
|
+
return path18.join(dataDir(), compactSubdir, `${_compactSlug(abs)}.md`);
|
|
7556
7729
|
}
|
|
7557
7730
|
function readCompactHeader(compactPath) {
|
|
7558
7731
|
try {
|
|
@@ -7601,13 +7774,13 @@ function readCompactBody(compactPath) {
|
|
|
7601
7774
|
}
|
|
7602
7775
|
}
|
|
7603
7776
|
function writeCompact(compactPath, sourcePath, compactBody, sourceRel) {
|
|
7604
|
-
const srcPath =
|
|
7777
|
+
const srcPath = path18.resolve(sourcePath);
|
|
7605
7778
|
const sha = sourceHash(srcPath);
|
|
7606
|
-
const displayRel = sourceRel ||
|
|
7779
|
+
const displayRel = sourceRel || path18.basename(srcPath);
|
|
7607
7780
|
const header = `${headerPrefix}${sha} source:${displayRel} -->
|
|
7608
7781
|
`;
|
|
7609
7782
|
const fullText = header + compactBody.trimStart();
|
|
7610
|
-
const dir =
|
|
7783
|
+
const dir = path18.dirname(compactPath);
|
|
7611
7784
|
if (!fs20.existsSync(dir)) {
|
|
7612
7785
|
ensureDirSync(dir);
|
|
7613
7786
|
}
|
|
@@ -7744,12 +7917,12 @@ function compactDoc(filePath, heading) {
|
|
|
7744
7917
|
init_define_import_meta_env();
|
|
7745
7918
|
import crypto2 from "node:crypto";
|
|
7746
7919
|
import fs21 from "node:fs";
|
|
7747
|
-
import
|
|
7920
|
+
import path19 from "node:path";
|
|
7748
7921
|
var MAX_ENTRIES = 500;
|
|
7749
7922
|
var MAX_TEXT_BYTES = 128 * 1024;
|
|
7750
7923
|
var CACHE_FILE = "workspace-evidence.json";
|
|
7751
7924
|
function cachePath() {
|
|
7752
|
-
return
|
|
7925
|
+
return path19.join(dataDir(), CACHE_FILE);
|
|
7753
7926
|
}
|
|
7754
7927
|
function hash(text) {
|
|
7755
7928
|
return crypto2.createHash("sha256").update(text).digest("hex");
|
|
@@ -7872,7 +8045,7 @@ ${changed.map((entry) => `- ${displaySafePath(entry.source)} (use a fresh surgic
|
|
|
7872
8045
|
// src/notebook_compact.ts
|
|
7873
8046
|
init_define_import_meta_env();
|
|
7874
8047
|
import * as fs22 from "node:fs";
|
|
7875
|
-
import * as
|
|
8048
|
+
import * as path20 from "node:path";
|
|
7876
8049
|
var NB_STRIP_MIN_SAVINGS = 4096;
|
|
7877
8050
|
function stripNotebook(nbDict) {
|
|
7878
8051
|
const cells = [];
|
|
@@ -7892,14 +8065,14 @@ function stripNotebook(nbDict) {
|
|
|
7892
8065
|
var SIDECAR_DEFAULT_MAX_COUNT = 200;
|
|
7893
8066
|
var SIDECAR_DEFAULT_MAX_AGE_MS = 24 * 3600 * 1e3;
|
|
7894
8067
|
function pruneSidecars(cacheRoot, maxCount = SIDECAR_DEFAULT_MAX_COUNT, maxAgeMs = SIDECAR_DEFAULT_MAX_AGE_MS) {
|
|
7895
|
-
const nbStripDir =
|
|
8068
|
+
const nbStripDir = path20.join(cacheRoot, "nb_strip");
|
|
7896
8069
|
let removed = 0;
|
|
7897
8070
|
try {
|
|
7898
8071
|
if (!fs22.existsSync(nbStripDir)) return 0;
|
|
7899
8072
|
const cutoff = Date.now() - maxAgeMs;
|
|
7900
8073
|
const kept = [];
|
|
7901
8074
|
for (const entry of fs22.readdirSync(nbStripDir)) {
|
|
7902
|
-
const dir =
|
|
8075
|
+
const dir = path20.join(nbStripDir, entry);
|
|
7903
8076
|
let mtime;
|
|
7904
8077
|
try {
|
|
7905
8078
|
mtime = fs22.statSync(dir).mtimeMs;
|
|
@@ -7935,8 +8108,8 @@ function pruneSidecars(cacheRoot, maxCount = SIDECAR_DEFAULT_MAX_COUNT, maxAgeMs
|
|
|
7935
8108
|
}
|
|
7936
8109
|
function getOrCreateSidecar(rawBytes, cacheRoot, opts = {}) {
|
|
7937
8110
|
const sha = fingerprintContent(rawBytes);
|
|
7938
|
-
const sidecarDir =
|
|
7939
|
-
const sidecarPath =
|
|
8111
|
+
const sidecarDir = path20.join(cacheRoot, "nb_strip", sha);
|
|
8112
|
+
const sidecarPath = path20.join(sidecarDir, "stripped.ipynb");
|
|
7940
8113
|
if (fs22.existsSync(sidecarPath)) {
|
|
7941
8114
|
return [sidecarPath, false];
|
|
7942
8115
|
}
|
|
@@ -8042,10 +8215,10 @@ var COMMENT_SYNTAX = new Map([
|
|
|
8042
8215
|
...["py", "rb", "sh", "bash", "zsh", "pl", "r"].map((ext) => [ext, HASH]),
|
|
8043
8216
|
...["sql", "lua", "hs"].map((ext) => [ext, DOUBLE_DASH])
|
|
8044
8217
|
]);
|
|
8045
|
-
function commentSyntaxFor(
|
|
8046
|
-
const dot =
|
|
8218
|
+
function commentSyntaxFor(path26) {
|
|
8219
|
+
const dot = path26.lastIndexOf(".");
|
|
8047
8220
|
if (dot < 0) return null;
|
|
8048
|
-
return COMMENT_SYNTAX.get(
|
|
8221
|
+
return COMMENT_SYNTAX.get(path26.slice(dot + 1).toLowerCase()) ?? null;
|
|
8049
8222
|
}
|
|
8050
8223
|
function planCommentFolds(rows, syntax, commentKeep, commentMinBlock, occupied) {
|
|
8051
8224
|
if (syntax === null || rows.length === 0 || commentKeep < 1 || commentMinBlock < 1) return [];
|
|
@@ -8154,8 +8327,8 @@ function planProseFolds(rows, claimed) {
|
|
|
8154
8327
|
}
|
|
8155
8328
|
return folds;
|
|
8156
8329
|
}
|
|
8157
|
-
function foldDetail(
|
|
8158
|
-
const head = `${
|
|
8330
|
+
function foldDetail(path26, folds, maxLen = MAX_FOLD_DETAIL) {
|
|
8331
|
+
const head = `${path26}::`;
|
|
8159
8332
|
const ids = folds.map((f) => f.kind === "body" ? f.name : `#${f.firstLine}-${f.lastLine}`);
|
|
8160
8333
|
const budget = maxLen - head.length - FOLD_DETAIL_SUFFIX_BUDGET;
|
|
8161
8334
|
const kept = [];
|
|
@@ -8173,7 +8346,7 @@ function foldDetail(path24, folds, maxLen = MAX_FOLD_DETAIL) {
|
|
|
8173
8346
|
|
|
8174
8347
|
// src/fold_delivery.ts
|
|
8175
8348
|
init_define_import_meta_env();
|
|
8176
|
-
import { readFileSync as readFileSync13, statSync as
|
|
8349
|
+
import { readFileSync as readFileSync13, statSync as statSync13 } from "node:fs";
|
|
8177
8350
|
|
|
8178
8351
|
// src/indexed_source.ts
|
|
8179
8352
|
init_define_import_meta_env();
|
|
@@ -8228,7 +8401,7 @@ function commentFoldNotice(firstLine, lastLine, shownPath) {
|
|
|
8228
8401
|
var FOLD_SPAN_PARSE_MAX_BYTES = 4e5;
|
|
8229
8402
|
function parseFoldSpansFromDisk(normalizedPath, rows) {
|
|
8230
8403
|
try {
|
|
8231
|
-
if (
|
|
8404
|
+
if (statSync13(normalizedPath).size > FOLD_SPAN_PARSE_MAX_BYTES) return [];
|
|
8232
8405
|
const language = detectLanguage(normalizedPath);
|
|
8233
8406
|
if (!isTreeSitterAvailable(language)) return [];
|
|
8234
8407
|
const fileText = readFileSync13(normalizedPath, "utf-8");
|
|
@@ -8247,7 +8420,7 @@ function parseFoldSpansFromDisk(normalizedPath, rows) {
|
|
|
8247
8420
|
function resolveFoldSpans(normalizedPath, hasCommentSyntax, rows) {
|
|
8248
8421
|
try {
|
|
8249
8422
|
const entry = getFileEntry(normalizedPath);
|
|
8250
|
-
if (entry !== null && entry.sha !== "" && !isVirtualIndexedPath(normalizedPath) && entry.sha === fingerprintFile(normalizedPath) && entry.parserSha ===
|
|
8423
|
+
if (entry !== null && entry.sha !== "" && !isVirtualIndexedPath(normalizedPath) && entry.sha === fingerprintFile(normalizedPath) && entry.parserSha === parserFingerprintForLanguage(entry.language)) {
|
|
8251
8424
|
return querySymbols({ filePath: normalizedPath, limit: BODY_FOLD_SYMBOL_LIMIT });
|
|
8252
8425
|
}
|
|
8253
8426
|
if (hasCommentSyntax) enqueueDirtyPathSafe(normalizedPath);
|
|
@@ -8457,13 +8630,14 @@ function largeFileDenyBytes() {
|
|
|
8457
8630
|
function counterfactualCredit(counterfactualBytes, emittedBytes = 0) {
|
|
8458
8631
|
return Math.max(0, Math.min(counterfactualBytes, PER_FILE_COUNTERFACTUAL_CEILING) - emittedBytes);
|
|
8459
8632
|
}
|
|
8460
|
-
function
|
|
8461
|
-
const
|
|
8462
|
-
|
|
8633
|
+
function diffHintCredit(counterfactualBytes, body) {
|
|
8634
|
+
const credit = counterfactualCredit(counterfactualBytes, body.length);
|
|
8635
|
+
if (savedTokensFromBytes(credit) < loadConfig().hints.diff_hint_min_tokens_saved) return null;
|
|
8636
|
+
return credit;
|
|
8463
8637
|
}
|
|
8464
8638
|
function relPathWithinRoot(root, target) {
|
|
8465
|
-
const rel =
|
|
8466
|
-
if (rel.startsWith("..") ||
|
|
8639
|
+
const rel = path21.relative(root, target).replace(/\\/g, "/");
|
|
8640
|
+
if (rel.startsWith("..") || path21.isAbsolute(rel)) return null;
|
|
8467
8641
|
return rel;
|
|
8468
8642
|
}
|
|
8469
8643
|
function _isDocFile(filePath) {
|
|
@@ -8472,7 +8646,7 @@ function _isDocFile(filePath) {
|
|
|
8472
8646
|
}
|
|
8473
8647
|
function isSessionArtifactFile(filePath) {
|
|
8474
8648
|
if (/[/\\]tasks[/\\][a-z0-9]+\.output$/i.test(filePath)) return true;
|
|
8475
|
-
if (
|
|
8649
|
+
if (new RegExp("[/\\\\]tool-results[/\\\\]" + TOOL_RESULTS_ID_CHARS + "\\.txt$", "i").test(filePath)) return true;
|
|
8476
8650
|
return false;
|
|
8477
8651
|
}
|
|
8478
8652
|
function sessionArtifactRecall(rawPath) {
|
|
@@ -8489,7 +8663,7 @@ var BINARY_FILE_TYPE_EXTS = /* @__PURE__ */ new Set(["pdf", "docx", "xlsx", "ppt
|
|
|
8489
8663
|
var TEXT_FILE_TYPE_EXTS = /* @__PURE__ */ new Set(["html", "htm", "xhtml", "txt", "log", "out", "err", "trace", "csv", "tsv", "vtt", "srt", "svg", "xml", "dtsx", "ampkg", "xaml", "json", "yaml", "yml", "jsonl"]);
|
|
8490
8664
|
var DISPATCHED_FILE_TYPE_EXTS = /* @__PURE__ */ new Set([...BINARY_FILE_TYPE_EXTS, ...TEXT_FILE_TYPE_EXTS]);
|
|
8491
8665
|
function isDispatchedFileType(basename11) {
|
|
8492
|
-
return DISPATCHED_FILE_TYPE_EXTS.has(
|
|
8666
|
+
return DISPATCHED_FILE_TYPE_EXTS.has(path21.extname(basename11).slice(1).toLowerCase());
|
|
8493
8667
|
}
|
|
8494
8668
|
function extractQuickSymbolSamples(content, _filePath) {
|
|
8495
8669
|
const symbols = [];
|
|
@@ -8539,11 +8713,6 @@ function surgicalHint(filePath, basename11, lineCount, fileContent) {
|
|
|
8539
8713
|
const isHtmlFile = /\.(html|htm)$/i.test(basename11);
|
|
8540
8714
|
const isSectionFile = /\.(css|scss|sass|less|toml)$/i.test(basename11);
|
|
8541
8715
|
const isXmlFile = /\.(xml|dtsx|ampkg|xaml)$/i.test(basename11) && !basename11.toLowerCase().endsWith("-meta.xml");
|
|
8542
|
-
const escapeHintName = (name) => {
|
|
8543
|
-
const quoted = name.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
8544
|
-
const safe = displaySafeText(quoted);
|
|
8545
|
-
return safe !== quoted ? "" : safe.trim();
|
|
8546
|
-
};
|
|
8547
8716
|
if (isXmlFile) {
|
|
8548
8717
|
return `Use \`token-goat xml-outline "${filePath}"\` for structure or \`token-goat xml-query "${filePath}" "<selector>"\` for nodes.`;
|
|
8549
8718
|
} else if (isDocFile) {
|
|
@@ -8589,9 +8758,62 @@ function surgicalHint(filePath, basename11, lineCount, fileContent) {
|
|
|
8589
8758
|
if (isShellScript && samples.length > 0) {
|
|
8590
8759
|
return `Use \`token-goat section "${filePath}::${sym}"\`${avail} for a section, or \`token-goat skeleton "${filePath}"\` / \`token-goat outline "${filePath}"\` for structure.`;
|
|
8591
8760
|
}
|
|
8761
|
+
if (samples.length > 0) {
|
|
8762
|
+
try {
|
|
8763
|
+
const indexed = querySymbols({ filePath, name: sym, limit: 1 })[0];
|
|
8764
|
+
if (indexed !== void 0 && isLargeSymbolSpan(indexed.lineStart, indexed.lineEnd, filePath)) {
|
|
8765
|
+
return `\`${sym}\` spans ${indexed.lineEnd - indexed.lineStart + 1} lines -- use \`token-goat grep "<pattern>" ${filePath} -C 15 --symbol\` for a slice inside it, or \`token-goat scope ${filePath}:${indexed.lineStart}\` to confirm the enclosing symbol.`;
|
|
8766
|
+
}
|
|
8767
|
+
} catch {
|
|
8768
|
+
}
|
|
8769
|
+
}
|
|
8592
8770
|
return `Use \`token-goat read "${filePath}::${sym}"\`${avail} for one function, or \`token-goat skeleton "${filePath}"\` / \`token-goat outline "${filePath}"\` for structure.`;
|
|
8593
8771
|
}
|
|
8594
8772
|
}
|
|
8773
|
+
function escapeHintName(name) {
|
|
8774
|
+
const quoted = name.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
8775
|
+
const safe = displaySafeText(quoted);
|
|
8776
|
+
return safe !== quoted ? "" : safe.trim();
|
|
8777
|
+
}
|
|
8778
|
+
function isLargeSymbolSpan(lineStart, lineEnd, filePath) {
|
|
8779
|
+
const span = lineEnd - lineStart + 1;
|
|
8780
|
+
if (span > LARGE_SYMBOL_LINE_THRESHOLD) return true;
|
|
8781
|
+
const totalLines = lineCountForSurgicalHint(filePath);
|
|
8782
|
+
return totalLines > 0 && span > totalLines / 2;
|
|
8783
|
+
}
|
|
8784
|
+
function isWindowInsideMuchLargerSymbol(filePath, start, end) {
|
|
8785
|
+
try {
|
|
8786
|
+
const windowLines = end - start + 1;
|
|
8787
|
+
return querySymbols({ filePath, limit: 500 }).some((s) => {
|
|
8788
|
+
const span = s.lineEnd - s.lineStart + 1;
|
|
8789
|
+
return s.lineStart <= start && s.lineEnd >= end && windowLines * 3 <= span;
|
|
8790
|
+
});
|
|
8791
|
+
} catch {
|
|
8792
|
+
return false;
|
|
8793
|
+
}
|
|
8794
|
+
}
|
|
8795
|
+
function realSymbolReadHint(filePath, shown, range) {
|
|
8796
|
+
let candidates;
|
|
8797
|
+
try {
|
|
8798
|
+
const all = querySymbols({ filePath, limit: 500 }).map((s) => ({ name: escapeHintName(s.name), lineStart: s.lineStart, lineEnd: s.lineEnd }));
|
|
8799
|
+
const overlapping = range !== void 0 ? all.filter((s) => s.lineStart <= range.end && s.lineEnd >= range.start) : [];
|
|
8800
|
+
candidates = (overlapping.length > 0 ? overlapping : all).filter((s) => s.name !== "");
|
|
8801
|
+
} catch {
|
|
8802
|
+
candidates = [];
|
|
8803
|
+
}
|
|
8804
|
+
const top = candidates[0];
|
|
8805
|
+
if (top === void 0) {
|
|
8806
|
+
return range !== void 0 ? '`token-goat read "' + shown + "@" + range.start + "-" + range.end + '"`' : '`token-goat outline "' + shown + '"`';
|
|
8807
|
+
}
|
|
8808
|
+
if (isLargeSymbolSpan(top.lineStart, top.lineEnd, filePath)) {
|
|
8809
|
+
const line = range !== void 0 ? range.start : top.lineStart;
|
|
8810
|
+
const span = top.lineEnd - top.lineStart + 1;
|
|
8811
|
+
return '`token-goat grep "<pattern>" ' + shown + " -C 15 --symbol` for a slice inside `" + top.name + "` (" + span + " lines), or `token-goat scope " + shown + ":" + line + "` to confirm the enclosing symbol";
|
|
8812
|
+
}
|
|
8813
|
+
const names = candidates.map((s) => s.name).slice(0, 3);
|
|
8814
|
+
const rest = names.length > 1 ? " (or: " + names.slice(1).join(", ") + ")" : "";
|
|
8815
|
+
return '`token-goat read "' + shown + "::" + names[0] + '"`' + rest;
|
|
8816
|
+
}
|
|
8595
8817
|
function lineCountForSurgicalHint(filePath, fileStatSize) {
|
|
8596
8818
|
try {
|
|
8597
8819
|
if (fileStatSize !== void 0 && fileStatSize > SLICE_ESTIMATE_SCAN_CAP_BYTES) {
|
|
@@ -8702,7 +8924,7 @@ function preReadHandlerInner(event) {
|
|
|
8702
8924
|
} catch {
|
|
8703
8925
|
}
|
|
8704
8926
|
}
|
|
8705
|
-
const basename11 = displaySafePath(
|
|
8927
|
+
const basename11 = displaySafePath(path21.basename(normalized));
|
|
8706
8928
|
if (isLockFile(basename11)) {
|
|
8707
8929
|
return denyOutput(
|
|
8708
8930
|
'Lock files are rarely useful to read in full. Use `token-goat section "' + shown + '::<section>"` to extract a specific dependency, or read the relevant manifest instead.'
|
|
@@ -8823,12 +9045,13 @@ function preReadHandlerInner(event) {
|
|
|
8823
9045
|
(basename11 + " is unchanged since last read. " + surgicalHint(normalized, basename11, countTextLines(snapDiff.currentContent), snapDiff.currentContent)).trimEnd()
|
|
8824
9046
|
);
|
|
8825
9047
|
}
|
|
8826
|
-
if (snapDiff.kind === "diff"
|
|
8827
|
-
|
|
8828
|
-
|
|
8829
|
-
|
|
8830
|
-
("
|
|
8831
|
-
|
|
9048
|
+
if (snapDiff.kind === "diff") {
|
|
9049
|
+
const diffBody = ("Content changed since last read of " + basename11 + ". Here is what changed:\n\n" + fenceUntrustedFileContent("```diff\n" + snapDiff.diff + "\n```") + "\n\n" + surgicalHint(normalized, basename11, countTextLines(snapDiff.currentContent), snapDiff.currentContent)).trimEnd();
|
|
9050
|
+
if (diffHintCredit(snapDiff.currentContent.length, diffBody) !== null) {
|
|
9051
|
+
recordActualRead(event, normalized);
|
|
9052
|
+
recordStat("session_hint", 0, 0);
|
|
9053
|
+
return denyOutput(diffBody);
|
|
9054
|
+
}
|
|
8832
9055
|
}
|
|
8833
9056
|
recordActualRead(event, normalized);
|
|
8834
9057
|
recordStat("session_hint", 0, 0);
|
|
@@ -8859,12 +9082,13 @@ function preReadHandlerInner(event) {
|
|
|
8859
9082
|
(basename11 + " is unchanged since last read. " + surgicalHint(normalized, basename11, countTextLines(memSnapDiff.currentContent), memSnapDiff.currentContent)).trimEnd()
|
|
8860
9083
|
);
|
|
8861
9084
|
}
|
|
8862
|
-
if (memSnapDiff.kind === "diff"
|
|
8863
|
-
|
|
8864
|
-
|
|
8865
|
-
|
|
8866
|
-
("
|
|
8867
|
-
|
|
9085
|
+
if (memSnapDiff.kind === "diff") {
|
|
9086
|
+
const diffBody = ("Content changed since last read of " + basename11 + ". Here is what changed:\n\n" + fenceUntrustedFileContent("```diff\n" + memSnapDiff.diff + "\n```") + "\n\n" + surgicalHint(normalized, basename11, countTextLines(memSnapDiff.currentContent), memSnapDiff.currentContent)).trimEnd();
|
|
9087
|
+
if (diffHintCredit(memSnapDiff.currentContent.length, diffBody) !== null) {
|
|
9088
|
+
recordActualRead(event, normalized);
|
|
9089
|
+
recordStat("session_hint", 0, 0);
|
|
9090
|
+
return denyOutput(diffBody);
|
|
9091
|
+
}
|
|
8868
9092
|
}
|
|
8869
9093
|
recordActualRead(event, normalized);
|
|
8870
9094
|
recordStat("session_hint", 0, 0);
|
|
@@ -8913,12 +9137,13 @@ function preReadHandlerInner(event) {
|
|
|
8913
9137
|
);
|
|
8914
9138
|
}
|
|
8915
9139
|
if (snapDiff.kind === "diff") {
|
|
8916
|
-
|
|
8917
|
-
const artifactDiffCredit =
|
|
8918
|
-
|
|
8919
|
-
|
|
8920
|
-
"
|
|
8921
|
-
|
|
9140
|
+
const diffBody = "Content changed since last read of " + basename11 + ". Here is what changed:\n\n" + fenceUntrustedFileContent("```diff\n" + snapDiff.diff + "\n```") + "\n\n" + sessionArtifactRecall(normalized);
|
|
9141
|
+
const artifactDiffCredit = diffHintCredit(snapDiff.currentContent.length, diffBody);
|
|
9142
|
+
if (artifactDiffCredit !== null) {
|
|
9143
|
+
recordActualRead(event, normalized);
|
|
9144
|
+
recordStat("session_hint", artifactDiffCredit, savedTokensFromBytes(artifactDiffCredit), void 0, "artifact-snapshot-diff");
|
|
9145
|
+
return denyOutput(diffBody);
|
|
9146
|
+
}
|
|
8922
9147
|
}
|
|
8923
9148
|
recordActualRead(event, normalized);
|
|
8924
9149
|
recordStat("session_hint", 0, 0);
|
|
@@ -8961,13 +9186,12 @@ function preReadHandlerInner(event) {
|
|
|
8961
9186
|
);
|
|
8962
9187
|
}
|
|
8963
9188
|
if (snapDiff.kind === "diff") {
|
|
8964
|
-
|
|
9189
|
+
const diffBody = ("Content changed since last read of " + basename11 + ". Here is what changed:\n\n" + fenceUntrustedFileContent("```diff\n" + snapDiff.diff + "\n```") + "\n\n" + surgicalHint(normalized, basename11, countTextLines(snapDiff.currentContent), snapDiff.currentContent)).trimEnd();
|
|
9190
|
+
const diffCredit = diffHintCredit(snapDiff.currentContent.length, diffBody);
|
|
9191
|
+
if (diffCredit !== null) {
|
|
8965
9192
|
recordActualRead(event, normalized);
|
|
8966
|
-
const diffCredit = counterfactualCredit(snapDiff.currentContent.length, snapDiff.diff.length);
|
|
8967
9193
|
recordStat("diff_hint", diffCredit, savedTokensFromBytes(diffCredit));
|
|
8968
|
-
return denyOutput(
|
|
8969
|
-
("Content changed since last read of " + basename11 + ". Here is what changed:\n\n" + fenceUntrustedFileContent("```diff\n" + snapDiff.diff + "\n```") + "\n\n" + surgicalHint(normalized, basename11, countTextLines(snapDiff.currentContent), snapDiff.currentContent)).trimEnd()
|
|
8970
|
-
);
|
|
9194
|
+
return denyOutput(diffBody);
|
|
8971
9195
|
}
|
|
8972
9196
|
}
|
|
8973
9197
|
}
|
|
@@ -9017,7 +9241,7 @@ function preReadHandlerInner(event) {
|
|
|
9017
9241
|
recordStat("read_served_deny", blocked, savedTokensFromBytes(blocked));
|
|
9018
9242
|
recordStat("session_hint", 0, 0);
|
|
9019
9243
|
return denyOutput(
|
|
9020
|
-
"Every line of " + shown + " this read would return was already served in this session, byte for byte. Recall it with `token-goat bash-output " + alreadyServed.id +
|
|
9244
|
+
"Every line of " + shown + " this read would return was already served in this session, byte for byte. Recall it with `token-goat bash-output " + alreadyServed.id + "`, or pull just the part you need with " + realSymbolReadHint(normalized, shown) + "."
|
|
9021
9245
|
);
|
|
9022
9246
|
}
|
|
9023
9247
|
const window = readRequestedSliceWindow(event);
|
|
@@ -9032,7 +9256,7 @@ function preReadHandlerInner(event) {
|
|
|
9032
9256
|
recordStat("read_served_deny", blocked, savedTokensFromBytes(blocked));
|
|
9033
9257
|
recordStat("session_hint", 0, 0);
|
|
9034
9258
|
return denyOutput(
|
|
9035
|
-
"Lines " + start + ".." + end + " of " + shown +
|
|
9259
|
+
"Lines " + start + ".." + end + " of " + shown + " was already read this session. Pull just the part you need with " + realSymbolReadHint(normalized, shown, { start, end }) + "."
|
|
9036
9260
|
);
|
|
9037
9261
|
};
|
|
9038
9262
|
if (snapDiff.kind === "unchanged") {
|
|
@@ -9058,11 +9282,14 @@ function preReadHandlerInner(event) {
|
|
|
9058
9282
|
}
|
|
9059
9283
|
const isPagingTracked = isSourceExt || /\.(md|mdx|markdown|rst|xml|dtsx|ampkg|xaml)$/i.test(basename11);
|
|
9060
9284
|
if (isPagingTracked && window.isExplicitSlice && prevRanges.length >= 3) {
|
|
9061
|
-
|
|
9062
|
-
|
|
9063
|
-
|
|
9064
|
-
|
|
9065
|
-
|
|
9285
|
+
const pagingWindowSpan = window.offset !== void 0 && window.limit !== void 0 ? { start: window.offset, end: window.offset + window.limit - 1 } : void 0;
|
|
9286
|
+
if (pagingWindowSpan === void 0 || !isWindowInsideMuchLargerSymbol(normalized, pagingWindowSpan.start, pagingWindowSpan.end)) {
|
|
9287
|
+
recordStat("read_count_deny", rereadCredit, savedTokensFromBytes(rereadCredit));
|
|
9288
|
+
recordStat("session_hint", 0, 0);
|
|
9289
|
+
return denyOutput(
|
|
9290
|
+
"Sequential line-range paging detected on " + shown + " (" + (prevRanges.length + 1) + " slices read). " + surgicalHint(normalized, basename11, lineCountForSurgicalHint(normalized)) + " Inspect structure directly without manual chunk paging."
|
|
9291
|
+
);
|
|
9292
|
+
}
|
|
9066
9293
|
}
|
|
9067
9294
|
}
|
|
9068
9295
|
if (config2.hints.reread_deny && !protectedRead) {
|
|
@@ -9104,7 +9331,7 @@ function preReadHandlerInner(event) {
|
|
|
9104
9331
|
}
|
|
9105
9332
|
const pagingWindow = readRequestedSliceWindow(event);
|
|
9106
9333
|
const activeRanges = getFileLineRanges(normalized);
|
|
9107
|
-
const pagingNote = pagingWindow.isExplicitSlice && activeRanges.length >= 2 ? " Sequential line-range paging detected (" + (activeRanges.length + 1) + " slices read). Prefer `token-goat skeleton " + shown +
|
|
9334
|
+
const pagingNote = pagingWindow.isExplicitSlice && activeRanges.length >= 2 ? " Sequential line-range paging detected (" + (activeRanges.length + 1) + " slices read). Prefer `token-goat skeleton " + shown + "` or " + realSymbolReadHint(normalized, shown, pagingWindow.offset !== void 0 && pagingWindow.limit !== void 0 ? { start: pagingWindow.offset, end: pagingWindow.offset + pagingWindow.limit - 1 } : void 0) + "." : "";
|
|
9108
9335
|
if (!isWithinQuietHours(config2.hints.quiet_hours)) {
|
|
9109
9336
|
recordStat("session_hint", 0, 0);
|
|
9110
9337
|
}
|
|
@@ -9147,7 +9374,7 @@ function preReadHandlerInner(event) {
|
|
|
9147
9374
|
"Note: " + shown + " is large (" + kb + "KB). " + hint + contextPressureAdvisorySuffix()
|
|
9148
9375
|
);
|
|
9149
9376
|
}
|
|
9150
|
-
const fileTypeExt =
|
|
9377
|
+
const fileTypeExt = path21.extname(normalized).slice(1).toLowerCase();
|
|
9151
9378
|
const fileStatSize = size ?? statSize(normalized) ?? 0;
|
|
9152
9379
|
const isKnownFileType = DISPATCHED_FILE_TYPE_EXTS.has(fileTypeExt);
|
|
9153
9380
|
if (event.toolName !== "Grep" && !isImagePath(normalized) && (isKnownFileType || fileStatSize >= FILE_TYPE_THRESHOLDS.generic)) {
|
|
@@ -9228,7 +9455,7 @@ function postReadHandlerInner(event, suppressStructuralHint) {
|
|
|
9228
9455
|
if (isTruncatedReadDelivery(event, respText)) {
|
|
9229
9456
|
markFileTruncated(normalized, !readRequestedSliceWindow(event).isExplicitSlice);
|
|
9230
9457
|
}
|
|
9231
|
-
const postBasename =
|
|
9458
|
+
const postBasename = path21.basename(normalized);
|
|
9232
9459
|
const diffSourcesEnabled = loadConfig().hints.serve_diff_on_reread;
|
|
9233
9460
|
if (/\.(md|mdx|markdown|rst|txt)$/i.test(postBasename) || isSessionArtifactFile(normalized) || diffSourcesEnabled && isDiffableSource(postBasename)) {
|
|
9234
9461
|
try {
|
|
@@ -9344,7 +9571,7 @@ function elideAlreadyServedLines(event, respText) {
|
|
|
9344
9571
|
if (ids.length === 0) return null;
|
|
9345
9572
|
const parsed = parseNumberedReadResult(respText, readStartLine(event));
|
|
9346
9573
|
if (parsed === null) return null;
|
|
9347
|
-
if (
|
|
9574
|
+
if (hasPreciseSecret(respText)) return null;
|
|
9348
9575
|
const bodies = [];
|
|
9349
9576
|
for (let i = ids.length - 1; i >= 0; i--) {
|
|
9350
9577
|
const id = ids[i];
|
|
@@ -9384,7 +9611,7 @@ function structuralFoldInputs(event, respText) {
|
|
|
9384
9611
|
if (filePath === void 0) return null;
|
|
9385
9612
|
if (readRequestedSliceWindow(event).isExplicitSlice) return null;
|
|
9386
9613
|
if (isTruncatedReadDelivery(event, respText)) return null;
|
|
9387
|
-
if (
|
|
9614
|
+
if (hasPreciseSecret(respText)) return null;
|
|
9388
9615
|
const parsed = parseNumberedReadResult(respText, readStartLine(event));
|
|
9389
9616
|
if (parsed === null) return null;
|
|
9390
9617
|
const normalized = normalizePath(filePath);
|
|
@@ -9421,7 +9648,7 @@ function foldCodeBodies(event, respText) {
|
|
|
9421
9648
|
const requestedOffset = sliceWin.offset;
|
|
9422
9649
|
if (requestedOffset !== void 0 && readStartLine(event) !== requestedOffset) return null;
|
|
9423
9650
|
if (isTruncatedReadDelivery(event, respText)) return null;
|
|
9424
|
-
if (
|
|
9651
|
+
if (hasPreciseSecret(respText)) return null;
|
|
9425
9652
|
const parsed = parseNumberedReadResult(respText, readStartLine(event));
|
|
9426
9653
|
if (parsed === null) return null;
|
|
9427
9654
|
const shown = displaySafePath(toDisplayPath(findProject(getCwd(event) ?? process.cwd())?.root, normalized));
|
|
@@ -9458,13 +9685,13 @@ registerHook("post_tool_use", postReadHandler, { toolName: "Read" });
|
|
|
9458
9685
|
// src/baseline.ts
|
|
9459
9686
|
init_define_import_meta_env();
|
|
9460
9687
|
import * as fs26 from "node:fs";
|
|
9461
|
-
import * as
|
|
9688
|
+
import * as path24 from "node:path";
|
|
9462
9689
|
|
|
9463
9690
|
// src/cli_context_stats.ts
|
|
9464
9691
|
init_define_import_meta_env();
|
|
9465
9692
|
import * as fs25 from "node:fs";
|
|
9466
|
-
import * as
|
|
9467
|
-
import * as
|
|
9693
|
+
import * as os6 from "node:os";
|
|
9694
|
+
import * as path23 from "node:path";
|
|
9468
9695
|
|
|
9469
9696
|
// src/confirm_apply.ts
|
|
9470
9697
|
init_define_import_meta_env();
|
|
@@ -9521,7 +9748,7 @@ ${diff}
|
|
|
9521
9748
|
// src/memory_prune.ts
|
|
9522
9749
|
init_define_import_meta_env();
|
|
9523
9750
|
import * as fs24 from "node:fs";
|
|
9524
|
-
import * as
|
|
9751
|
+
import * as path22 from "node:path";
|
|
9525
9752
|
var ENTRY_RE = /^\s*-\s*\[(?<title>[^\]]+)\]\((?<target>[^)]+?\.md)\)/;
|
|
9526
9753
|
var URL_SCHEME_RE = /^[a-z][a-z0-9+.-]*:\/\//i;
|
|
9527
9754
|
function parseIndex(text) {
|
|
@@ -9566,7 +9793,7 @@ function pruneIndex(memoryDir, opts) {
|
|
|
9566
9793
|
changed: false,
|
|
9567
9794
|
tokensSaved: 0
|
|
9568
9795
|
};
|
|
9569
|
-
const memoryMd =
|
|
9796
|
+
const memoryMd = path22.join(memoryDir, "MEMORY.md");
|
|
9570
9797
|
let text;
|
|
9571
9798
|
try {
|
|
9572
9799
|
text = fs24.readFileSync(memoryMd, "utf-8");
|
|
@@ -9580,7 +9807,7 @@ function pruneIndex(memoryDir, opts) {
|
|
|
9580
9807
|
const dups = [];
|
|
9581
9808
|
for (const entry of entries) {
|
|
9582
9809
|
const isUrl = URL_SCHEME_RE.test(entry.target);
|
|
9583
|
-
const targetExists = isUrl ? true :
|
|
9810
|
+
const targetExists = isUrl ? true : path22.isAbsolute(entry.target) ? fs24.existsSync(entry.target) : fs24.existsSync(path22.join(memoryDir, entry.target));
|
|
9584
9811
|
const foldedTarget = foldPath(entry.target);
|
|
9585
9812
|
if (!targetExists) {
|
|
9586
9813
|
dead.push(entry);
|
|
@@ -9724,7 +9951,7 @@ async function tryEmbeddingClusters(siblings, snippets, threshold) {
|
|
|
9724
9951
|
}
|
|
9725
9952
|
async function findContentDuplicates(memoryDir, _opts) {
|
|
9726
9953
|
const threshold = _opts?.threshold ?? 0.92;
|
|
9727
|
-
const siblings = fs24.readdirSync(memoryDir).filter((name) => name.toLowerCase().endsWith(".md") && name.toLowerCase() !== "memory.md").map((name) =>
|
|
9954
|
+
const siblings = fs24.readdirSync(memoryDir).filter((name) => name.toLowerCase().endsWith(".md") && name.toLowerCase() !== "memory.md").map((name) => path22.join(memoryDir, name)).sort();
|
|
9728
9955
|
if (siblings.length < 2) {
|
|
9729
9956
|
return [];
|
|
9730
9957
|
}
|
|
@@ -9808,7 +10035,7 @@ function auditClaudeMd(files) {
|
|
|
9808
10035
|
const overlaps = [];
|
|
9809
10036
|
for (const [stripped, filesSet] of lineToFiles) {
|
|
9810
10037
|
if (filesSet.has(report.path) && filesSet.size > 1) {
|
|
9811
|
-
const others = Array.from(filesSet).filter((p) => p !== report.path).map((p) =>
|
|
10038
|
+
const others = Array.from(filesSet).filter((p) => p !== report.path).map((p) => path22.basename(p));
|
|
9812
10039
|
if (others.length > 0) {
|
|
9813
10040
|
if (stripped.length > 60) {
|
|
9814
10041
|
overlaps.push(
|
|
@@ -9834,31 +10061,31 @@ function tok(filePath) {
|
|
|
9834
10061
|
return 0;
|
|
9835
10062
|
}
|
|
9836
10063
|
}
|
|
9837
|
-
function findClaudeMdFiles(projectRoot, homeDir =
|
|
10064
|
+
function findClaudeMdFiles(projectRoot, homeDir = os6.homedir()) {
|
|
9838
10065
|
const found = [];
|
|
9839
10066
|
const seen = /* @__PURE__ */ new Set();
|
|
9840
|
-
let current =
|
|
10067
|
+
let current = path23.resolve(projectRoot);
|
|
9841
10068
|
while (true) {
|
|
9842
|
-
const candidate =
|
|
10069
|
+
const candidate = path23.join(current, "CLAUDE.md");
|
|
9843
10070
|
if (!seen.has(candidate) && fs25.existsSync(candidate)) {
|
|
9844
10071
|
found.push(candidate);
|
|
9845
10072
|
seen.add(candidate);
|
|
9846
10073
|
}
|
|
9847
|
-
const parent =
|
|
10074
|
+
const parent = path23.dirname(current);
|
|
9848
10075
|
if (parent === current) break;
|
|
9849
10076
|
current = parent;
|
|
9850
10077
|
}
|
|
9851
|
-
const globalMd =
|
|
10078
|
+
const globalMd = path23.join(claudeConfigDir(homeDir), "CLAUDE.md");
|
|
9852
10079
|
if (!seen.has(globalMd) && fs25.existsSync(globalMd)) {
|
|
9853
10080
|
found.push(globalMd);
|
|
9854
10081
|
}
|
|
9855
10082
|
return found;
|
|
9856
10083
|
}
|
|
9857
|
-
function findMemoryMd(projectRoot, homeDir =
|
|
10084
|
+
function findMemoryMd(projectRoot, homeDir = os6.homedir(), alternateRoots = []) {
|
|
9858
10085
|
try {
|
|
9859
|
-
const projectsDir =
|
|
10086
|
+
const projectsDir = path23.join(claudeConfigDir(homeDir), "projects");
|
|
9860
10087
|
if (!fs25.existsSync(projectsDir)) return null;
|
|
9861
|
-
const rootStr =
|
|
10088
|
+
const rootStr = path23.resolve(projectRoot);
|
|
9862
10089
|
const candidateRoots = [rootStr];
|
|
9863
10090
|
try {
|
|
9864
10091
|
const realRoot = fs25.realpathSync.native(rootStr);
|
|
@@ -9866,12 +10093,12 @@ function findMemoryMd(projectRoot, homeDir = os5.homedir(), alternateRoots = [])
|
|
|
9866
10093
|
} catch {
|
|
9867
10094
|
}
|
|
9868
10095
|
for (const alternate of alternateRoots) {
|
|
9869
|
-
const resolved =
|
|
10096
|
+
const resolved = path23.resolve(alternate);
|
|
9870
10097
|
if (!candidateRoots.includes(resolved)) candidateRoots.push(resolved);
|
|
9871
10098
|
}
|
|
9872
10099
|
for (const root of candidateRoots) {
|
|
9873
10100
|
const expectedSlug = root.replace(/[^A-Za-z0-9]/g, "-");
|
|
9874
|
-
const candidate =
|
|
10101
|
+
const candidate = path23.join(projectsDir, expectedSlug, "memory", "MEMORY.md");
|
|
9875
10102
|
if (fs25.existsSync(candidate)) return candidate;
|
|
9876
10103
|
}
|
|
9877
10104
|
return null;
|
|
@@ -9879,17 +10106,17 @@ function findMemoryMd(projectRoot, homeDir = os5.homedir(), alternateRoots = [])
|
|
|
9879
10106
|
return null;
|
|
9880
10107
|
}
|
|
9881
10108
|
}
|
|
9882
|
-
function buildStats(projectRoot, homeDir =
|
|
10109
|
+
function buildStats(projectRoot, homeDir = os6.homedir(), alternateRoots = []) {
|
|
9883
10110
|
const claudeMds = findClaudeMdFiles(projectRoot, homeDir);
|
|
9884
10111
|
const claudeMdRows = [];
|
|
9885
10112
|
let claudeMdTotal = 0;
|
|
9886
10113
|
for (const p of claudeMds) {
|
|
9887
10114
|
const t = tok(p);
|
|
9888
10115
|
claudeMdTotal += t;
|
|
9889
|
-
const parentDir =
|
|
10116
|
+
const parentDir = path23.basename(path23.dirname(p));
|
|
9890
10117
|
const label = parentDir === ".claude" ? "~/.claude/CLAUDE.md" : (() => {
|
|
9891
10118
|
try {
|
|
9892
|
-
return
|
|
10119
|
+
return path23.relative(projectRoot, p);
|
|
9893
10120
|
} catch {
|
|
9894
10121
|
return p;
|
|
9895
10122
|
}
|
|
@@ -9953,7 +10180,7 @@ async function runContextStats(opts = {}) {
|
|
|
9953
10180
|
process.stdout.write("[--fix] No MEMORY.md found; nothing to prune.\n");
|
|
9954
10181
|
} else {
|
|
9955
10182
|
const memPath = result.memory_md_path;
|
|
9956
|
-
const pruneResult = pruneIndex(
|
|
10183
|
+
const pruneResult = pruneIndex(path23.dirname(memPath), { dryRun: true });
|
|
9957
10184
|
if (!pruneResult.changed || pruneResult.after === void 0) {
|
|
9958
10185
|
process.stdout.write("[--fix] MEMORY.md already clean; nothing to prune.\n");
|
|
9959
10186
|
} else {
|
|
@@ -10007,6 +10234,10 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
10007
10234
|
".idea",
|
|
10008
10235
|
".vscode"
|
|
10009
10236
|
]);
|
|
10237
|
+
function isIgnoredIndexPath(p) {
|
|
10238
|
+
const segments = foldPath(p).split(/[/\\]/);
|
|
10239
|
+
return segments.slice(0, -1).some((s) => SKIP_DIRS.has(s));
|
|
10240
|
+
}
|
|
10010
10241
|
var MAX_FILES_SCANNED = 2e4;
|
|
10011
10242
|
function walkProject(rootDir, opts = {}) {
|
|
10012
10243
|
const files = [];
|
|
@@ -10026,7 +10257,7 @@ function walkProject(rootDir, opts = {}) {
|
|
|
10026
10257
|
continue;
|
|
10027
10258
|
}
|
|
10028
10259
|
for (const entry of entries) {
|
|
10029
|
-
const full =
|
|
10260
|
+
const full = path24.join(dir, entry.name);
|
|
10030
10261
|
if (entry.isDirectory()) {
|
|
10031
10262
|
if (SKIP_DIRS.has(entry.name) || extraSkipDirs.includes(entry.name)) continue;
|
|
10032
10263
|
if (entry.name.startsWith(".") && entry.name !== ".") {
|
|
@@ -10108,7 +10339,7 @@ function fetchTopSymbols(limit, dbPath, rootDir) {
|
|
|
10108
10339
|
}
|
|
10109
10340
|
}
|
|
10110
10341
|
function buildProjectMap(rootDir = process.cwd(), opts = {}) {
|
|
10111
|
-
const root =
|
|
10342
|
+
const root = path24.resolve(rootDir);
|
|
10112
10343
|
const config = loadConfig();
|
|
10113
10344
|
const { files, languages, truncated } = walkProject(root, { excludeTests: config.repomap.exclude_tests });
|
|
10114
10345
|
const compact = opts.compact === true || files.length > config.repomap.compact_file_threshold;
|
|
@@ -10122,7 +10353,7 @@ function buildProjectMap(rootDir = process.cwd(), opts = {}) {
|
|
|
10122
10353
|
mtime = 0;
|
|
10123
10354
|
}
|
|
10124
10355
|
return { f, mtime };
|
|
10125
|
-
}).sort((a, b) => b.mtime - a.mtime).slice(0, compact ? 5 : 15).map((x) =>
|
|
10356
|
+
}).sort((a, b) => b.mtime - a.mtime).slice(0, compact ? 5 : 15).map((x) => path24.relative(root, x.f));
|
|
10126
10357
|
return {
|
|
10127
10358
|
rootDir: root,
|
|
10128
10359
|
fileCount: files.length,
|
|
@@ -10135,7 +10366,7 @@ function buildProjectMap(rootDir = process.cwd(), opts = {}) {
|
|
|
10135
10366
|
}
|
|
10136
10367
|
function formatProjectMap(map, compact = false) {
|
|
10137
10368
|
const lines = [];
|
|
10138
|
-
const rel =
|
|
10369
|
+
const rel = path24.basename(map.rootDir);
|
|
10139
10370
|
lines.push(`# Project map: ${rel}`);
|
|
10140
10371
|
lines.push(map.fileCountTruncated === true ? `Files: at least ${map.fileCount} (walk stopped at the ${MAX_FILES_SCANNED}-file cap)` : `Files: ${map.fileCount}`);
|
|
10141
10372
|
const langPairs = Object.entries(map.languages).sort((a, b) => b[1] - a[1]);
|
|
@@ -10164,7 +10395,7 @@ function formatProjectMap(map, compact = false) {
|
|
|
10164
10395
|
}
|
|
10165
10396
|
function mapLookupBytesSaved(map, emittedText) {
|
|
10166
10397
|
const referencedFiles = /* @__PURE__ */ new Set([
|
|
10167
|
-
...map.recentFiles.map((f) => normalizePath(
|
|
10398
|
+
...map.recentFiles.map((f) => normalizePath(path24.resolve(map.rootDir, f))),
|
|
10168
10399
|
...map.topSymbols.map((s) => normalizePath(s.filePath))
|
|
10169
10400
|
]);
|
|
10170
10401
|
const listingText = Array.from(referencedFiles).sort().join("\n");
|
|
@@ -10179,7 +10410,7 @@ function findMemSuggestionCandidates(projectRoot) {
|
|
|
10179
10410
|
const claudeMdFiles = findClaudeMdFiles(projectRoot);
|
|
10180
10411
|
const candidateFiles = new Set(claudeMdFiles);
|
|
10181
10412
|
for (const claudeMd of claudeMdFiles) {
|
|
10182
|
-
const agentsMd =
|
|
10413
|
+
const agentsMd = path24.join(path24.dirname(claudeMd), "AGENTS.md");
|
|
10183
10414
|
if (fs26.existsSync(agentsMd)) candidateFiles.add(agentsMd);
|
|
10184
10415
|
}
|
|
10185
10416
|
const suggestions = [];
|
|
@@ -10215,7 +10446,7 @@ function formatMemSuggestions(projectRoot) {
|
|
|
10215
10446
|
if (suggestions.length === 0) return "";
|
|
10216
10447
|
const lines = ["", "## mem suggestions"];
|
|
10217
10448
|
for (const s of suggestions) {
|
|
10218
|
-
const basename11 =
|
|
10449
|
+
const basename11 = path24.basename(s.path);
|
|
10219
10450
|
lines.push(
|
|
10220
10451
|
"Consider: mem import --from-md " + s.path + " # migrates " + s.count + " preference-shaped lines from " + basename11 + " as pending facts for review"
|
|
10221
10452
|
);
|
|
@@ -10260,15 +10491,15 @@ function scanAndRecord(text) {
|
|
|
10260
10491
|
// src/repomap.ts
|
|
10261
10492
|
init_define_import_meta_env();
|
|
10262
10493
|
import * as fs27 from "fs";
|
|
10263
|
-
import * as
|
|
10494
|
+
import * as path25 from "path";
|
|
10264
10495
|
function getTrackedFiles(cwd = process.cwd()) {
|
|
10265
10496
|
try {
|
|
10266
10497
|
let dir = cwd;
|
|
10267
10498
|
let onlyFile = "";
|
|
10268
10499
|
try {
|
|
10269
10500
|
if (fs27.statSync(cwd).isFile()) {
|
|
10270
|
-
dir =
|
|
10271
|
-
onlyFile =
|
|
10501
|
+
dir = path25.dirname(cwd);
|
|
10502
|
+
onlyFile = path25.basename(cwd);
|
|
10272
10503
|
}
|
|
10273
10504
|
} catch {
|
|
10274
10505
|
}
|
|
@@ -10277,7 +10508,7 @@ function getTrackedFiles(cwd = process.cwd()) {
|
|
|
10277
10508
|
if (result.exitCode !== 0 || !result.stdout) {
|
|
10278
10509
|
return [];
|
|
10279
10510
|
}
|
|
10280
|
-
return result.stdout.split("\n").filter((line) => line.trim().length > 0).map((rel) =>
|
|
10511
|
+
return result.stdout.split("\n").filter((line) => line.trim().length > 0).map((rel) => path25.join(dir, rel));
|
|
10281
10512
|
} catch {
|
|
10282
10513
|
return [];
|
|
10283
10514
|
}
|
|
@@ -10503,7 +10734,49 @@ function embeddedIpv4Candidates(host) {
|
|
|
10503
10734
|
return out;
|
|
10504
10735
|
}
|
|
10505
10736
|
|
|
10737
|
+
// src/line_regions.ts
|
|
10738
|
+
init_define_import_meta_env();
|
|
10739
|
+
function resolveLineRegions(symbols, totalLines, start, end) {
|
|
10740
|
+
if (symbols.length === 0) return [];
|
|
10741
|
+
const firstStart = symbols.reduce((m, s) => Math.min(m, s.lineStart), Number.POSITIVE_INFINITY);
|
|
10742
|
+
const regionFor = (line) => {
|
|
10743
|
+
let best = null;
|
|
10744
|
+
for (const s of symbols) {
|
|
10745
|
+
if (s.lineStart > line || s.lineEnd < line) continue;
|
|
10746
|
+
if (best === null || s.lineEnd - s.lineStart < best.lineEnd - best.lineStart) best = s;
|
|
10747
|
+
}
|
|
10748
|
+
if (best !== null) {
|
|
10749
|
+
return { kind: "symbol", label: `${best.kind} ${best.name}`, start: best.lineStart, end: best.lineEnd };
|
|
10750
|
+
}
|
|
10751
|
+
if (line < firstStart) return { kind: "preamble", label: "file preamble", start: 1, end: firstStart - 1 };
|
|
10752
|
+
let prev = null;
|
|
10753
|
+
let next = null;
|
|
10754
|
+
for (const s of symbols) {
|
|
10755
|
+
if (s.lineEnd < line && (prev === null || s.lineEnd > prev.lineEnd)) prev = s;
|
|
10756
|
+
if (s.lineStart > line && (next === null || s.lineStart < next.lineStart)) next = s;
|
|
10757
|
+
}
|
|
10758
|
+
const prevName = prev === null ? "start of file" : prev.name;
|
|
10759
|
+
return {
|
|
10760
|
+
kind: "gap",
|
|
10761
|
+
label: next === null ? `gap after ${prevName}` : `gap between ${prevName} and ${next.name}`,
|
|
10762
|
+
start: prev === null ? 1 : prev.lineEnd + 1,
|
|
10763
|
+
end: next === null ? totalLines : next.lineStart - 1
|
|
10764
|
+
};
|
|
10765
|
+
};
|
|
10766
|
+
const bySpan = /* @__PURE__ */ new Map();
|
|
10767
|
+
for (let line = start; line <= Math.min(end, totalLines); line++) {
|
|
10768
|
+
const r = regionFor(line);
|
|
10769
|
+
const key = `${r.start}-${r.end}`;
|
|
10770
|
+
const seen = bySpan.get(key);
|
|
10771
|
+
if (seen === void 0) bySpan.set(key, r);
|
|
10772
|
+
else if (!seen.label.split(" / ").includes(r.label)) seen.label = `${seen.label} / ${r.label}`;
|
|
10773
|
+
}
|
|
10774
|
+
const picked = [...bySpan.values()];
|
|
10775
|
+
return picked.filter((r) => !picked.some((o) => o !== r && o.start <= r.start && o.end >= r.end)).sort((a, b) => a.start - b.start || a.end - b.end);
|
|
10776
|
+
}
|
|
10777
|
+
|
|
10506
10778
|
export {
|
|
10779
|
+
claudeConfigDir,
|
|
10507
10780
|
indexedSourceText,
|
|
10508
10781
|
isVirtualIndexedPath,
|
|
10509
10782
|
virtualIndexedScopeNote,
|
|
@@ -10545,6 +10818,7 @@ export {
|
|
|
10545
10818
|
readServersJson,
|
|
10546
10819
|
serversOf,
|
|
10547
10820
|
setTokenGoatServer,
|
|
10821
|
+
isResidueServersJson,
|
|
10548
10822
|
dropEmptyServers,
|
|
10549
10823
|
hasManagedServer,
|
|
10550
10824
|
visualStudioUserMcpPath,
|
|
@@ -10586,6 +10860,7 @@ export {
|
|
|
10586
10860
|
classifyBashHint,
|
|
10587
10861
|
classifyEditHint,
|
|
10588
10862
|
applyHintTracking,
|
|
10863
|
+
logSuppressedDetection,
|
|
10589
10864
|
isSuppressionCategory,
|
|
10590
10865
|
meetsSavingsFloor,
|
|
10591
10866
|
getHintStatsSummary,
|
|
@@ -10641,6 +10916,7 @@ export {
|
|
|
10641
10916
|
getSessionId,
|
|
10642
10917
|
setTranscriptPath,
|
|
10643
10918
|
SESSIONS_SUBDIR,
|
|
10919
|
+
sessionsDir,
|
|
10644
10920
|
AGENT_SALT_MARKER,
|
|
10645
10921
|
sessionSidecarPath,
|
|
10646
10922
|
listSiblingSessionStates,
|
|
@@ -10662,6 +10938,7 @@ export {
|
|
|
10662
10938
|
loadSessionCache,
|
|
10663
10939
|
computeAdaptiveBudget,
|
|
10664
10940
|
buildManifestWithCount,
|
|
10941
|
+
LARGE_SYMBOL_LINE_THRESHOLD,
|
|
10665
10942
|
buildLineDiff,
|
|
10666
10943
|
SKILLS_OUTPUT_SUBDIR,
|
|
10667
10944
|
skillOutputsDir,
|
|
@@ -10685,7 +10962,6 @@ export {
|
|
|
10685
10962
|
visionTokensSavedByText,
|
|
10686
10963
|
canShrinkFormat,
|
|
10687
10964
|
formatShrinkSummary,
|
|
10688
|
-
isImagePath,
|
|
10689
10965
|
ImageDecodeError,
|
|
10690
10966
|
probeImageMeta,
|
|
10691
10967
|
imageQualifiesForShrink,
|
|
@@ -10700,6 +10976,7 @@ export {
|
|
|
10700
10976
|
searchEvidenceSemantically,
|
|
10701
10977
|
buildDeltaCapsule,
|
|
10702
10978
|
foldDetail,
|
|
10979
|
+
pruneUnembeddableChunks,
|
|
10703
10980
|
removeFileFromIndex,
|
|
10704
10981
|
pruneDeletedFiles,
|
|
10705
10982
|
pruneBlockedRoot,
|
|
@@ -10708,6 +10985,7 @@ export {
|
|
|
10708
10985
|
findOrphanedChunkPaths,
|
|
10709
10986
|
pruneOrphanedChunks,
|
|
10710
10987
|
recordKnownRootThrottled,
|
|
10988
|
+
findTopIndexedProjects,
|
|
10711
10989
|
cmdReclaimIndex,
|
|
10712
10990
|
applyIndexingPriority,
|
|
10713
10991
|
WORKER_HEARTBEAT_STALE_MS,
|
|
@@ -10722,6 +11000,7 @@ export {
|
|
|
10722
11000
|
runDetachedWorkerDaemon,
|
|
10723
11001
|
appendDirtyPath,
|
|
10724
11002
|
enqueueDirtyPathSafe,
|
|
11003
|
+
enqueueDirtyPathsSafe,
|
|
10725
11004
|
foldingEnabled,
|
|
10726
11005
|
foldDelivery,
|
|
10727
11006
|
OUTLINE_MIN_HEADINGS,
|
|
@@ -10729,6 +11008,7 @@ export {
|
|
|
10729
11008
|
planMarkdownOutline,
|
|
10730
11009
|
planSourceSkeleton,
|
|
10731
11010
|
isStructuralRewriteAccepted,
|
|
11011
|
+
escapeHintName,
|
|
10732
11012
|
confirmAndApply,
|
|
10733
11013
|
findContentDuplicates,
|
|
10734
11014
|
auditClaudeMd,
|
|
@@ -10737,6 +11017,7 @@ export {
|
|
|
10737
11017
|
buildStats,
|
|
10738
11018
|
runContextStats,
|
|
10739
11019
|
SKIP_DIRS,
|
|
11020
|
+
isIgnoredIndexPath,
|
|
10740
11021
|
MAX_FILES_SCANNED,
|
|
10741
11022
|
walkProject,
|
|
10742
11023
|
buildProjectMap,
|
|
@@ -10756,5 +11037,6 @@ export {
|
|
|
10756
11037
|
parseIpv6Groups,
|
|
10757
11038
|
isPrivateIpv4Octets,
|
|
10758
11039
|
isPrivateIpv6Groups,
|
|
10759
|
-
metadataEndpointRefusal
|
|
11040
|
+
metadataEndpointRefusal,
|
|
11041
|
+
resolveLineRegions
|
|
10760
11042
|
};
|