token-goat 2.9.18 → 2.9.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/{token-goat-chunk-V3ZWBEHM.mjs → token-goat-chunk-2GTB5RBZ.mjs} +365 -26
- package/dist/{token-goat-chunk-ARNRFR6M.mjs → token-goat-chunk-3W5P673V.mjs} +1 -1
- package/dist/{token-goat-chunk-YQZE5NS6.mjs → token-goat-chunk-62VJ47WN.mjs} +6 -6
- package/dist/{token-goat-chunk-CTOYJ232.mjs → token-goat-chunk-7DGTVBWP.mjs} +45 -8
- package/dist/{token-goat-chunk-5MMJAJF3.mjs → token-goat-chunk-AVRD7SBO.mjs} +60 -8
- package/dist/{token-goat-chunk-7VLWNQDQ.mjs → token-goat-chunk-BTKDZW7F.mjs} +1 -1
- package/dist/{token-goat-chunk-LMFO66YD.mjs → token-goat-chunk-DA43OE7L.mjs} +1 -1
- package/dist/{token-goat-chunk-6LGVCCMY.mjs → token-goat-chunk-DA6TIDLI.mjs} +21 -16
- package/dist/{token-goat-chunk-UENEL6KX.mjs → token-goat-chunk-DHIXE6GV.mjs} +2 -2
- package/dist/{token-goat-chunk-W3E65QOW.mjs → token-goat-chunk-EP7TCWHM.mjs} +304 -46
- package/dist/{token-goat-chunk-HIZXQVNU.mjs → token-goat-chunk-EXLLLIC3.mjs} +4 -4
- package/dist/{token-goat-chunk-H3FONFBO.mjs → token-goat-chunk-F24NE3FO.mjs} +3 -3
- package/dist/{token-goat-chunk-LA3O4JGD.mjs → token-goat-chunk-FJZ5MZK7.mjs} +3 -3
- package/dist/{token-goat-chunk-BFEWOMA3.mjs → token-goat-chunk-FQYMZASI.mjs} +632 -352
- package/dist/{token-goat-chunk-OWSJJY5R.mjs → token-goat-chunk-FX5DHKGF.mjs} +5 -5
- package/dist/{token-goat-chunk-K4DG6EEF.mjs → token-goat-chunk-GYC34EGE.mjs} +7 -8
- package/dist/{token-goat-chunk-WFX2V3OF.mjs → token-goat-chunk-HPMMLV37.mjs} +358 -116
- package/dist/{token-goat-chunk-I67CPTDD.mjs → token-goat-chunk-IBTFGZBX.mjs} +19 -14
- package/dist/{token-goat-chunk-L7VCX33I.mjs → token-goat-chunk-JNIFSVWM.mjs} +36 -10
- package/dist/{token-goat-chunk-EPXNOKIV.mjs → token-goat-chunk-KWM6KBPA.mjs} +2 -2
- package/dist/{token-goat-chunk-BWCRC23L.mjs → token-goat-chunk-L2UOJBV4.mjs} +21 -20
- package/dist/{token-goat-chunk-52IMXJ2L.mjs → token-goat-chunk-LACH3WKS.mjs} +4 -4
- package/dist/token-goat-chunk-MU6I4IDI.mjs +337 -0
- package/dist/{token-goat-chunk-GRI6HWX3.mjs → token-goat-chunk-NJKNAC37.mjs} +1 -1
- package/dist/{token-goat-chunk-ERTXEKB6.mjs → token-goat-chunk-NMTKNYGF.mjs} +2 -0
- package/dist/{token-goat-chunk-34OUJ2IE.mjs → token-goat-chunk-NYFYIRG3.mjs} +12 -12
- package/dist/{token-goat-chunk-4EXFN2AW.mjs → token-goat-chunk-OZRSREKR.mjs} +2 -2
- package/dist/token-goat-chunk-P46RTOAQ.mjs +27 -0
- package/dist/{token-goat-chunk-QML7ITHJ.mjs → token-goat-chunk-PIAESKVU.mjs} +139 -23
- package/dist/{token-goat-chunk-HPOFR6QX.mjs → token-goat-chunk-S7RFIX4Y.mjs} +180 -50
- package/dist/{token-goat-chunk-CA6V36KW.mjs → token-goat-chunk-SOKAPOOC.mjs} +23 -4
- package/dist/token-goat-chunk-VJQBYSXG.mjs +20 -0
- package/dist/{token-goat-chunk-LTYWLKAG.mjs → token-goat-chunk-WKO5TVNA.mjs} +922 -611
- package/dist/{token-goat-chunk-QIDLJGXZ.mjs → token-goat-chunk-ZGQXRYKE.mjs} +29 -5
- package/dist/token-goat-hook.mjs +12 -12
- package/dist/token-goat.core.mjs +21 -20
- package/docs/cli.md +2 -1
- package/docs/security.md +1 -1
- package/package.json +1 -1
- package/dist/token-goat-chunk-2KTZ6J7T.mjs +0 -173
- package/dist/token-goat-chunk-RHHFEXKF.mjs +0 -93
|
@@ -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,32 +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
|
-
const resolved =
|
|
5629
|
-
|
|
5630
|
-
|
|
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);
|
|
5631
5766
|
} catch {
|
|
5632
5767
|
return;
|
|
5633
5768
|
}
|
|
@@ -5649,9 +5784,9 @@ function getDirtyPaths() {
|
|
|
5649
5784
|
function preCompactIndexHandler(_event) {
|
|
5650
5785
|
const paths = getDirtyPaths();
|
|
5651
5786
|
if (paths.length > 0) {
|
|
5652
|
-
const sidecar =
|
|
5787
|
+
const sidecar = path16.join(dataDir(), "queue", "pending.txt");
|
|
5653
5788
|
try {
|
|
5654
|
-
ensureDirSync(
|
|
5789
|
+
ensureDirSync(path16.dirname(sidecar));
|
|
5655
5790
|
atomicWriteBytes(sidecar, Buffer.from(`${paths.join("\n")}
|
|
5656
5791
|
`, "utf8"));
|
|
5657
5792
|
} catch {
|
|
@@ -5664,7 +5799,7 @@ registerHook("pre_compact", preCompactIndexHandler, { advisory: true });
|
|
|
5664
5799
|
// src/hooks_read.ts
|
|
5665
5800
|
init_define_import_meta_env();
|
|
5666
5801
|
import * as fs23 from "node:fs";
|
|
5667
|
-
import * as
|
|
5802
|
+
import * as path21 from "node:path";
|
|
5668
5803
|
|
|
5669
5804
|
// src/hint_stats.ts
|
|
5670
5805
|
init_define_import_meta_env();
|
|
@@ -5690,6 +5825,13 @@ var KNOWN_CORRELATOR_PLACEHOLDERS = /* @__PURE__ */ new Set([
|
|
|
5690
5825
|
"table_name",
|
|
5691
5826
|
"name"
|
|
5692
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
|
+
}
|
|
5693
5835
|
function extractPathCorrelator(text) {
|
|
5694
5836
|
const m = /(?:[A-Za-z]:[\\/]|\.{1,2}\/|\/)[^\s"'`]+/.exec(text);
|
|
5695
5837
|
if (m === null) return null;
|
|
@@ -5750,12 +5892,15 @@ function resetSuppressionStreak(category) {
|
|
|
5750
5892
|
}
|
|
5751
5893
|
function applyHintTracking(event, output, classify) {
|
|
5752
5894
|
if (output.hookType !== "context") return output;
|
|
5753
|
-
const
|
|
5895
|
+
const classified = classify(output.context);
|
|
5896
|
+
const { category } = classified;
|
|
5897
|
+
const correlator = output.correlators === void 0 ? classified.correlator : joinCorrelators(output.correlators);
|
|
5754
5898
|
const compensateSelfResolve = event.eventName === "pre_tool_use";
|
|
5755
5899
|
if (shouldSuppress(category, event.sessionId)) {
|
|
5756
5900
|
const occasion = bumpSuppressionStreak(category);
|
|
5757
5901
|
const thresholds = loadConfig().hints.backoff_thresholds;
|
|
5758
5902
|
if (!isProbeOccasion(occasion, thresholds)) {
|
|
5903
|
+
logSuppressedDetection(category, event.sessionId, correlator);
|
|
5759
5904
|
return passOutput();
|
|
5760
5905
|
}
|
|
5761
5906
|
logHintEmission(category, event.sessionId, correlator, compensateSelfResolve, output.context.length);
|
|
@@ -5765,15 +5910,23 @@ function applyHintTracking(event, output, classify) {
|
|
|
5765
5910
|
logHintEmission(category, event.sessionId, correlator, compensateSelfResolve, output.context.length);
|
|
5766
5911
|
return output;
|
|
5767
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
|
+
}
|
|
5768
5922
|
function logHintEmission(category, sessionId, correlator, compensateSelfResolve = false, bytesEmitted = null) {
|
|
5769
5923
|
try {
|
|
5770
5924
|
const db = getDb(globalDbPath());
|
|
5771
5925
|
const resolved = correlator === null ? 1 : 0;
|
|
5772
|
-
const actedOn = resolved === 1 && isSuppressionCategory(category) ? 1 : 0;
|
|
5773
5926
|
const window = ACTED_ON_WINDOW + (compensateSelfResolve ? 1 : 0);
|
|
5774
5927
|
db.prepare(
|
|
5775
|
-
`INSERT INTO hint_emissions (category, session_id, harness, correlator, emitted_at, resolved, acted_on, calls_remaining, bytes_emitted)
|
|
5776
|
-
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)`
|
|
5777
5930
|
).run({
|
|
5778
5931
|
category,
|
|
5779
5932
|
sessionId,
|
|
@@ -5781,9 +5934,9 @@ function logHintEmission(category, sessionId, correlator, compensateSelfResolve
|
|
|
5781
5934
|
correlator,
|
|
5782
5935
|
emittedAt: Date.now(),
|
|
5783
5936
|
resolved,
|
|
5784
|
-
actedOn,
|
|
5785
5937
|
callsRemaining: correlator === null ? 0 : window,
|
|
5786
|
-
bytesEmitted
|
|
5938
|
+
bytesEmitted,
|
|
5939
|
+
observable: correlator === null ? 0 : 1
|
|
5787
5940
|
});
|
|
5788
5941
|
} catch {
|
|
5789
5942
|
}
|
|
@@ -5803,7 +5956,8 @@ function commandMentionsCorrelator(command, correlator) {
|
|
|
5803
5956
|
}
|
|
5804
5957
|
var SUPPRESSION_HINT_CATEGORIES = /* @__PURE__ */ new Set([
|
|
5805
5958
|
"read_reread_dedup",
|
|
5806
|
-
"edit_reread_suggest"
|
|
5959
|
+
"edit_reread_suggest",
|
|
5960
|
+
"read_structural_nav"
|
|
5807
5961
|
]);
|
|
5808
5962
|
function isSuppressionCategory(category) {
|
|
5809
5963
|
return SUPPRESSION_HINT_CATEGORIES.has(category);
|
|
@@ -5823,14 +5977,13 @@ function eventTargetText(event) {
|
|
|
5823
5977
|
function isDefiance(correlator, target) {
|
|
5824
5978
|
if (target === "") return false;
|
|
5825
5979
|
if (TOKEN_GOAT_INVOCATION_RE.test(target)) return false;
|
|
5826
|
-
return commandMentionsCorrelator(target,
|
|
5980
|
+
return splitCorrelators(correlator).some((c) => commandMentionsCorrelator(target, c));
|
|
5827
5981
|
}
|
|
5828
5982
|
function isActedOn(category, correlator, command) {
|
|
5829
5983
|
if (!TOKEN_GOAT_INVOCATION_RE.test(command)) return false;
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
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;
|
|
5834
5987
|
}
|
|
5835
5988
|
function resolvePendingHintsForEvent(event) {
|
|
5836
5989
|
try {
|
|
@@ -5840,8 +5993,7 @@ function resolvePendingHintsForEvent(event) {
|
|
|
5840
5993
|
const pending = db.prepare(`SELECT id, category, correlator, calls_remaining FROM hint_emissions WHERE session_id = ? AND resolved = 0`).all(event.sessionId);
|
|
5841
5994
|
for (const row of pending) {
|
|
5842
5995
|
if (!isHintCategory(row.category) || row.correlator === null) {
|
|
5843
|
-
|
|
5844
|
-
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);
|
|
5845
5997
|
continue;
|
|
5846
5998
|
}
|
|
5847
5999
|
if (isSuppressionCategory(row.category)) {
|
|
@@ -5886,11 +6038,26 @@ registerHook(
|
|
|
5886
6038
|
function categoryStats(category) {
|
|
5887
6039
|
const db = getDb(globalDbPath());
|
|
5888
6040
|
const row = db.prepare(
|
|
5889
|
-
|
|
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,
|
|
5890
6057
|
SUM(CASE WHEN bytes_emitted IS NULL THEN 1 ELSE 0 END) AS legacyEmissions
|
|
5891
6058
|
FROM hint_emissions WHERE category = ? AND harness = ?`
|
|
5892
6059
|
).get(category, getHarnessName());
|
|
5893
|
-
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 };
|
|
5894
6061
|
}
|
|
5895
6062
|
function shouldSuppress(category, _sessionId2) {
|
|
5896
6063
|
try {
|
|
@@ -5898,6 +6065,7 @@ function shouldSuppress(category, _sessionId2) {
|
|
|
5898
6065
|
const { emitted, actedOn } = categoryStats(category);
|
|
5899
6066
|
if (emitted < cfg.min_sample_size) return false;
|
|
5900
6067
|
const pct = 100 * (actedOn ?? 0) / emitted;
|
|
6068
|
+
if (isSuppressionCategory(category)) return 100 - pct > cfg.defiance_threshold_pct;
|
|
5901
6069
|
return pct < cfg.suppress_threshold_pct;
|
|
5902
6070
|
} catch {
|
|
5903
6071
|
return false;
|
|
@@ -5918,7 +6086,7 @@ function manualMarks(category) {
|
|
|
5918
6086
|
function getHintStatsSummary() {
|
|
5919
6087
|
const probeThresholds = loadConfig().hints.backoff_thresholds.filter((t) => t > 0);
|
|
5920
6088
|
return HINT_CATEGORIES.map((category) => {
|
|
5921
|
-
const { emitted, actedOn, bytesEmitted, legacyEmissions } = categoryStats(category);
|
|
6089
|
+
const { emitted, actedOn, unobservable, detected, bytesEmitted, legacyEmissions } = categoryStats(category);
|
|
5922
6090
|
const marks = manualMarks(category);
|
|
5923
6091
|
const suppressed = shouldSuppress(category, "");
|
|
5924
6092
|
return {
|
|
@@ -5926,6 +6094,8 @@ function getHintStatsSummary() {
|
|
|
5926
6094
|
emitted,
|
|
5927
6095
|
actedOn: actedOn ?? 0,
|
|
5928
6096
|
efficacyPct: emitted === 0 ? null : Math.round(1e3 * (actedOn ?? 0) / emitted) / 10,
|
|
6097
|
+
unobservable,
|
|
6098
|
+
detected,
|
|
5929
6099
|
suppressed,
|
|
5930
6100
|
suppressionPermanent: suppressed && probeThresholds.length === 0,
|
|
5931
6101
|
manualEffective: marks.effective,
|
|
@@ -6064,7 +6234,7 @@ function planServedElisions(rows, bodies) {
|
|
|
6064
6234
|
// src/compact.ts
|
|
6065
6235
|
init_define_import_meta_env();
|
|
6066
6236
|
import * as fs18 from "node:fs";
|
|
6067
|
-
import * as
|
|
6237
|
+
import * as path17 from "node:path";
|
|
6068
6238
|
|
|
6069
6239
|
// src/resident_context.ts
|
|
6070
6240
|
init_define_import_meta_env();
|
|
@@ -6157,9 +6327,9 @@ function collectInvokedSkills(acc, record) {
|
|
|
6157
6327
|
function skillName(skill) {
|
|
6158
6328
|
const name = skill["name"];
|
|
6159
6329
|
if (typeof name === "string" && name.trim() !== "") return name.trim();
|
|
6160
|
-
const
|
|
6161
|
-
if (typeof
|
|
6162
|
-
const segments =
|
|
6330
|
+
const path26 = skill["path"];
|
|
6331
|
+
if (typeof path26 !== "string") return null;
|
|
6332
|
+
const segments = path26.split(/[\\/]/).filter((part) => part !== "");
|
|
6163
6333
|
const last = segments[segments.length - 1];
|
|
6164
6334
|
return last === void 0 || last === "" ? null : last;
|
|
6165
6335
|
}
|
|
@@ -6501,7 +6671,7 @@ function inferSessionGoal(cache, maxTokens = 80) {
|
|
|
6501
6671
|
const dirCounts = new Counter();
|
|
6502
6672
|
for (const fpath of editedPaths) {
|
|
6503
6673
|
try {
|
|
6504
|
-
let parent =
|
|
6674
|
+
let parent = path17.dirname(fpath);
|
|
6505
6675
|
if (parent === ".") {
|
|
6506
6676
|
parent = "root";
|
|
6507
6677
|
} else if (parent.startsWith("./")) {
|
|
@@ -6579,11 +6749,11 @@ function isNoisePath(inputPath) {
|
|
|
6579
6749
|
}
|
|
6580
6750
|
function findLatestSessionId() {
|
|
6581
6751
|
try {
|
|
6582
|
-
const
|
|
6583
|
-
if (!fs18.existsSync(
|
|
6752
|
+
const sessionsDir2 = path17.join(tokenGoatHome(), "sessions");
|
|
6753
|
+
if (!fs18.existsSync(sessionsDir2)) {
|
|
6584
6754
|
return null;
|
|
6585
6755
|
}
|
|
6586
|
-
const files = fs18.readdirSync(
|
|
6756
|
+
const files = fs18.readdirSync(sessionsDir2);
|
|
6587
6757
|
const jsonFiles = files.filter((f) => f.endsWith(".json") && !f.includes(AGENT_SALT_MARKER));
|
|
6588
6758
|
if (jsonFiles.length === 0) {
|
|
6589
6759
|
return null;
|
|
@@ -6593,9 +6763,9 @@ function findLatestSessionId() {
|
|
|
6593
6763
|
return null;
|
|
6594
6764
|
}
|
|
6595
6765
|
let latestFile = firstFile;
|
|
6596
|
-
let latestMtime = fs18.statSync(
|
|
6766
|
+
let latestMtime = fs18.statSync(path17.join(sessionsDir2, firstFile)).mtimeMs;
|
|
6597
6767
|
for (const file of jsonFiles) {
|
|
6598
|
-
const mtime = fs18.statSync(
|
|
6768
|
+
const mtime = fs18.statSync(path17.join(sessionsDir2, file)).mtimeMs;
|
|
6599
6769
|
if (mtime > latestMtime) {
|
|
6600
6770
|
latestFile = file;
|
|
6601
6771
|
latestMtime = mtime;
|
|
@@ -6617,28 +6787,28 @@ function eventCount(cache) {
|
|
|
6617
6787
|
function writeSessionManifest(projectHash, sessionId, manifestJson) {
|
|
6618
6788
|
const safeSessionId = sessionFileStem(sessionId);
|
|
6619
6789
|
if (!safeSessionId) return;
|
|
6620
|
-
const
|
|
6621
|
-
if (!fs18.existsSync(
|
|
6622
|
-
ensureDirSync(
|
|
6790
|
+
const sessionsDir2 = path17.join(dataDir(), "projects", projectHash, "sessions");
|
|
6791
|
+
if (!fs18.existsSync(sessionsDir2)) {
|
|
6792
|
+
ensureDirSync(sessionsDir2);
|
|
6623
6793
|
}
|
|
6624
|
-
const dest =
|
|
6794
|
+
const dest = path17.join(sessionsDir2, `${safeSessionId}.json`);
|
|
6625
6795
|
atomicWriteText(dest, JSON.stringify(manifestJson));
|
|
6626
6796
|
}
|
|
6627
6797
|
function readAllSessionManifests(projectHash, maxAgeSecs = 3600) {
|
|
6628
|
-
const
|
|
6629
|
-
if (!fs18.existsSync(
|
|
6798
|
+
const sessionsDir2 = path17.join(dataDir(), "projects", projectHash, "sessions");
|
|
6799
|
+
if (!fs18.existsSync(sessionsDir2)) {
|
|
6630
6800
|
return [];
|
|
6631
6801
|
}
|
|
6632
6802
|
const now = Date.now() / 1e3;
|
|
6633
6803
|
const results = [];
|
|
6634
6804
|
try {
|
|
6635
|
-
const files = fs18.readdirSync(
|
|
6805
|
+
const files = fs18.readdirSync(sessionsDir2);
|
|
6636
6806
|
for (const file of files) {
|
|
6637
6807
|
if (!file.endsWith(".json")) {
|
|
6638
6808
|
continue;
|
|
6639
6809
|
}
|
|
6640
6810
|
try {
|
|
6641
|
-
const fullPath =
|
|
6811
|
+
const fullPath = path17.join(sessionsDir2, file);
|
|
6642
6812
|
const stat2 = fs18.statSync(fullPath);
|
|
6643
6813
|
if (now - stat2.mtimeMs / 1e3 > maxAgeSecs) {
|
|
6644
6814
|
try {
|
|
@@ -6831,6 +7001,7 @@ import * as fs19 from "node:fs";
|
|
|
6831
7001
|
|
|
6832
7002
|
// src/hints/file_type_handler.ts
|
|
6833
7003
|
init_define_import_meta_env();
|
|
7004
|
+
var LARGE_SYMBOL_LINE_THRESHOLD = 200;
|
|
6834
7005
|
var FILE_TYPE_THRESHOLDS = {
|
|
6835
7006
|
pdf: 0,
|
|
6836
7007
|
// always intercept (any size)
|
|
@@ -7523,17 +7694,17 @@ function buildPackageManifestHint(options) {
|
|
|
7523
7694
|
return null;
|
|
7524
7695
|
}
|
|
7525
7696
|
}
|
|
7526
|
-
function _sanitizeHintPath(
|
|
7527
|
-
if (typeof
|
|
7697
|
+
function _sanitizeHintPath(path26) {
|
|
7698
|
+
if (typeof path26 !== "string") {
|
|
7528
7699
|
return "???";
|
|
7529
7700
|
}
|
|
7530
|
-
return
|
|
7701
|
+
return path26.replace(/[\x00]/g, "").slice(0, 200);
|
|
7531
7702
|
}
|
|
7532
7703
|
|
|
7533
7704
|
// src/doc_compact.ts
|
|
7534
7705
|
init_define_import_meta_env();
|
|
7535
7706
|
import * as fs20 from "fs";
|
|
7536
|
-
import * as
|
|
7707
|
+
import * as path18 from "path";
|
|
7537
7708
|
var defaultSentencesPerSection = 2;
|
|
7538
7709
|
var headerPrefix = "<!-- token-goat doc-compact source-hash:";
|
|
7539
7710
|
var headerRegex = /^<!-- token-goat doc-compact source-hash:(\S+) source:(.+?) -->\r?$/;
|
|
@@ -7547,14 +7718,14 @@ function sourceHash(filePath) {
|
|
|
7547
7718
|
}
|
|
7548
7719
|
function _compactSlug(absPathStr) {
|
|
7549
7720
|
const h = fingerprintContent(foldPath(absPathStr)).slice(0, 12);
|
|
7550
|
-
const ext =
|
|
7551
|
-
const stem =
|
|
7721
|
+
const ext = path18.extname(absPathStr);
|
|
7722
|
+
const stem = path18.basename(absPathStr, ext);
|
|
7552
7723
|
const safeStem = sanitizeIdForFilename(stem, 32);
|
|
7553
7724
|
return `${h}_${safeStem}`;
|
|
7554
7725
|
}
|
|
7555
7726
|
function compactPathFor(sourcePath) {
|
|
7556
7727
|
const abs = resolveIndexPath(sourcePath);
|
|
7557
|
-
return
|
|
7728
|
+
return path18.join(dataDir(), compactSubdir, `${_compactSlug(abs)}.md`);
|
|
7558
7729
|
}
|
|
7559
7730
|
function readCompactHeader(compactPath) {
|
|
7560
7731
|
try {
|
|
@@ -7603,13 +7774,13 @@ function readCompactBody(compactPath) {
|
|
|
7603
7774
|
}
|
|
7604
7775
|
}
|
|
7605
7776
|
function writeCompact(compactPath, sourcePath, compactBody, sourceRel) {
|
|
7606
|
-
const srcPath =
|
|
7777
|
+
const srcPath = path18.resolve(sourcePath);
|
|
7607
7778
|
const sha = sourceHash(srcPath);
|
|
7608
|
-
const displayRel = sourceRel ||
|
|
7779
|
+
const displayRel = sourceRel || path18.basename(srcPath);
|
|
7609
7780
|
const header = `${headerPrefix}${sha} source:${displayRel} -->
|
|
7610
7781
|
`;
|
|
7611
7782
|
const fullText = header + compactBody.trimStart();
|
|
7612
|
-
const dir =
|
|
7783
|
+
const dir = path18.dirname(compactPath);
|
|
7613
7784
|
if (!fs20.existsSync(dir)) {
|
|
7614
7785
|
ensureDirSync(dir);
|
|
7615
7786
|
}
|
|
@@ -7746,12 +7917,12 @@ function compactDoc(filePath, heading) {
|
|
|
7746
7917
|
init_define_import_meta_env();
|
|
7747
7918
|
import crypto2 from "node:crypto";
|
|
7748
7919
|
import fs21 from "node:fs";
|
|
7749
|
-
import
|
|
7920
|
+
import path19 from "node:path";
|
|
7750
7921
|
var MAX_ENTRIES = 500;
|
|
7751
7922
|
var MAX_TEXT_BYTES = 128 * 1024;
|
|
7752
7923
|
var CACHE_FILE = "workspace-evidence.json";
|
|
7753
7924
|
function cachePath() {
|
|
7754
|
-
return
|
|
7925
|
+
return path19.join(dataDir(), CACHE_FILE);
|
|
7755
7926
|
}
|
|
7756
7927
|
function hash(text) {
|
|
7757
7928
|
return crypto2.createHash("sha256").update(text).digest("hex");
|
|
@@ -7874,7 +8045,7 @@ ${changed.map((entry) => `- ${displaySafePath(entry.source)} (use a fresh surgic
|
|
|
7874
8045
|
// src/notebook_compact.ts
|
|
7875
8046
|
init_define_import_meta_env();
|
|
7876
8047
|
import * as fs22 from "node:fs";
|
|
7877
|
-
import * as
|
|
8048
|
+
import * as path20 from "node:path";
|
|
7878
8049
|
var NB_STRIP_MIN_SAVINGS = 4096;
|
|
7879
8050
|
function stripNotebook(nbDict) {
|
|
7880
8051
|
const cells = [];
|
|
@@ -7894,14 +8065,14 @@ function stripNotebook(nbDict) {
|
|
|
7894
8065
|
var SIDECAR_DEFAULT_MAX_COUNT = 200;
|
|
7895
8066
|
var SIDECAR_DEFAULT_MAX_AGE_MS = 24 * 3600 * 1e3;
|
|
7896
8067
|
function pruneSidecars(cacheRoot, maxCount = SIDECAR_DEFAULT_MAX_COUNT, maxAgeMs = SIDECAR_DEFAULT_MAX_AGE_MS) {
|
|
7897
|
-
const nbStripDir =
|
|
8068
|
+
const nbStripDir = path20.join(cacheRoot, "nb_strip");
|
|
7898
8069
|
let removed = 0;
|
|
7899
8070
|
try {
|
|
7900
8071
|
if (!fs22.existsSync(nbStripDir)) return 0;
|
|
7901
8072
|
const cutoff = Date.now() - maxAgeMs;
|
|
7902
8073
|
const kept = [];
|
|
7903
8074
|
for (const entry of fs22.readdirSync(nbStripDir)) {
|
|
7904
|
-
const dir =
|
|
8075
|
+
const dir = path20.join(nbStripDir, entry);
|
|
7905
8076
|
let mtime;
|
|
7906
8077
|
try {
|
|
7907
8078
|
mtime = fs22.statSync(dir).mtimeMs;
|
|
@@ -7937,8 +8108,8 @@ function pruneSidecars(cacheRoot, maxCount = SIDECAR_DEFAULT_MAX_COUNT, maxAgeMs
|
|
|
7937
8108
|
}
|
|
7938
8109
|
function getOrCreateSidecar(rawBytes, cacheRoot, opts = {}) {
|
|
7939
8110
|
const sha = fingerprintContent(rawBytes);
|
|
7940
|
-
const sidecarDir =
|
|
7941
|
-
const sidecarPath =
|
|
8111
|
+
const sidecarDir = path20.join(cacheRoot, "nb_strip", sha);
|
|
8112
|
+
const sidecarPath = path20.join(sidecarDir, "stripped.ipynb");
|
|
7942
8113
|
if (fs22.existsSync(sidecarPath)) {
|
|
7943
8114
|
return [sidecarPath, false];
|
|
7944
8115
|
}
|
|
@@ -8044,10 +8215,10 @@ var COMMENT_SYNTAX = new Map([
|
|
|
8044
8215
|
...["py", "rb", "sh", "bash", "zsh", "pl", "r"].map((ext) => [ext, HASH]),
|
|
8045
8216
|
...["sql", "lua", "hs"].map((ext) => [ext, DOUBLE_DASH])
|
|
8046
8217
|
]);
|
|
8047
|
-
function commentSyntaxFor(
|
|
8048
|
-
const dot =
|
|
8218
|
+
function commentSyntaxFor(path26) {
|
|
8219
|
+
const dot = path26.lastIndexOf(".");
|
|
8049
8220
|
if (dot < 0) return null;
|
|
8050
|
-
return COMMENT_SYNTAX.get(
|
|
8221
|
+
return COMMENT_SYNTAX.get(path26.slice(dot + 1).toLowerCase()) ?? null;
|
|
8051
8222
|
}
|
|
8052
8223
|
function planCommentFolds(rows, syntax, commentKeep, commentMinBlock, occupied) {
|
|
8053
8224
|
if (syntax === null || rows.length === 0 || commentKeep < 1 || commentMinBlock < 1) return [];
|
|
@@ -8156,8 +8327,8 @@ function planProseFolds(rows, claimed) {
|
|
|
8156
8327
|
}
|
|
8157
8328
|
return folds;
|
|
8158
8329
|
}
|
|
8159
|
-
function foldDetail(
|
|
8160
|
-
const head = `${
|
|
8330
|
+
function foldDetail(path26, folds, maxLen = MAX_FOLD_DETAIL) {
|
|
8331
|
+
const head = `${path26}::`;
|
|
8161
8332
|
const ids = folds.map((f) => f.kind === "body" ? f.name : `#${f.firstLine}-${f.lastLine}`);
|
|
8162
8333
|
const budget = maxLen - head.length - FOLD_DETAIL_SUFFIX_BUDGET;
|
|
8163
8334
|
const kept = [];
|
|
@@ -8175,7 +8346,7 @@ function foldDetail(path24, folds, maxLen = MAX_FOLD_DETAIL) {
|
|
|
8175
8346
|
|
|
8176
8347
|
// src/fold_delivery.ts
|
|
8177
8348
|
init_define_import_meta_env();
|
|
8178
|
-
import { readFileSync as readFileSync13, statSync as
|
|
8349
|
+
import { readFileSync as readFileSync13, statSync as statSync13 } from "node:fs";
|
|
8179
8350
|
|
|
8180
8351
|
// src/indexed_source.ts
|
|
8181
8352
|
init_define_import_meta_env();
|
|
@@ -8230,7 +8401,7 @@ function commentFoldNotice(firstLine, lastLine, shownPath) {
|
|
|
8230
8401
|
var FOLD_SPAN_PARSE_MAX_BYTES = 4e5;
|
|
8231
8402
|
function parseFoldSpansFromDisk(normalizedPath, rows) {
|
|
8232
8403
|
try {
|
|
8233
|
-
if (
|
|
8404
|
+
if (statSync13(normalizedPath).size > FOLD_SPAN_PARSE_MAX_BYTES) return [];
|
|
8234
8405
|
const language = detectLanguage(normalizedPath);
|
|
8235
8406
|
if (!isTreeSitterAvailable(language)) return [];
|
|
8236
8407
|
const fileText = readFileSync13(normalizedPath, "utf-8");
|
|
@@ -8249,7 +8420,7 @@ function parseFoldSpansFromDisk(normalizedPath, rows) {
|
|
|
8249
8420
|
function resolveFoldSpans(normalizedPath, hasCommentSyntax, rows) {
|
|
8250
8421
|
try {
|
|
8251
8422
|
const entry = getFileEntry(normalizedPath);
|
|
8252
|
-
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)) {
|
|
8253
8424
|
return querySymbols({ filePath: normalizedPath, limit: BODY_FOLD_SYMBOL_LIMIT });
|
|
8254
8425
|
}
|
|
8255
8426
|
if (hasCommentSyntax) enqueueDirtyPathSafe(normalizedPath);
|
|
@@ -8459,13 +8630,14 @@ function largeFileDenyBytes() {
|
|
|
8459
8630
|
function counterfactualCredit(counterfactualBytes, emittedBytes = 0) {
|
|
8460
8631
|
return Math.max(0, Math.min(counterfactualBytes, PER_FILE_COUNTERFACTUAL_CEILING) - emittedBytes);
|
|
8461
8632
|
}
|
|
8462
|
-
function
|
|
8463
|
-
const
|
|
8464
|
-
|
|
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;
|
|
8465
8637
|
}
|
|
8466
8638
|
function relPathWithinRoot(root, target) {
|
|
8467
|
-
const rel =
|
|
8468
|
-
if (rel.startsWith("..") ||
|
|
8639
|
+
const rel = path21.relative(root, target).replace(/\\/g, "/");
|
|
8640
|
+
if (rel.startsWith("..") || path21.isAbsolute(rel)) return null;
|
|
8469
8641
|
return rel;
|
|
8470
8642
|
}
|
|
8471
8643
|
function _isDocFile(filePath) {
|
|
@@ -8474,7 +8646,7 @@ function _isDocFile(filePath) {
|
|
|
8474
8646
|
}
|
|
8475
8647
|
function isSessionArtifactFile(filePath) {
|
|
8476
8648
|
if (/[/\\]tasks[/\\][a-z0-9]+\.output$/i.test(filePath)) return true;
|
|
8477
|
-
if (
|
|
8649
|
+
if (new RegExp("[/\\\\]tool-results[/\\\\]" + TOOL_RESULTS_ID_CHARS + "\\.txt$", "i").test(filePath)) return true;
|
|
8478
8650
|
return false;
|
|
8479
8651
|
}
|
|
8480
8652
|
function sessionArtifactRecall(rawPath) {
|
|
@@ -8491,7 +8663,7 @@ var BINARY_FILE_TYPE_EXTS = /* @__PURE__ */ new Set(["pdf", "docx", "xlsx", "ppt
|
|
|
8491
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"]);
|
|
8492
8664
|
var DISPATCHED_FILE_TYPE_EXTS = /* @__PURE__ */ new Set([...BINARY_FILE_TYPE_EXTS, ...TEXT_FILE_TYPE_EXTS]);
|
|
8493
8665
|
function isDispatchedFileType(basename11) {
|
|
8494
|
-
return DISPATCHED_FILE_TYPE_EXTS.has(
|
|
8666
|
+
return DISPATCHED_FILE_TYPE_EXTS.has(path21.extname(basename11).slice(1).toLowerCase());
|
|
8495
8667
|
}
|
|
8496
8668
|
function extractQuickSymbolSamples(content, _filePath) {
|
|
8497
8669
|
const symbols = [];
|
|
@@ -8541,11 +8713,6 @@ function surgicalHint(filePath, basename11, lineCount, fileContent) {
|
|
|
8541
8713
|
const isHtmlFile = /\.(html|htm)$/i.test(basename11);
|
|
8542
8714
|
const isSectionFile = /\.(css|scss|sass|less|toml)$/i.test(basename11);
|
|
8543
8715
|
const isXmlFile = /\.(xml|dtsx|ampkg|xaml)$/i.test(basename11) && !basename11.toLowerCase().endsWith("-meta.xml");
|
|
8544
|
-
const escapeHintName = (name) => {
|
|
8545
|
-
const quoted = name.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
8546
|
-
const safe = displaySafeText(quoted);
|
|
8547
|
-
return safe !== quoted ? "" : safe.trim();
|
|
8548
|
-
};
|
|
8549
8716
|
if (isXmlFile) {
|
|
8550
8717
|
return `Use \`token-goat xml-outline "${filePath}"\` for structure or \`token-goat xml-query "${filePath}" "<selector>"\` for nodes.`;
|
|
8551
8718
|
} else if (isDocFile) {
|
|
@@ -8591,9 +8758,62 @@ function surgicalHint(filePath, basename11, lineCount, fileContent) {
|
|
|
8591
8758
|
if (isShellScript && samples.length > 0) {
|
|
8592
8759
|
return `Use \`token-goat section "${filePath}::${sym}"\`${avail} for a section, or \`token-goat skeleton "${filePath}"\` / \`token-goat outline "${filePath}"\` for structure.`;
|
|
8593
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
|
+
}
|
|
8594
8770
|
return `Use \`token-goat read "${filePath}::${sym}"\`${avail} for one function, or \`token-goat skeleton "${filePath}"\` / \`token-goat outline "${filePath}"\` for structure.`;
|
|
8595
8771
|
}
|
|
8596
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
|
+
}
|
|
8597
8817
|
function lineCountForSurgicalHint(filePath, fileStatSize) {
|
|
8598
8818
|
try {
|
|
8599
8819
|
if (fileStatSize !== void 0 && fileStatSize > SLICE_ESTIMATE_SCAN_CAP_BYTES) {
|
|
@@ -8704,7 +8924,7 @@ function preReadHandlerInner(event) {
|
|
|
8704
8924
|
} catch {
|
|
8705
8925
|
}
|
|
8706
8926
|
}
|
|
8707
|
-
const basename11 = displaySafePath(
|
|
8927
|
+
const basename11 = displaySafePath(path21.basename(normalized));
|
|
8708
8928
|
if (isLockFile(basename11)) {
|
|
8709
8929
|
return denyOutput(
|
|
8710
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.'
|
|
@@ -8825,12 +9045,13 @@ function preReadHandlerInner(event) {
|
|
|
8825
9045
|
(basename11 + " is unchanged since last read. " + surgicalHint(normalized, basename11, countTextLines(snapDiff.currentContent), snapDiff.currentContent)).trimEnd()
|
|
8826
9046
|
);
|
|
8827
9047
|
}
|
|
8828
|
-
if (snapDiff.kind === "diff"
|
|
8829
|
-
|
|
8830
|
-
|
|
8831
|
-
|
|
8832
|
-
("
|
|
8833
|
-
|
|
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
|
+
}
|
|
8834
9055
|
}
|
|
8835
9056
|
recordActualRead(event, normalized);
|
|
8836
9057
|
recordStat("session_hint", 0, 0);
|
|
@@ -8861,12 +9082,13 @@ function preReadHandlerInner(event) {
|
|
|
8861
9082
|
(basename11 + " is unchanged since last read. " + surgicalHint(normalized, basename11, countTextLines(memSnapDiff.currentContent), memSnapDiff.currentContent)).trimEnd()
|
|
8862
9083
|
);
|
|
8863
9084
|
}
|
|
8864
|
-
if (memSnapDiff.kind === "diff"
|
|
8865
|
-
|
|
8866
|
-
|
|
8867
|
-
|
|
8868
|
-
("
|
|
8869
|
-
|
|
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
|
+
}
|
|
8870
9092
|
}
|
|
8871
9093
|
recordActualRead(event, normalized);
|
|
8872
9094
|
recordStat("session_hint", 0, 0);
|
|
@@ -8915,12 +9137,13 @@ function preReadHandlerInner(event) {
|
|
|
8915
9137
|
);
|
|
8916
9138
|
}
|
|
8917
9139
|
if (snapDiff.kind === "diff") {
|
|
8918
|
-
|
|
8919
|
-
const artifactDiffCredit =
|
|
8920
|
-
|
|
8921
|
-
|
|
8922
|
-
"
|
|
8923
|
-
|
|
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
|
+
}
|
|
8924
9147
|
}
|
|
8925
9148
|
recordActualRead(event, normalized);
|
|
8926
9149
|
recordStat("session_hint", 0, 0);
|
|
@@ -8963,13 +9186,12 @@ function preReadHandlerInner(event) {
|
|
|
8963
9186
|
);
|
|
8964
9187
|
}
|
|
8965
9188
|
if (snapDiff.kind === "diff") {
|
|
8966
|
-
|
|
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) {
|
|
8967
9192
|
recordActualRead(event, normalized);
|
|
8968
|
-
const diffCredit = counterfactualCredit(snapDiff.currentContent.length, snapDiff.diff.length);
|
|
8969
9193
|
recordStat("diff_hint", diffCredit, savedTokensFromBytes(diffCredit));
|
|
8970
|
-
return denyOutput(
|
|
8971
|
-
("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()
|
|
8972
|
-
);
|
|
9194
|
+
return denyOutput(diffBody);
|
|
8973
9195
|
}
|
|
8974
9196
|
}
|
|
8975
9197
|
}
|
|
@@ -9019,7 +9241,7 @@ function preReadHandlerInner(event) {
|
|
|
9019
9241
|
recordStat("read_served_deny", blocked, savedTokensFromBytes(blocked));
|
|
9020
9242
|
recordStat("session_hint", 0, 0);
|
|
9021
9243
|
return denyOutput(
|
|
9022
|
-
"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) + "."
|
|
9023
9245
|
);
|
|
9024
9246
|
}
|
|
9025
9247
|
const window = readRequestedSliceWindow(event);
|
|
@@ -9034,7 +9256,7 @@ function preReadHandlerInner(event) {
|
|
|
9034
9256
|
recordStat("read_served_deny", blocked, savedTokensFromBytes(blocked));
|
|
9035
9257
|
recordStat("session_hint", 0, 0);
|
|
9036
9258
|
return denyOutput(
|
|
9037
|
-
"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 }) + "."
|
|
9038
9260
|
);
|
|
9039
9261
|
};
|
|
9040
9262
|
if (snapDiff.kind === "unchanged") {
|
|
@@ -9060,11 +9282,14 @@ function preReadHandlerInner(event) {
|
|
|
9060
9282
|
}
|
|
9061
9283
|
const isPagingTracked = isSourceExt || /\.(md|mdx|markdown|rst|xml|dtsx|ampkg|xaml)$/i.test(basename11);
|
|
9062
9284
|
if (isPagingTracked && window.isExplicitSlice && prevRanges.length >= 3) {
|
|
9063
|
-
|
|
9064
|
-
|
|
9065
|
-
|
|
9066
|
-
|
|
9067
|
-
|
|
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
|
+
}
|
|
9068
9293
|
}
|
|
9069
9294
|
}
|
|
9070
9295
|
if (config2.hints.reread_deny && !protectedRead) {
|
|
@@ -9106,7 +9331,7 @@ function preReadHandlerInner(event) {
|
|
|
9106
9331
|
}
|
|
9107
9332
|
const pagingWindow = readRequestedSliceWindow(event);
|
|
9108
9333
|
const activeRanges = getFileLineRanges(normalized);
|
|
9109
|
-
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) + "." : "";
|
|
9110
9335
|
if (!isWithinQuietHours(config2.hints.quiet_hours)) {
|
|
9111
9336
|
recordStat("session_hint", 0, 0);
|
|
9112
9337
|
}
|
|
@@ -9149,7 +9374,7 @@ function preReadHandlerInner(event) {
|
|
|
9149
9374
|
"Note: " + shown + " is large (" + kb + "KB). " + hint + contextPressureAdvisorySuffix()
|
|
9150
9375
|
);
|
|
9151
9376
|
}
|
|
9152
|
-
const fileTypeExt =
|
|
9377
|
+
const fileTypeExt = path21.extname(normalized).slice(1).toLowerCase();
|
|
9153
9378
|
const fileStatSize = size ?? statSize(normalized) ?? 0;
|
|
9154
9379
|
const isKnownFileType = DISPATCHED_FILE_TYPE_EXTS.has(fileTypeExt);
|
|
9155
9380
|
if (event.toolName !== "Grep" && !isImagePath(normalized) && (isKnownFileType || fileStatSize >= FILE_TYPE_THRESHOLDS.generic)) {
|
|
@@ -9230,7 +9455,7 @@ function postReadHandlerInner(event, suppressStructuralHint) {
|
|
|
9230
9455
|
if (isTruncatedReadDelivery(event, respText)) {
|
|
9231
9456
|
markFileTruncated(normalized, !readRequestedSliceWindow(event).isExplicitSlice);
|
|
9232
9457
|
}
|
|
9233
|
-
const postBasename =
|
|
9458
|
+
const postBasename = path21.basename(normalized);
|
|
9234
9459
|
const diffSourcesEnabled = loadConfig().hints.serve_diff_on_reread;
|
|
9235
9460
|
if (/\.(md|mdx|markdown|rst|txt)$/i.test(postBasename) || isSessionArtifactFile(normalized) || diffSourcesEnabled && isDiffableSource(postBasename)) {
|
|
9236
9461
|
try {
|
|
@@ -9346,7 +9571,7 @@ function elideAlreadyServedLines(event, respText) {
|
|
|
9346
9571
|
if (ids.length === 0) return null;
|
|
9347
9572
|
const parsed = parseNumberedReadResult(respText, readStartLine(event));
|
|
9348
9573
|
if (parsed === null) return null;
|
|
9349
|
-
if (
|
|
9574
|
+
if (hasPreciseSecret(respText)) return null;
|
|
9350
9575
|
const bodies = [];
|
|
9351
9576
|
for (let i = ids.length - 1; i >= 0; i--) {
|
|
9352
9577
|
const id = ids[i];
|
|
@@ -9386,7 +9611,7 @@ function structuralFoldInputs(event, respText) {
|
|
|
9386
9611
|
if (filePath === void 0) return null;
|
|
9387
9612
|
if (readRequestedSliceWindow(event).isExplicitSlice) return null;
|
|
9388
9613
|
if (isTruncatedReadDelivery(event, respText)) return null;
|
|
9389
|
-
if (
|
|
9614
|
+
if (hasPreciseSecret(respText)) return null;
|
|
9390
9615
|
const parsed = parseNumberedReadResult(respText, readStartLine(event));
|
|
9391
9616
|
if (parsed === null) return null;
|
|
9392
9617
|
const normalized = normalizePath(filePath);
|
|
@@ -9423,7 +9648,7 @@ function foldCodeBodies(event, respText) {
|
|
|
9423
9648
|
const requestedOffset = sliceWin.offset;
|
|
9424
9649
|
if (requestedOffset !== void 0 && readStartLine(event) !== requestedOffset) return null;
|
|
9425
9650
|
if (isTruncatedReadDelivery(event, respText)) return null;
|
|
9426
|
-
if (
|
|
9651
|
+
if (hasPreciseSecret(respText)) return null;
|
|
9427
9652
|
const parsed = parseNumberedReadResult(respText, readStartLine(event));
|
|
9428
9653
|
if (parsed === null) return null;
|
|
9429
9654
|
const shown = displaySafePath(toDisplayPath(findProject(getCwd(event) ?? process.cwd())?.root, normalized));
|
|
@@ -9460,13 +9685,13 @@ registerHook("post_tool_use", postReadHandler, { toolName: "Read" });
|
|
|
9460
9685
|
// src/baseline.ts
|
|
9461
9686
|
init_define_import_meta_env();
|
|
9462
9687
|
import * as fs26 from "node:fs";
|
|
9463
|
-
import * as
|
|
9688
|
+
import * as path24 from "node:path";
|
|
9464
9689
|
|
|
9465
9690
|
// src/cli_context_stats.ts
|
|
9466
9691
|
init_define_import_meta_env();
|
|
9467
9692
|
import * as fs25 from "node:fs";
|
|
9468
|
-
import * as
|
|
9469
|
-
import * as
|
|
9693
|
+
import * as os6 from "node:os";
|
|
9694
|
+
import * as path23 from "node:path";
|
|
9470
9695
|
|
|
9471
9696
|
// src/confirm_apply.ts
|
|
9472
9697
|
init_define_import_meta_env();
|
|
@@ -9523,7 +9748,7 @@ ${diff}
|
|
|
9523
9748
|
// src/memory_prune.ts
|
|
9524
9749
|
init_define_import_meta_env();
|
|
9525
9750
|
import * as fs24 from "node:fs";
|
|
9526
|
-
import * as
|
|
9751
|
+
import * as path22 from "node:path";
|
|
9527
9752
|
var ENTRY_RE = /^\s*-\s*\[(?<title>[^\]]+)\]\((?<target>[^)]+?\.md)\)/;
|
|
9528
9753
|
var URL_SCHEME_RE = /^[a-z][a-z0-9+.-]*:\/\//i;
|
|
9529
9754
|
function parseIndex(text) {
|
|
@@ -9568,7 +9793,7 @@ function pruneIndex(memoryDir, opts) {
|
|
|
9568
9793
|
changed: false,
|
|
9569
9794
|
tokensSaved: 0
|
|
9570
9795
|
};
|
|
9571
|
-
const memoryMd =
|
|
9796
|
+
const memoryMd = path22.join(memoryDir, "MEMORY.md");
|
|
9572
9797
|
let text;
|
|
9573
9798
|
try {
|
|
9574
9799
|
text = fs24.readFileSync(memoryMd, "utf-8");
|
|
@@ -9582,7 +9807,7 @@ function pruneIndex(memoryDir, opts) {
|
|
|
9582
9807
|
const dups = [];
|
|
9583
9808
|
for (const entry of entries) {
|
|
9584
9809
|
const isUrl = URL_SCHEME_RE.test(entry.target);
|
|
9585
|
-
const targetExists = isUrl ? true :
|
|
9810
|
+
const targetExists = isUrl ? true : path22.isAbsolute(entry.target) ? fs24.existsSync(entry.target) : fs24.existsSync(path22.join(memoryDir, entry.target));
|
|
9586
9811
|
const foldedTarget = foldPath(entry.target);
|
|
9587
9812
|
if (!targetExists) {
|
|
9588
9813
|
dead.push(entry);
|
|
@@ -9726,7 +9951,7 @@ async function tryEmbeddingClusters(siblings, snippets, threshold) {
|
|
|
9726
9951
|
}
|
|
9727
9952
|
async function findContentDuplicates(memoryDir, _opts) {
|
|
9728
9953
|
const threshold = _opts?.threshold ?? 0.92;
|
|
9729
|
-
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();
|
|
9730
9955
|
if (siblings.length < 2) {
|
|
9731
9956
|
return [];
|
|
9732
9957
|
}
|
|
@@ -9810,7 +10035,7 @@ function auditClaudeMd(files) {
|
|
|
9810
10035
|
const overlaps = [];
|
|
9811
10036
|
for (const [stripped, filesSet] of lineToFiles) {
|
|
9812
10037
|
if (filesSet.has(report.path) && filesSet.size > 1) {
|
|
9813
|
-
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));
|
|
9814
10039
|
if (others.length > 0) {
|
|
9815
10040
|
if (stripped.length > 60) {
|
|
9816
10041
|
overlaps.push(
|
|
@@ -9836,31 +10061,31 @@ function tok(filePath) {
|
|
|
9836
10061
|
return 0;
|
|
9837
10062
|
}
|
|
9838
10063
|
}
|
|
9839
|
-
function findClaudeMdFiles(projectRoot, homeDir =
|
|
10064
|
+
function findClaudeMdFiles(projectRoot, homeDir = os6.homedir()) {
|
|
9840
10065
|
const found = [];
|
|
9841
10066
|
const seen = /* @__PURE__ */ new Set();
|
|
9842
|
-
let current =
|
|
10067
|
+
let current = path23.resolve(projectRoot);
|
|
9843
10068
|
while (true) {
|
|
9844
|
-
const candidate =
|
|
10069
|
+
const candidate = path23.join(current, "CLAUDE.md");
|
|
9845
10070
|
if (!seen.has(candidate) && fs25.existsSync(candidate)) {
|
|
9846
10071
|
found.push(candidate);
|
|
9847
10072
|
seen.add(candidate);
|
|
9848
10073
|
}
|
|
9849
|
-
const parent =
|
|
10074
|
+
const parent = path23.dirname(current);
|
|
9850
10075
|
if (parent === current) break;
|
|
9851
10076
|
current = parent;
|
|
9852
10077
|
}
|
|
9853
|
-
const globalMd =
|
|
10078
|
+
const globalMd = path23.join(claudeConfigDir(homeDir), "CLAUDE.md");
|
|
9854
10079
|
if (!seen.has(globalMd) && fs25.existsSync(globalMd)) {
|
|
9855
10080
|
found.push(globalMd);
|
|
9856
10081
|
}
|
|
9857
10082
|
return found;
|
|
9858
10083
|
}
|
|
9859
|
-
function findMemoryMd(projectRoot, homeDir =
|
|
10084
|
+
function findMemoryMd(projectRoot, homeDir = os6.homedir(), alternateRoots = []) {
|
|
9860
10085
|
try {
|
|
9861
|
-
const projectsDir =
|
|
10086
|
+
const projectsDir = path23.join(claudeConfigDir(homeDir), "projects");
|
|
9862
10087
|
if (!fs25.existsSync(projectsDir)) return null;
|
|
9863
|
-
const rootStr =
|
|
10088
|
+
const rootStr = path23.resolve(projectRoot);
|
|
9864
10089
|
const candidateRoots = [rootStr];
|
|
9865
10090
|
try {
|
|
9866
10091
|
const realRoot = fs25.realpathSync.native(rootStr);
|
|
@@ -9868,12 +10093,12 @@ function findMemoryMd(projectRoot, homeDir = os5.homedir(), alternateRoots = [])
|
|
|
9868
10093
|
} catch {
|
|
9869
10094
|
}
|
|
9870
10095
|
for (const alternate of alternateRoots) {
|
|
9871
|
-
const resolved =
|
|
10096
|
+
const resolved = path23.resolve(alternate);
|
|
9872
10097
|
if (!candidateRoots.includes(resolved)) candidateRoots.push(resolved);
|
|
9873
10098
|
}
|
|
9874
10099
|
for (const root of candidateRoots) {
|
|
9875
10100
|
const expectedSlug = root.replace(/[^A-Za-z0-9]/g, "-");
|
|
9876
|
-
const candidate =
|
|
10101
|
+
const candidate = path23.join(projectsDir, expectedSlug, "memory", "MEMORY.md");
|
|
9877
10102
|
if (fs25.existsSync(candidate)) return candidate;
|
|
9878
10103
|
}
|
|
9879
10104
|
return null;
|
|
@@ -9881,17 +10106,17 @@ function findMemoryMd(projectRoot, homeDir = os5.homedir(), alternateRoots = [])
|
|
|
9881
10106
|
return null;
|
|
9882
10107
|
}
|
|
9883
10108
|
}
|
|
9884
|
-
function buildStats(projectRoot, homeDir =
|
|
10109
|
+
function buildStats(projectRoot, homeDir = os6.homedir(), alternateRoots = []) {
|
|
9885
10110
|
const claudeMds = findClaudeMdFiles(projectRoot, homeDir);
|
|
9886
10111
|
const claudeMdRows = [];
|
|
9887
10112
|
let claudeMdTotal = 0;
|
|
9888
10113
|
for (const p of claudeMds) {
|
|
9889
10114
|
const t = tok(p);
|
|
9890
10115
|
claudeMdTotal += t;
|
|
9891
|
-
const parentDir =
|
|
10116
|
+
const parentDir = path23.basename(path23.dirname(p));
|
|
9892
10117
|
const label = parentDir === ".claude" ? "~/.claude/CLAUDE.md" : (() => {
|
|
9893
10118
|
try {
|
|
9894
|
-
return
|
|
10119
|
+
return path23.relative(projectRoot, p);
|
|
9895
10120
|
} catch {
|
|
9896
10121
|
return p;
|
|
9897
10122
|
}
|
|
@@ -9955,7 +10180,7 @@ async function runContextStats(opts = {}) {
|
|
|
9955
10180
|
process.stdout.write("[--fix] No MEMORY.md found; nothing to prune.\n");
|
|
9956
10181
|
} else {
|
|
9957
10182
|
const memPath = result.memory_md_path;
|
|
9958
|
-
const pruneResult = pruneIndex(
|
|
10183
|
+
const pruneResult = pruneIndex(path23.dirname(memPath), { dryRun: true });
|
|
9959
10184
|
if (!pruneResult.changed || pruneResult.after === void 0) {
|
|
9960
10185
|
process.stdout.write("[--fix] MEMORY.md already clean; nothing to prune.\n");
|
|
9961
10186
|
} else {
|
|
@@ -10009,6 +10234,10 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
10009
10234
|
".idea",
|
|
10010
10235
|
".vscode"
|
|
10011
10236
|
]);
|
|
10237
|
+
function isIgnoredIndexPath(p) {
|
|
10238
|
+
const segments = foldPath(p).split(/[/\\]/);
|
|
10239
|
+
return segments.slice(0, -1).some((s) => SKIP_DIRS.has(s));
|
|
10240
|
+
}
|
|
10012
10241
|
var MAX_FILES_SCANNED = 2e4;
|
|
10013
10242
|
function walkProject(rootDir, opts = {}) {
|
|
10014
10243
|
const files = [];
|
|
@@ -10028,7 +10257,7 @@ function walkProject(rootDir, opts = {}) {
|
|
|
10028
10257
|
continue;
|
|
10029
10258
|
}
|
|
10030
10259
|
for (const entry of entries) {
|
|
10031
|
-
const full =
|
|
10260
|
+
const full = path24.join(dir, entry.name);
|
|
10032
10261
|
if (entry.isDirectory()) {
|
|
10033
10262
|
if (SKIP_DIRS.has(entry.name) || extraSkipDirs.includes(entry.name)) continue;
|
|
10034
10263
|
if (entry.name.startsWith(".") && entry.name !== ".") {
|
|
@@ -10110,7 +10339,7 @@ function fetchTopSymbols(limit, dbPath, rootDir) {
|
|
|
10110
10339
|
}
|
|
10111
10340
|
}
|
|
10112
10341
|
function buildProjectMap(rootDir = process.cwd(), opts = {}) {
|
|
10113
|
-
const root =
|
|
10342
|
+
const root = path24.resolve(rootDir);
|
|
10114
10343
|
const config = loadConfig();
|
|
10115
10344
|
const { files, languages, truncated } = walkProject(root, { excludeTests: config.repomap.exclude_tests });
|
|
10116
10345
|
const compact = opts.compact === true || files.length > config.repomap.compact_file_threshold;
|
|
@@ -10124,7 +10353,7 @@ function buildProjectMap(rootDir = process.cwd(), opts = {}) {
|
|
|
10124
10353
|
mtime = 0;
|
|
10125
10354
|
}
|
|
10126
10355
|
return { f, mtime };
|
|
10127
|
-
}).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));
|
|
10128
10357
|
return {
|
|
10129
10358
|
rootDir: root,
|
|
10130
10359
|
fileCount: files.length,
|
|
@@ -10137,7 +10366,7 @@ function buildProjectMap(rootDir = process.cwd(), opts = {}) {
|
|
|
10137
10366
|
}
|
|
10138
10367
|
function formatProjectMap(map, compact = false) {
|
|
10139
10368
|
const lines = [];
|
|
10140
|
-
const rel =
|
|
10369
|
+
const rel = path24.basename(map.rootDir);
|
|
10141
10370
|
lines.push(`# Project map: ${rel}`);
|
|
10142
10371
|
lines.push(map.fileCountTruncated === true ? `Files: at least ${map.fileCount} (walk stopped at the ${MAX_FILES_SCANNED}-file cap)` : `Files: ${map.fileCount}`);
|
|
10143
10372
|
const langPairs = Object.entries(map.languages).sort((a, b) => b[1] - a[1]);
|
|
@@ -10166,7 +10395,7 @@ function formatProjectMap(map, compact = false) {
|
|
|
10166
10395
|
}
|
|
10167
10396
|
function mapLookupBytesSaved(map, emittedText) {
|
|
10168
10397
|
const referencedFiles = /* @__PURE__ */ new Set([
|
|
10169
|
-
...map.recentFiles.map((f) => normalizePath(
|
|
10398
|
+
...map.recentFiles.map((f) => normalizePath(path24.resolve(map.rootDir, f))),
|
|
10170
10399
|
...map.topSymbols.map((s) => normalizePath(s.filePath))
|
|
10171
10400
|
]);
|
|
10172
10401
|
const listingText = Array.from(referencedFiles).sort().join("\n");
|
|
@@ -10181,7 +10410,7 @@ function findMemSuggestionCandidates(projectRoot) {
|
|
|
10181
10410
|
const claudeMdFiles = findClaudeMdFiles(projectRoot);
|
|
10182
10411
|
const candidateFiles = new Set(claudeMdFiles);
|
|
10183
10412
|
for (const claudeMd of claudeMdFiles) {
|
|
10184
|
-
const agentsMd =
|
|
10413
|
+
const agentsMd = path24.join(path24.dirname(claudeMd), "AGENTS.md");
|
|
10185
10414
|
if (fs26.existsSync(agentsMd)) candidateFiles.add(agentsMd);
|
|
10186
10415
|
}
|
|
10187
10416
|
const suggestions = [];
|
|
@@ -10217,7 +10446,7 @@ function formatMemSuggestions(projectRoot) {
|
|
|
10217
10446
|
if (suggestions.length === 0) return "";
|
|
10218
10447
|
const lines = ["", "## mem suggestions"];
|
|
10219
10448
|
for (const s of suggestions) {
|
|
10220
|
-
const basename11 =
|
|
10449
|
+
const basename11 = path24.basename(s.path);
|
|
10221
10450
|
lines.push(
|
|
10222
10451
|
"Consider: mem import --from-md " + s.path + " # migrates " + s.count + " preference-shaped lines from " + basename11 + " as pending facts for review"
|
|
10223
10452
|
);
|
|
@@ -10262,15 +10491,15 @@ function scanAndRecord(text) {
|
|
|
10262
10491
|
// src/repomap.ts
|
|
10263
10492
|
init_define_import_meta_env();
|
|
10264
10493
|
import * as fs27 from "fs";
|
|
10265
|
-
import * as
|
|
10494
|
+
import * as path25 from "path";
|
|
10266
10495
|
function getTrackedFiles(cwd = process.cwd()) {
|
|
10267
10496
|
try {
|
|
10268
10497
|
let dir = cwd;
|
|
10269
10498
|
let onlyFile = "";
|
|
10270
10499
|
try {
|
|
10271
10500
|
if (fs27.statSync(cwd).isFile()) {
|
|
10272
|
-
dir =
|
|
10273
|
-
onlyFile =
|
|
10501
|
+
dir = path25.dirname(cwd);
|
|
10502
|
+
onlyFile = path25.basename(cwd);
|
|
10274
10503
|
}
|
|
10275
10504
|
} catch {
|
|
10276
10505
|
}
|
|
@@ -10279,7 +10508,7 @@ function getTrackedFiles(cwd = process.cwd()) {
|
|
|
10279
10508
|
if (result.exitCode !== 0 || !result.stdout) {
|
|
10280
10509
|
return [];
|
|
10281
10510
|
}
|
|
10282
|
-
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));
|
|
10283
10512
|
} catch {
|
|
10284
10513
|
return [];
|
|
10285
10514
|
}
|
|
@@ -10505,7 +10734,49 @@ function embeddedIpv4Candidates(host) {
|
|
|
10505
10734
|
return out;
|
|
10506
10735
|
}
|
|
10507
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
|
+
|
|
10508
10778
|
export {
|
|
10779
|
+
claudeConfigDir,
|
|
10509
10780
|
indexedSourceText,
|
|
10510
10781
|
isVirtualIndexedPath,
|
|
10511
10782
|
virtualIndexedScopeNote,
|
|
@@ -10547,6 +10818,7 @@ export {
|
|
|
10547
10818
|
readServersJson,
|
|
10548
10819
|
serversOf,
|
|
10549
10820
|
setTokenGoatServer,
|
|
10821
|
+
isResidueServersJson,
|
|
10550
10822
|
dropEmptyServers,
|
|
10551
10823
|
hasManagedServer,
|
|
10552
10824
|
visualStudioUserMcpPath,
|
|
@@ -10588,6 +10860,7 @@ export {
|
|
|
10588
10860
|
classifyBashHint,
|
|
10589
10861
|
classifyEditHint,
|
|
10590
10862
|
applyHintTracking,
|
|
10863
|
+
logSuppressedDetection,
|
|
10591
10864
|
isSuppressionCategory,
|
|
10592
10865
|
meetsSavingsFloor,
|
|
10593
10866
|
getHintStatsSummary,
|
|
@@ -10643,6 +10916,7 @@ export {
|
|
|
10643
10916
|
getSessionId,
|
|
10644
10917
|
setTranscriptPath,
|
|
10645
10918
|
SESSIONS_SUBDIR,
|
|
10919
|
+
sessionsDir,
|
|
10646
10920
|
AGENT_SALT_MARKER,
|
|
10647
10921
|
sessionSidecarPath,
|
|
10648
10922
|
listSiblingSessionStates,
|
|
@@ -10664,6 +10938,7 @@ export {
|
|
|
10664
10938
|
loadSessionCache,
|
|
10665
10939
|
computeAdaptiveBudget,
|
|
10666
10940
|
buildManifestWithCount,
|
|
10941
|
+
LARGE_SYMBOL_LINE_THRESHOLD,
|
|
10667
10942
|
buildLineDiff,
|
|
10668
10943
|
SKILLS_OUTPUT_SUBDIR,
|
|
10669
10944
|
skillOutputsDir,
|
|
@@ -10687,7 +10962,6 @@ export {
|
|
|
10687
10962
|
visionTokensSavedByText,
|
|
10688
10963
|
canShrinkFormat,
|
|
10689
10964
|
formatShrinkSummary,
|
|
10690
|
-
isImagePath,
|
|
10691
10965
|
ImageDecodeError,
|
|
10692
10966
|
probeImageMeta,
|
|
10693
10967
|
imageQualifiesForShrink,
|
|
@@ -10702,6 +10976,7 @@ export {
|
|
|
10702
10976
|
searchEvidenceSemantically,
|
|
10703
10977
|
buildDeltaCapsule,
|
|
10704
10978
|
foldDetail,
|
|
10979
|
+
pruneUnembeddableChunks,
|
|
10705
10980
|
removeFileFromIndex,
|
|
10706
10981
|
pruneDeletedFiles,
|
|
10707
10982
|
pruneBlockedRoot,
|
|
@@ -10710,6 +10985,7 @@ export {
|
|
|
10710
10985
|
findOrphanedChunkPaths,
|
|
10711
10986
|
pruneOrphanedChunks,
|
|
10712
10987
|
recordKnownRootThrottled,
|
|
10988
|
+
findTopIndexedProjects,
|
|
10713
10989
|
cmdReclaimIndex,
|
|
10714
10990
|
applyIndexingPriority,
|
|
10715
10991
|
WORKER_HEARTBEAT_STALE_MS,
|
|
@@ -10724,6 +11000,7 @@ export {
|
|
|
10724
11000
|
runDetachedWorkerDaemon,
|
|
10725
11001
|
appendDirtyPath,
|
|
10726
11002
|
enqueueDirtyPathSafe,
|
|
11003
|
+
enqueueDirtyPathsSafe,
|
|
10727
11004
|
foldingEnabled,
|
|
10728
11005
|
foldDelivery,
|
|
10729
11006
|
OUTLINE_MIN_HEADINGS,
|
|
@@ -10731,6 +11008,7 @@ export {
|
|
|
10731
11008
|
planMarkdownOutline,
|
|
10732
11009
|
planSourceSkeleton,
|
|
10733
11010
|
isStructuralRewriteAccepted,
|
|
11011
|
+
escapeHintName,
|
|
10734
11012
|
confirmAndApply,
|
|
10735
11013
|
findContentDuplicates,
|
|
10736
11014
|
auditClaudeMd,
|
|
@@ -10739,6 +11017,7 @@ export {
|
|
|
10739
11017
|
buildStats,
|
|
10740
11018
|
runContextStats,
|
|
10741
11019
|
SKIP_DIRS,
|
|
11020
|
+
isIgnoredIndexPath,
|
|
10742
11021
|
MAX_FILES_SCANNED,
|
|
10743
11022
|
walkProject,
|
|
10744
11023
|
buildProjectMap,
|
|
@@ -10758,5 +11037,6 @@ export {
|
|
|
10758
11037
|
parseIpv6Groups,
|
|
10759
11038
|
isPrivateIpv4Octets,
|
|
10760
11039
|
isPrivateIpv6Groups,
|
|
10761
|
-
metadataEndpointRefusal
|
|
11040
|
+
metadataEndpointRefusal,
|
|
11041
|
+
resolveLineRegions
|
|
10762
11042
|
};
|