token-goat 2.9.3 → 2.9.4
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 -1
- package/dist/{token-goat-chunk-KKHKXUVS.mjs → token-goat-chunk-3ZR4NLB3.mjs} +12 -1
- package/dist/{token-goat-chunk-LLNA42NI.mjs → token-goat-chunk-5BHXZEOI.mjs} +2 -2
- package/dist/{token-goat-chunk-2VVXTAGD.mjs → token-goat-chunk-63XEQMNU.mjs} +5 -5
- package/dist/{token-goat-chunk-6FQMEMWX.mjs → token-goat-chunk-6MU6FHE3.mjs} +54 -10
- package/dist/{token-goat-chunk-3XVGPLDS.mjs → token-goat-chunk-CBO45DYZ.mjs} +238 -17
- package/dist/{token-goat-chunk-OKJX2JWW.mjs → token-goat-chunk-EPX5DWGM.mjs} +1 -1
- package/dist/{token-goat-chunk-HC5NMGPD.mjs → token-goat-chunk-EQKNR7CN.mjs} +5 -5
- package/dist/{token-goat-chunk-A4VBNWSL.mjs → token-goat-chunk-FSC44QOH.mjs} +2 -2
- package/dist/{token-goat-chunk-XSSQFI4C.mjs → token-goat-chunk-GEXHIIKM.mjs} +5 -5
- package/dist/{token-goat-chunk-3UBORD6Z.mjs → token-goat-chunk-OZAEJQ7L.mjs} +3 -3
- package/dist/{token-goat-chunk-7A7SBE6R.mjs → token-goat-chunk-US57I7GN.mjs} +2 -2
- package/dist/token-goat-hook.mjs +5 -5
- package/dist/token-goat.core.mjs +5 -5
- package/docs/cli.md +15 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -114,6 +114,7 @@ The fastest way to reduce AI token costs is fixing these five, not writing short
|
|
|
114
114
|
| Index-only files (lockfiles, source maps, bundles) read on every session | Pre-Read suppression for read-only files (package-lock.json, *.map, dist/), skipped unless explicitly edited |
|
|
115
115
|
| Large markdown file read in full (README.md, CHANGELOG.md, CLAUDE.md ≥8 KB) | Heading tree intercepted instead — H1–H3 with `#2`/`#3` disambiguation; `token-goat section` shortcuts listed for well-known files; post-edit injects a re-read suggestion rather than the full file |
|
|
116
116
|
| Subagent reads a ≥30 KB markdown file whole, on its first look at it | Off by default. Set `subagent_markdown_first_read_deny = true` under `[hints]` to block that read and answer with the heading tree instead. Only fires in a subagent, only on a first, un-ranged read of a `.md`/`.mdx`/`.markdown` file with at least three headings; a read that already asks for a line range, and every main-session read, pass through untouched |
|
|
117
|
+
| A source file is read whole for the first time, with no earlier copy to compare it against | Off by default. Set `fold_code_bodies = true` under `[hints]` to keep each function's declaration and its first eight lines, replacing the rest with one line naming the symbol, the exact range removed, and the command that returns it. Comment blocks of twelve lines or more fold to their first two on the same terms. Imports, signatures, type declarations, classes and interfaces are never folded, and a file whose index no longer matches what is on disk keeps its bodies. Measured on this repository's own source: 229 of 256 files fold, and 43% of what a read of them delivers is removed. A read that already names an offset or a limit, and any file holding something that looks like a secret, pass through untouched |
|
|
117
118
|
| PDF opened via Read | Full read denied; PDF shows page count and outline (`token-goat pdf-extract` pulls the actual text, optionally paged/sliced, when the outline isn't enough) |
|
|
118
119
|
| Excel/PowerPoint/Word file (.xlsx/.pptx/.docx) opened via Read | Full read denied; redirects to the matching narrow-slice command family (`xlsx-sheets`/`xlsx-head`/`xlsx-range`/`xlsx-query`, `pptx-outline`/`pptx-slide`/`pptx-notes`/`pptx-text`, `docx-outline`/`docx-text`) instead of extracting the whole document as text |
|
|
119
120
|
| Other Office binary (.odt, .ods, .ott, .odp) opened via Read | Full read denied; redirects to `pandoc` for text extraction (no dedicated reader for these formats yet) |
|
|
@@ -142,7 +143,7 @@ The fastest way to reduce AI token costs is fixing these five, not writing short
|
|
|
142
143
|
| `grep`/`rg` matches a line in a `.min.js` or `.min.css` file | Matching line truncated to 200 chars; filename and line number preserved |
|
|
143
144
|
| Claude Code writes async-task output to a temp file | `pre_read` intercepts the path and redirects to `token-goat bash-output <id>` with `--head`/`--tail`/`--grep` support |
|
|
144
145
|
| Re-read hints fire immediately after conversation compaction | Grace period suppresses deny hints for the first few reads after a compact so the model can re-orient |
|
|
145
|
-
| Large reference doc (CLAUDE.arch.md, API spec) re-read in full every new session | `token-goat compact-doc <path>` builds a deterministic extractive sidecar (headings + first N lines per section); `pre_read` serves it in place of the full file —
|
|
146
|
+
| Large reference doc (CLAUDE.arch.md, API spec) re-read in full every new session | `token-goat compact-doc <path>` builds a deterministic extractive sidecar (headings + first N lines per section); `pre_read` serves it in place of the full file — typically 60–95% smaller (median 67% across this repo's own docs). Sidecar is automatically marked stale when the source is edited. |
|
|
146
147
|
| Re-read denial fires as an advisory hint the model can ignore | When `deny_reread` is on (default), `pre_read` actively denies re-reads of files confirmed in the current context window, not just nudges; the advisory still fires for older reads that may have scrolled out |
|
|
147
148
|
| Unchanged files produce duplicate hints across sessions | Hint fingerprint includes file path; unchanged-file short-circuit skips re-read pre-check entirely |
|
|
148
149
|
| Bash dedup hints conflict with other compression | `token-goat compress` can be called as dedup-vs-hint filter; one-call access to cached output |
|
|
@@ -12,7 +12,7 @@ init_define_import_meta_env();
|
|
|
12
12
|
import { createRequire } from "node:module";
|
|
13
13
|
function resolveVersion() {
|
|
14
14
|
if (true) {
|
|
15
|
-
return "2.9.
|
|
15
|
+
return "2.9.4";
|
|
16
16
|
}
|
|
17
17
|
const require2 = createRequire(import.meta.url);
|
|
18
18
|
const pkg = require2("../package.json");
|
|
@@ -2273,6 +2273,8 @@ var CONFIG_DEFAULTS = {
|
|
|
2273
2273
|
// existing users -- see the reread_deny/reread_deny_min_bytes fix's commit message.
|
|
2274
2274
|
reread_deny_min_bytes: 51200,
|
|
2275
2275
|
stable_doc_compacts: true,
|
|
2276
|
+
// Off until measured. This one rewrites what the model reads on a FIRST look at a source file, where -- unlike every re-read mechanism beside it -- the reader has no prior copy to notice an omission against. Its restore rate and edit-error delta cannot be observed until it has run, so the honest default is the one that changes nothing.
|
|
2277
|
+
fold_code_bodies: false,
|
|
2276
2278
|
truncated_read_min_lines: 200,
|
|
2277
2279
|
protect_recent_reads: 4,
|
|
2278
2280
|
warn_unbalanced_shell_quoting: true,
|
|
@@ -2607,6 +2609,8 @@ var PROJECT_LOCKED_SECTIONS = [
|
|
|
2607
2609
|
"screenshot"
|
|
2608
2610
|
];
|
|
2609
2611
|
var PROJECT_LOCKED_KEYS = [
|
|
2612
|
+
// A repository must not be able to decide how much of its own source an agent gets to see. Turning this on folds function bodies out of every Read of this project's files, so a checked-in `.token-goat.toml` setting it true would shrink what a reviewing agent is shown of the very code it came to review -- and the fold is silent about intent, so it reads as normal output. The user's own global config and TOKEN_GOAT_FOLD_CODE_BODIES still set it freely; only the project-supplied layer is refused.
|
|
2613
|
+
"hints.fold_code_bodies",
|
|
2610
2614
|
"image_shrink.max_image_pixels",
|
|
2611
2615
|
"indexing.cross_project_symbols",
|
|
2612
2616
|
"worker.blocked_roots"
|
|
@@ -3019,6 +3023,7 @@ function _buildConfig(raw, projectRaw = {}) {
|
|
|
3019
3023
|
hi.reread_deny = validatedBool(hi_raw["reread_deny"], hi.reread_deny);
|
|
3020
3024
|
hi.reread_deny_min_bytes = validatedIntWithLegacySentinel(hi_raw["reread_deny_min_bytes"], hi.reread_deny_min_bytes, 2048, ...boundsOf("hints.reread_deny_min_bytes"));
|
|
3021
3025
|
hi.stable_doc_compacts = validatedBool(hi_raw["stable_doc_compacts"], hi.stable_doc_compacts);
|
|
3026
|
+
hi.fold_code_bodies = validatedBool(hi_raw["fold_code_bodies"], hi.fold_code_bodies);
|
|
3022
3027
|
hi.truncated_read_min_lines = validatedInt(hi_raw["truncated_read_min_lines"], hi.truncated_read_min_lines, ...boundsOf("hints.truncated_read_min_lines"));
|
|
3023
3028
|
hi.protect_recent_reads = validatedInt(hi_raw["protect_recent_reads"], hi.protect_recent_reads, ...boundsOf("hints.protect_recent_reads"));
|
|
3024
3029
|
hi.warn_unbalanced_shell_quoting = validatedBool(hi_raw["warn_unbalanced_shell_quoting"], hi.warn_unbalanced_shell_quoting);
|
|
@@ -3047,6 +3052,7 @@ function _buildConfig(raw, projectRaw = {}) {
|
|
|
3047
3052
|
hi.min_file_lines_for_hint = envInt("TOKEN_GOAT_MIN_FILE_LINES_FOR_HINT", hi.min_file_lines_for_hint, ...boundsOf("hints.min_file_lines_for_hint"));
|
|
3048
3053
|
hi.git_hint_max_ms = envInt("TOKEN_GOAT_GIT_HINT_MAX_MS", hi.git_hint_max_ms, ...boundsOf("hints.git_hint_max_ms"));
|
|
3049
3054
|
hi.stable_doc_compacts = envBool("TOKEN_GOAT_STABLE_DOC_COMPACTS", hi.stable_doc_compacts);
|
|
3055
|
+
hi.fold_code_bodies = envBool("TOKEN_GOAT_FOLD_CODE_BODIES", hi.fold_code_bodies);
|
|
3050
3056
|
hi.context_threshold_advisory = envBool("TOKEN_GOAT_CONTEXT_THRESHOLD_ADVISORY", hi.context_threshold_advisory);
|
|
3051
3057
|
hi.pre_skill_advisory = envBool("TOKEN_GOAT_PRE_SKILL_ADVISORY", hi.pre_skill_advisory);
|
|
3052
3058
|
hi.quiet_hours = envStr("TOKEN_GOAT_QUIET_HOURS", hi.quiet_hours);
|
|
@@ -3214,6 +3220,7 @@ var CONFIG_KEY_ENV_OVERRIDES = {
|
|
|
3214
3220
|
"hints.min_file_lines_for_hint": ["TOKEN_GOAT_MIN_FILE_LINES_FOR_HINT"],
|
|
3215
3221
|
"hints.git_hint_max_ms": ["TOKEN_GOAT_GIT_HINT_MAX_MS"],
|
|
3216
3222
|
"hints.stable_doc_compacts": ["TOKEN_GOAT_STABLE_DOC_COMPACTS"],
|
|
3223
|
+
"hints.fold_code_bodies": ["TOKEN_GOAT_FOLD_CODE_BODIES"],
|
|
3217
3224
|
"hints.context_threshold_advisory": ["TOKEN_GOAT_CONTEXT_THRESHOLD_ADVISORY"],
|
|
3218
3225
|
"hints.pre_skill_advisory": ["TOKEN_GOAT_PRE_SKILL_ADVISORY"],
|
|
3219
3226
|
"hints.quiet_hours": ["TOKEN_GOAT_QUIET_HOURS"],
|
|
@@ -3352,6 +3359,7 @@ function saveConfig(config) {
|
|
|
3352
3359
|
reread_deny: config.hints.reread_deny,
|
|
3353
3360
|
reread_deny_min_bytes: config.hints.reread_deny_min_bytes,
|
|
3354
3361
|
stable_doc_compacts: config.hints.stable_doc_compacts,
|
|
3362
|
+
fold_code_bodies: config.hints.fold_code_bodies,
|
|
3355
3363
|
truncated_read_min_lines: config.hints.truncated_read_min_lines,
|
|
3356
3364
|
protect_recent_reads: config.hints.protect_recent_reads,
|
|
3357
3365
|
prompt_triggers: config.hints.prompt_triggers,
|
|
@@ -6002,6 +6010,7 @@ var _KIND_GROUPS = [
|
|
|
6002
6010
|
"browser_tab_dedup",
|
|
6003
6011
|
"grep:fold",
|
|
6004
6012
|
"read:served_elide",
|
|
6013
|
+
"read:body_fold",
|
|
6005
6014
|
"handoff_create",
|
|
6006
6015
|
"handoff_resolve",
|
|
6007
6016
|
"plan_echo_collapse"
|
|
@@ -6447,6 +6456,8 @@ var KIND_TO_SOURCE = {
|
|
|
6447
6456
|
// with real bytes removed from it. Filing it under the advisory bucket would add non-hint
|
|
6448
6457
|
// savings to hint_stats.ts's savedBytes, which reads by_source[SOURCE_HINT] wholesale.
|
|
6449
6458
|
"read:served_elide": SOURCE_CONTENT,
|
|
6459
|
+
// Same bucket and same reasoning as read:served_elide directly above: a rewrite of a Read that did happen, with real bytes removed, not an advisory about whether to read at all.
|
|
6460
|
+
"read:body_fold": SOURCE_CONTENT,
|
|
6450
6461
|
content_retrieve: SOURCE_CONTENT,
|
|
6451
6462
|
handoff_create: SOURCE_CONTENT,
|
|
6452
6463
|
handoff_resolve: SOURCE_CONTENT
|
|
@@ -3,7 +3,7 @@ const require = __cjsRequire(import.meta.url);
|
|
|
3
3
|
import {
|
|
4
4
|
deliveredOutputBytes,
|
|
5
5
|
wrappedShell
|
|
6
|
-
} from "./token-goat-chunk-
|
|
6
|
+
} from "./token-goat-chunk-EPX5DWGM.mjs";
|
|
7
7
|
import {
|
|
8
8
|
ToolFilter,
|
|
9
9
|
capTokens,
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
recordStat,
|
|
16
16
|
selectFilter,
|
|
17
17
|
shlexSplit
|
|
18
|
-
} from "./token-goat-chunk-
|
|
18
|
+
} from "./token-goat-chunk-3ZR4NLB3.mjs";
|
|
19
19
|
import "./token-goat-chunk-EEIDFMEM.mjs";
|
|
20
20
|
import {
|
|
21
21
|
init_define_import_meta_env
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
storeWebOutput,
|
|
19
19
|
summarizeResidentContext,
|
|
20
20
|
taskListPruneHint
|
|
21
|
-
} from "./token-goat-chunk-
|
|
21
|
+
} from "./token-goat-chunk-FSC44QOH.mjs";
|
|
22
22
|
import {
|
|
23
23
|
BASH_OUTPUT_SUBDIR,
|
|
24
24
|
UNTRUSTED_TOOL_TAG,
|
|
@@ -112,11 +112,11 @@ import {
|
|
|
112
112
|
wasCliReadThisSession,
|
|
113
113
|
wasFileReadThisSession,
|
|
114
114
|
wasHintShown
|
|
115
|
-
} from "./token-goat-chunk-
|
|
115
|
+
} from "./token-goat-chunk-CBO45DYZ.mjs";
|
|
116
116
|
import {
|
|
117
117
|
canRunWrappedShell,
|
|
118
118
|
deliveredOutputBytes
|
|
119
|
-
} from "./token-goat-chunk-
|
|
119
|
+
} from "./token-goat-chunk-EPX5DWGM.mjs";
|
|
120
120
|
import {
|
|
121
121
|
BODY_FIRST_TOOL_RESPONSE_KEYS,
|
|
122
122
|
ENV_KEYS,
|
|
@@ -173,7 +173,7 @@ import {
|
|
|
173
173
|
splitOwnTrailingNotices,
|
|
174
174
|
stripAnsiEscapes,
|
|
175
175
|
toKB
|
|
176
|
-
} from "./token-goat-chunk-
|
|
176
|
+
} from "./token-goat-chunk-3ZR4NLB3.mjs";
|
|
177
177
|
import {
|
|
178
178
|
init_define_import_meta_env
|
|
179
179
|
} from "./token-goat-chunk-A37V4PBF.mjs";
|
|
@@ -703,7 +703,7 @@ init_define_import_meta_env();
|
|
|
703
703
|
var TRACKED_SKILL = "token-goat";
|
|
704
704
|
var MAX_COMMANDS_SHOWN = 8;
|
|
705
705
|
async function currentCommandNames() {
|
|
706
|
-
const { buildProgram } = await import("./token-goat-chunk-
|
|
706
|
+
const { buildProgram } = await import("./token-goat-chunk-GEXHIIKM.mjs");
|
|
707
707
|
return flattenCommandNames(buildCommandManifest(buildProgram()));
|
|
708
708
|
}
|
|
709
709
|
async function recordSkillVersionSnapshot(sessionId, skillName) {
|
|
@@ -86,7 +86,7 @@ import {
|
|
|
86
86
|
runZipRead,
|
|
87
87
|
symbolNamesInFile,
|
|
88
88
|
upsertNote
|
|
89
|
-
} from "./token-goat-chunk-
|
|
89
|
+
} from "./token-goat-chunk-US57I7GN.mjs";
|
|
90
90
|
import {
|
|
91
91
|
DEFAULT_RECONCILE_BUDGET_MS,
|
|
92
92
|
GEMINI_TOOL_NAME_MAP,
|
|
@@ -108,7 +108,7 @@ import {
|
|
|
108
108
|
runReconcile,
|
|
109
109
|
storeWebOutput,
|
|
110
110
|
summarizeResidentContext
|
|
111
|
-
} from "./token-goat-chunk-
|
|
111
|
+
} from "./token-goat-chunk-FSC44QOH.mjs";
|
|
112
112
|
import {
|
|
113
113
|
AGENT_SALT_MARKER,
|
|
114
114
|
BASH_OUTPUT_SUBDIR,
|
|
@@ -175,6 +175,7 @@ import {
|
|
|
175
175
|
getFileEntry,
|
|
176
176
|
getHintStatsSummary,
|
|
177
177
|
getHintStatsTotals,
|
|
178
|
+
getParserFreshness,
|
|
178
179
|
getProjectIndexCounts,
|
|
179
180
|
getSessionFiles,
|
|
180
181
|
getSessionId,
|
|
@@ -193,6 +194,7 @@ import {
|
|
|
193
194
|
isHintCategory,
|
|
194
195
|
isParseSkipEligible,
|
|
195
196
|
isRecallCacheType,
|
|
197
|
+
isSuppressionCategory,
|
|
196
198
|
isWorkerRunning,
|
|
197
199
|
listBlobs,
|
|
198
200
|
listOutputs,
|
|
@@ -238,7 +240,7 @@ import {
|
|
|
238
240
|
visionTokensSavedByText,
|
|
239
241
|
walkProject,
|
|
240
242
|
writeCompact
|
|
241
|
-
} from "./token-goat-chunk-
|
|
243
|
+
} from "./token-goat-chunk-CBO45DYZ.mjs";
|
|
242
244
|
import {
|
|
243
245
|
C,
|
|
244
246
|
CONFIG_KEY_ENV_OVERRIDES,
|
|
@@ -364,7 +366,7 @@ import {
|
|
|
364
366
|
withFileLock,
|
|
365
367
|
withRetryOnLock,
|
|
366
368
|
writeJsonSettings
|
|
367
|
-
} from "./token-goat-chunk-
|
|
369
|
+
} from "./token-goat-chunk-3ZR4NLB3.mjs";
|
|
368
370
|
import {
|
|
369
371
|
__export,
|
|
370
372
|
init_define_import_meta_env
|
|
@@ -6219,6 +6221,38 @@ function checkEmbeddingCoverage(dbPath, rootDir) {
|
|
|
6219
6221
|
};
|
|
6220
6222
|
}
|
|
6221
6223
|
}
|
|
6224
|
+
var PARSER_FRESHNESS_WARN_FRACTION = 0.25;
|
|
6225
|
+
function checkParserFreshness(dbPath, rootDir) {
|
|
6226
|
+
if (!fs12.existsSync(dbPath)) {
|
|
6227
|
+
return { name: "Parser freshness", status: "ok", message: "no database yet" };
|
|
6228
|
+
}
|
|
6229
|
+
try {
|
|
6230
|
+
const { indexedFiles, currentFiles } = getParserFreshness(dbPath, PARSER_FINGERPRINT, rootDir);
|
|
6231
|
+
if (indexedFiles === 0) {
|
|
6232
|
+
return { name: "Parser freshness", status: "ok", message: "no indexed files yet" };
|
|
6233
|
+
}
|
|
6234
|
+
const stale = indexedFiles - currentFiles;
|
|
6235
|
+
const pct3 = Math.round(stale / indexedFiles * 100);
|
|
6236
|
+
if (currentFiles / indexedFiles < 1 - PARSER_FRESHNESS_WARN_FRACTION) {
|
|
6237
|
+
return {
|
|
6238
|
+
name: "Parser freshness",
|
|
6239
|
+
status: "warn",
|
|
6240
|
+
message: `${stale} of ${indexedFiles} indexed file(s) (${pct3}%) were parsed by a different build of the extraction logic, so their symbols are whatever that build extracted \u2014 'symbol', 'read', 'outline' and 'skeleton' answer from those rows, and the read-hook body fold declines on them. Run 'token-goat index' in this project to reparse them; --force is not needed, a parser mismatch reindexes on its own`
|
|
6241
|
+
};
|
|
6242
|
+
}
|
|
6243
|
+
return {
|
|
6244
|
+
name: "Parser freshness",
|
|
6245
|
+
status: "ok",
|
|
6246
|
+
message: `${currentFiles} of ${indexedFiles} indexed file(s) match the running parser`
|
|
6247
|
+
};
|
|
6248
|
+
} catch (err2) {
|
|
6249
|
+
return {
|
|
6250
|
+
name: "Parser freshness",
|
|
6251
|
+
status: "warn",
|
|
6252
|
+
message: `could not query parser freshness: ${extractErrorMessage(err2)}`
|
|
6253
|
+
};
|
|
6254
|
+
}
|
|
6255
|
+
}
|
|
6222
6256
|
var DIRTY_QUEUE_BACKLOG_WARN_THRESHOLD = 500;
|
|
6223
6257
|
function checkDirtyQueueHealth(dataDir2) {
|
|
6224
6258
|
let pendingCount = 0;
|
|
@@ -6454,6 +6488,7 @@ var LOCKED_BOOLEAN_SAFE_VALUE = {
|
|
|
6454
6488
|
"screenshot.block_private_targets": true,
|
|
6455
6489
|
"network.offline": true,
|
|
6456
6490
|
"indexing.cross_project_symbols": false,
|
|
6491
|
+
"hints.fold_code_bodies": false,
|
|
6457
6492
|
"webfetch.compress_bodies": true
|
|
6458
6493
|
};
|
|
6459
6494
|
function lockedEnvOverridableKeys() {
|
|
@@ -6656,6 +6691,7 @@ function runDoctor(dataDir2, configPath2, rootDir, processes) {
|
|
|
6656
6691
|
results.push(checkConfigValid(actualConfigPath));
|
|
6657
6692
|
results.push(checkEmbeddings(loadConfig(rootDir)));
|
|
6658
6693
|
results.push(checkEmbeddingCoverage(path12.join(actualDataDir, "global.db"), rootDir));
|
|
6694
|
+
results.push(checkParserFreshness(path12.join(actualDataDir, "global.db"), rootDir));
|
|
6659
6695
|
for (const result of checkSecurityPosture(loadConfig(rootDir), actualDataDir)) results.push(result);
|
|
6660
6696
|
results.push(checkDiskSpace(actualDataDir));
|
|
6661
6697
|
const copilotResult = checkCopilotCli(copilotCliConfigPath(), copilotCliScriptPath());
|
|
@@ -15286,15 +15322,18 @@ function suppressedCell(row) {
|
|
|
15286
15322
|
if (!row.suppressed) return "no";
|
|
15287
15323
|
return row.suppressionPermanent ? "yes (permanent)" : "yes";
|
|
15288
15324
|
}
|
|
15325
|
+
function efficacyCell(row) {
|
|
15326
|
+
const pct3 = row.efficacyPct === null ? "n/a" : `${row.efficacyPct}%`;
|
|
15327
|
+
return isSuppressionCategory(row.category) ? `${pct3} *` : pct3;
|
|
15328
|
+
}
|
|
15289
15329
|
function printSummary(rows) {
|
|
15290
15330
|
const w = (text) => {
|
|
15291
15331
|
process.stdout.write(text);
|
|
15292
15332
|
};
|
|
15293
|
-
w(pad("category", 22) + pad("emitted", 9) + pad("acted-on", 10) + pad("efficacy",
|
|
15333
|
+
w(pad("category", 22) + pad("emitted", 9) + pad("acted-on", 10) + pad("efficacy", 12) + pad("suppressed", 17) + pad("manual+", 9) + pad("manual-", 9) + "spent\n");
|
|
15294
15334
|
for (const row of rows) {
|
|
15295
|
-
const pct3 = row.efficacyPct === null ? "n/a" : `${row.efficacyPct}%`;
|
|
15296
15335
|
w(
|
|
15297
|
-
pad(row.category, 22) + pad(String(row.emitted), 9) + pad(String(row.actedOn), 10) + pad(
|
|
15336
|
+
pad(row.category, 22) + pad(String(row.emitted), 9) + pad(String(row.actedOn), 10) + pad(efficacyCell(row), 12) + pad(suppressedCell(row), 17) + pad(String(row.manualEffective), 9) + pad(String(row.manualIneffective), 9) + formatSpentCell(row) + "\n"
|
|
15298
15337
|
);
|
|
15299
15338
|
}
|
|
15300
15339
|
}
|
|
@@ -15335,6 +15374,11 @@ function runHintStatsCommand(opts = {}) {
|
|
|
15335
15374
|
process.stdout.write("No hint emissions recorded yet \u2014 the zeros below are absence of data, not measured ineffectiveness.\n");
|
|
15336
15375
|
}
|
|
15337
15376
|
printSummary(rows);
|
|
15377
|
+
if (rows.some((r) => isSuppressionCategory(r.category) && r.emitted > 0)) {
|
|
15378
|
+
process.stdout.write(
|
|
15379
|
+
'\n* Scored on an absence: these hints ask the agent NOT to do something, so a window that expires with no re-read counts as compliance. Their percentage is not comparable with the unmarked rows, which only count when the agent runs the command the hint named. A high starred figure means "the warned-against read was not seen", not "this hint persuaded anyone".\n'
|
|
15380
|
+
);
|
|
15381
|
+
}
|
|
15338
15382
|
const permanent = rows.filter((r) => r.suppressionPermanent);
|
|
15339
15383
|
if (permanent.length > 0) {
|
|
15340
15384
|
const names = permanent.map((r) => r.category).join(", ");
|
|
@@ -15667,7 +15711,7 @@ async function cmdMcpServe() {
|
|
|
15667
15711
|
let StdioServerTransport;
|
|
15668
15712
|
try {
|
|
15669
15713
|
;
|
|
15670
|
-
({ createMcpServer } = await import("./token-goat-chunk-
|
|
15714
|
+
({ createMcpServer } = await import("./token-goat-chunk-OZAEJQ7L.mjs"));
|
|
15671
15715
|
({ StdioServerTransport } = await import("./token-goat-chunk-EVC4TOLE.mjs"));
|
|
15672
15716
|
} catch (err2) {
|
|
15673
15717
|
process.stderr.write(
|
|
@@ -15692,7 +15736,7 @@ async function cmdHook(event, opts) {
|
|
|
15692
15736
|
if (typeof opts.harness === "string" && opts.harness.length > 0) {
|
|
15693
15737
|
process.env[ENV_KEYS.HARNESS_OVERRIDE] = opts.harness;
|
|
15694
15738
|
}
|
|
15695
|
-
const { relay } = await import("./token-goat-chunk-
|
|
15739
|
+
const { relay } = await import("./token-goat-chunk-EQKNR7CN.mjs");
|
|
15696
15740
|
await relay(event);
|
|
15697
15741
|
}
|
|
15698
15742
|
async function cmdInstall(opts) {
|
|
@@ -16626,7 +16670,7 @@ function emitExtraFileArgsNote(command, first, extras, opts = {}) {
|
|
|
16626
16670
|
}
|
|
16627
16671
|
async function cmdCompress(opts) {
|
|
16628
16672
|
try {
|
|
16629
|
-
const bashRunner = await import("./token-goat-chunk-
|
|
16673
|
+
const bashRunner = await import("./token-goat-chunk-5BHXZEOI.mjs");
|
|
16630
16674
|
if (opts.compress === false) {
|
|
16631
16675
|
process.exitCode = bashRunner.runRaw(opts.cmd, parseTimeout(opts.timeout, bashRunner.DEFAULT_TIMEOUT_SECONDS));
|
|
16632
16676
|
return;
|
|
@@ -96,7 +96,7 @@ import {
|
|
|
96
96
|
toDisplayPath,
|
|
97
97
|
toKB,
|
|
98
98
|
withFileLock
|
|
99
|
-
} from "./token-goat-chunk-
|
|
99
|
+
} from "./token-goat-chunk-3ZR4NLB3.mjs";
|
|
100
100
|
import {
|
|
101
101
|
registerReset
|
|
102
102
|
} from "./token-goat-chunk-EEIDFMEM.mjs";
|
|
@@ -6057,6 +6057,18 @@ function getEmbeddingCoverage(dbPath, rootDir) {
|
|
|
6057
6057
|
embeddedFiles: countScoped("SELECT COUNT(DISTINCT file_path) as c FROM chunks", "file_path")
|
|
6058
6058
|
};
|
|
6059
6059
|
}
|
|
6060
|
+
function getParserFreshness(dbPath, parserSha, rootDir) {
|
|
6061
|
+
const db = getDb(dbPath);
|
|
6062
|
+
const count = (extra, params) => {
|
|
6063
|
+
if (rootDir === void 0) {
|
|
6064
|
+
return db.prepare(`SELECT COUNT(*) as c FROM files${extra === "" ? "" : ` WHERE ${extra}`}`).get(...params).c;
|
|
6065
|
+
}
|
|
6066
|
+
const scope = projectScopeClause("path");
|
|
6067
|
+
const where = extra === "" ? scope.clause : `${scope.clause} AND ${extra}`;
|
|
6068
|
+
return db.prepare(`SELECT COUNT(*) as c FROM files WHERE ${where}`).get(...scope.params(rootDir), ...params).c;
|
|
6069
|
+
};
|
|
6070
|
+
return { indexedFiles: count("", []), currentFiles: count("parser_sha = ?", [parserSha]) };
|
|
6071
|
+
}
|
|
6060
6072
|
function isIndexEmptyForProject(dbPath, rootDir) {
|
|
6061
6073
|
if (!fs4.existsSync(dbPath)) return true;
|
|
6062
6074
|
try {
|
|
@@ -11081,6 +11093,10 @@ function _pathPriorityPenalty(filePath) {
|
|
|
11081
11093
|
return 0;
|
|
11082
11094
|
}
|
|
11083
11095
|
|
|
11096
|
+
// src/parser_fingerprint.ts
|
|
11097
|
+
init_define_import_meta_env();
|
|
11098
|
+
var PARSER_FINGERPRINT = "4659c517f042a452";
|
|
11099
|
+
|
|
11084
11100
|
// src/hooks_read.ts
|
|
11085
11101
|
init_define_import_meta_env();
|
|
11086
11102
|
import * as fs17 from "node:fs";
|
|
@@ -12703,6 +12719,120 @@ function getOrCreateSidecar(rawBytes, cacheRoot, opts = {}) {
|
|
|
12703
12719
|
return [sidecarPath, true];
|
|
12704
12720
|
}
|
|
12705
12721
|
|
|
12722
|
+
// src/code_fold.ts
|
|
12723
|
+
init_define_import_meta_env();
|
|
12724
|
+
var FOLDABLE_KINDS = /* @__PURE__ */ new Set(["function", "method", "func", "def", "fn", "procedure", "constructor", "variable"]);
|
|
12725
|
+
var MIN_FOLDED_ROWS = 3;
|
|
12726
|
+
function planBodyFolds(rows, spans, keep, minSpan) {
|
|
12727
|
+
if (rows.length === 0 || spans.length === 0 || keep < 1) return [];
|
|
12728
|
+
const rowAt = /* @__PURE__ */ new Map();
|
|
12729
|
+
for (let i = 0; i < rows.length; i++) {
|
|
12730
|
+
const row = rows[i];
|
|
12731
|
+
if (row !== void 0) rowAt.set(row.no, i);
|
|
12732
|
+
}
|
|
12733
|
+
const ordered = [...spans].sort((a, b) => a.lineStart - b.lineStart || b.lineEnd - a.lineEnd);
|
|
12734
|
+
const folds = [];
|
|
12735
|
+
let foldedThrough = -1;
|
|
12736
|
+
for (const span of ordered) {
|
|
12737
|
+
if (!FOLDABLE_KINDS.has(span.kind)) continue;
|
|
12738
|
+
if (span.lineEnd - span.lineStart + 1 < minSpan) continue;
|
|
12739
|
+
if (span.lineStart <= foldedThrough) continue;
|
|
12740
|
+
const firstFolded = span.lineStart + keep;
|
|
12741
|
+
if (firstFolded > span.lineEnd) continue;
|
|
12742
|
+
let startIdx = -1;
|
|
12743
|
+
let endIdx = -1;
|
|
12744
|
+
for (let line = firstFolded; line <= span.lineEnd; line++) {
|
|
12745
|
+
const idx = rowAt.get(line);
|
|
12746
|
+
if (idx === void 0) continue;
|
|
12747
|
+
if (startIdx < 0) startIdx = idx;
|
|
12748
|
+
endIdx = idx;
|
|
12749
|
+
}
|
|
12750
|
+
if (startIdx < 0 || endIdx < startIdx) continue;
|
|
12751
|
+
const len = endIdx - startIdx + 1;
|
|
12752
|
+
const firstRow = rows[startIdx];
|
|
12753
|
+
const lastRow = rows[endIdx];
|
|
12754
|
+
if (firstRow === void 0 || lastRow === void 0) continue;
|
|
12755
|
+
if (lastRow.no - firstRow.no + 1 !== len) continue;
|
|
12756
|
+
if (len < MIN_FOLDED_ROWS) continue;
|
|
12757
|
+
folds.push({ startIdx, len, name: span.name, kind: "body", firstLine: firstRow.no, lastLine: lastRow.no });
|
|
12758
|
+
foldedThrough = span.lineEnd;
|
|
12759
|
+
}
|
|
12760
|
+
return folds;
|
|
12761
|
+
}
|
|
12762
|
+
var SLASH_STAR = { line: ["//"], open: "/*", close: "*/" };
|
|
12763
|
+
var HASH = { line: ["#"] };
|
|
12764
|
+
var DOUBLE_DASH = { line: ["--"] };
|
|
12765
|
+
var COMMENT_SYNTAX = new Map([
|
|
12766
|
+
...["ts", "tsx", "js", "jsx", "mjs", "cjs", "java", "c", "h", "cc", "cpp", "hpp", "cs", "go", "rs", "swift", "kt", "scala", "php", "dart"].map(
|
|
12767
|
+
(ext) => [ext, SLASH_STAR]
|
|
12768
|
+
),
|
|
12769
|
+
...["py", "rb", "sh", "bash", "zsh", "pl", "r"].map((ext) => [ext, HASH]),
|
|
12770
|
+
...["sql", "lua", "hs"].map((ext) => [ext, DOUBLE_DASH])
|
|
12771
|
+
]);
|
|
12772
|
+
function commentSyntaxFor(path26) {
|
|
12773
|
+
const dot = path26.lastIndexOf(".");
|
|
12774
|
+
if (dot < 0) return null;
|
|
12775
|
+
return COMMENT_SYNTAX.get(path26.slice(dot + 1).toLowerCase()) ?? null;
|
|
12776
|
+
}
|
|
12777
|
+
function planCommentFolds(rows, syntax, commentKeep, commentMinBlock, occupied) {
|
|
12778
|
+
if (syntax === null || rows.length === 0 || commentKeep < 1 || commentMinBlock < 1) return [];
|
|
12779
|
+
const folds = [];
|
|
12780
|
+
let i = 0;
|
|
12781
|
+
let inBlock = false;
|
|
12782
|
+
while (i < rows.length) {
|
|
12783
|
+
const start = i;
|
|
12784
|
+
while (i < rows.length) {
|
|
12785
|
+
const text = rows[i]?.text ?? "";
|
|
12786
|
+
const trimmed = text.trim();
|
|
12787
|
+
if (inBlock) {
|
|
12788
|
+
if (syntax.close !== void 0 && trimmed.includes(syntax.close)) inBlock = false;
|
|
12789
|
+
i++;
|
|
12790
|
+
continue;
|
|
12791
|
+
}
|
|
12792
|
+
if (syntax.open !== void 0 && trimmed.startsWith(syntax.open)) {
|
|
12793
|
+
if (syntax.close === void 0 || !trimmed.slice(syntax.open.length).includes(syntax.close)) inBlock = true;
|
|
12794
|
+
i++;
|
|
12795
|
+
continue;
|
|
12796
|
+
}
|
|
12797
|
+
if (syntax.line.some((p) => trimmed.startsWith(p))) {
|
|
12798
|
+
i++;
|
|
12799
|
+
continue;
|
|
12800
|
+
}
|
|
12801
|
+
break;
|
|
12802
|
+
}
|
|
12803
|
+
const runLen = i - start;
|
|
12804
|
+
if (runLen >= commentMinBlock) {
|
|
12805
|
+
const foldStart = start + commentKeep;
|
|
12806
|
+
const len = i - foldStart;
|
|
12807
|
+
const firstRow = rows[foldStart];
|
|
12808
|
+
const lastRow = rows[i - 1];
|
|
12809
|
+
if (len >= MIN_FOLDED_ROWS && firstRow !== void 0 && lastRow !== void 0 && lastRow.no - firstRow.no + 1 === len) {
|
|
12810
|
+
let clear = true;
|
|
12811
|
+
for (let k = foldStart; k < i; k++) {
|
|
12812
|
+
if (occupied.has(k)) {
|
|
12813
|
+
clear = false;
|
|
12814
|
+
break;
|
|
12815
|
+
}
|
|
12816
|
+
}
|
|
12817
|
+
if (clear) folds.push({ startIdx: foldStart, len, name: "comment", kind: "comment", firstLine: firstRow.no, lastLine: lastRow.no });
|
|
12818
|
+
}
|
|
12819
|
+
}
|
|
12820
|
+
if (i === start) i++;
|
|
12821
|
+
}
|
|
12822
|
+
return folds;
|
|
12823
|
+
}
|
|
12824
|
+
function mergeFolds(body, comment) {
|
|
12825
|
+
const merged = [...body, ...comment].sort((a, b) => a.startIdx - b.startIdx || b.len - a.len);
|
|
12826
|
+
const out = [];
|
|
12827
|
+
let through = -1;
|
|
12828
|
+
for (const fold of merged) {
|
|
12829
|
+
if (fold.startIdx <= through) continue;
|
|
12830
|
+
out.push(fold);
|
|
12831
|
+
through = fold.startIdx + fold.len - 1;
|
|
12832
|
+
}
|
|
12833
|
+
return out;
|
|
12834
|
+
}
|
|
12835
|
+
|
|
12706
12836
|
// src/hooks_read.ts
|
|
12707
12837
|
function isTsConfigFile(basename12) {
|
|
12708
12838
|
const lower = basename12.toLowerCase();
|
|
@@ -12757,6 +12887,15 @@ function readIntToolInput(event, key) {
|
|
|
12757
12887
|
}
|
|
12758
12888
|
return void 0;
|
|
12759
12889
|
}
|
|
12890
|
+
function readStartLine(event) {
|
|
12891
|
+
const resp = event.raw["tool_response"];
|
|
12892
|
+
if (resp === null || typeof resp !== "object") return 1;
|
|
12893
|
+
const file = resp["file"];
|
|
12894
|
+
if (file === null || typeof file !== "object") return 1;
|
|
12895
|
+
const start = file["startLine"];
|
|
12896
|
+
if (typeof start === "number" && Number.isSafeInteger(start) && start >= 1) return start;
|
|
12897
|
+
return 1;
|
|
12898
|
+
}
|
|
12760
12899
|
var SLICE_ESTIMATE_SCAN_CAP_BYTES = 2 * 1024 * 1024;
|
|
12761
12900
|
var NEAR_SINGLE_LINE_SCAN_THRESHOLD = 20;
|
|
12762
12901
|
function scanRequestedSlice(absPath, offset, limit) {
|
|
@@ -13046,7 +13185,8 @@ function preReadHandlerInner(event) {
|
|
|
13046
13185
|
} catch {
|
|
13047
13186
|
}
|
|
13048
13187
|
}
|
|
13049
|
-
|
|
13188
|
+
const compactUnrangedRead = readIntToolInput(event, "offset") === void 0 && readIntToolInput(event, "limit") === void 0;
|
|
13189
|
+
if (event.toolName !== "Grep" && compactUnrangedRead && loadConfig().hints.stable_doc_compacts && _isDocFile(normalized)) {
|
|
13050
13190
|
const compactPath = compactPathFor(normalized);
|
|
13051
13191
|
if (isCompactFresh(compactPath, normalized)) {
|
|
13052
13192
|
const compactBody = readCompactBody(compactPath);
|
|
@@ -13056,7 +13196,7 @@ function preReadHandlerInner(event) {
|
|
|
13056
13196
|
const savedBytes = counterfactualCredit(fullSize, compactBody.length);
|
|
13057
13197
|
recordStat("session_hint", savedBytes, savedTokensFromBytes(savedBytes));
|
|
13058
13198
|
return denyOutput(
|
|
13059
|
-
"Serving the extractive compact sidecar in place of the full file (source unchanged since the last `compact-doc` build):\n\n" + fenceUntrustedFileContent(compactBody) + '\n\
|
|
13199
|
+
"Serving the extractive compact sidecar in place of the full file (source unchanged since the last `compact-doc` build):\n\n" + fenceUntrustedFileContent(compactBody) + '\n\nThis is a lossy extract, not the whole file: front matter and any prose before the first heading are dropped entirely, each section is cut to its opening sentences, and long code fences are truncated. If you need content it left out, re-read with offset/limit for a line window, or `token-goat section "' + shown + '::Heading"` for one section in full. Use `token-goat compact-doc "' + shown + '" --force` to rebuild it, or `token-goat compact-doc "' + shown + '" --show` to view it directly. ' + editAnywayHint(normalized)
|
|
13060
13200
|
);
|
|
13061
13201
|
}
|
|
13062
13202
|
}
|
|
@@ -13521,7 +13661,7 @@ function alreadyServedOutputId(event, normalized) {
|
|
|
13521
13661
|
return null;
|
|
13522
13662
|
}
|
|
13523
13663
|
}
|
|
13524
|
-
function recordReadAsServedOutput(event) {
|
|
13664
|
+
function recordReadAsServedOutput(event, deliveredRaw = null) {
|
|
13525
13665
|
try {
|
|
13526
13666
|
const cfg = loadConfig().bash_compress;
|
|
13527
13667
|
if (!cfg.enabled) return;
|
|
@@ -13531,7 +13671,7 @@ function recordReadAsServedOutput(event) {
|
|
|
13531
13671
|
if (isImagePath(normalized)) return;
|
|
13532
13672
|
const respText = extractReadOutput(event.raw);
|
|
13533
13673
|
if (respText.includes("[Truncated:") || respText.includes("Truncated: PARTIAL view")) return;
|
|
13534
|
-
const served = readWindowFromDisk(event, normalized);
|
|
13674
|
+
const served = deliveredRaw ?? readWindowFromDisk(event, normalized);
|
|
13535
13675
|
if (served === null) return;
|
|
13536
13676
|
if (Buffer.byteLength(served, "utf-8") < Math.max(cfg.cache_min_bytes, IDENTICAL_READ_MIN_BODY_BYTES)) return;
|
|
13537
13677
|
const id = storeBashOutputSync("Read " + normalized, served, 0, getCwd(event) ?? process.cwd());
|
|
@@ -13540,7 +13680,7 @@ function recordReadAsServedOutput(event) {
|
|
|
13540
13680
|
}
|
|
13541
13681
|
}
|
|
13542
13682
|
var READ_NUMBERED_ROW_RE = /^\s*(\d+)[\t→](.*)$/;
|
|
13543
|
-
function parseNumberedReadResult(respText) {
|
|
13683
|
+
function parseNumberedReadResult(respText, firstLine = 1) {
|
|
13544
13684
|
const lines2 = respText.split("\n");
|
|
13545
13685
|
const header = [];
|
|
13546
13686
|
const rows = [];
|
|
@@ -13565,7 +13705,10 @@ function parseNumberedReadResult(respText) {
|
|
|
13565
13705
|
rows.push({ no: prev.no + 1, text: m[2] ?? "", raw: line });
|
|
13566
13706
|
}
|
|
13567
13707
|
for (; i < lines2.length; i++) trailer.push(lines2[i] ?? "");
|
|
13568
|
-
|
|
13708
|
+
if (rows.length > 0) return { header, rows, trailer };
|
|
13709
|
+
if (respText === "") return null;
|
|
13710
|
+
const plain = lines2.map((text, idx) => ({ no: firstLine + idx, text, raw: text }));
|
|
13711
|
+
return { header: [], rows: plain, trailer: [] };
|
|
13569
13712
|
}
|
|
13570
13713
|
var SERVED_RUN_COMPARISON_BUDGET = 4e5;
|
|
13571
13714
|
var SERVED_RUN_ANCHORS_PER_LINE = 32;
|
|
@@ -13620,7 +13763,7 @@ function elideAlreadyServedLines(event, respText) {
|
|
|
13620
13763
|
if (respText.includes("[Truncated:") || respText.includes("Truncated: PARTIAL view")) return null;
|
|
13621
13764
|
const ids = getFileServedOutputs(normalized);
|
|
13622
13765
|
if (ids.length === 0) return null;
|
|
13623
|
-
const parsed = parseNumberedReadResult(respText);
|
|
13766
|
+
const parsed = parseNumberedReadResult(respText, readStartLine(event));
|
|
13624
13767
|
if (parsed === null) return null;
|
|
13625
13768
|
if (redactSecrets(respText).count > 0) return null;
|
|
13626
13769
|
const bodies = [];
|
|
@@ -13694,11 +13837,91 @@ function elideAlreadyServedLines(event, respText) {
|
|
|
13694
13837
|
}
|
|
13695
13838
|
return emitRewrite(rewritten, "read", { kind: "read:served_elide", originalBytes });
|
|
13696
13839
|
}
|
|
13840
|
+
var BODY_FOLD_KEEP_LINES = 8;
|
|
13841
|
+
var BODY_FOLD_MIN_SPAN = 20;
|
|
13842
|
+
var COMMENT_FOLD_KEEP_LINES = 2;
|
|
13843
|
+
var COMMENT_FOLD_MIN_BLOCK = 12;
|
|
13844
|
+
var BODY_FOLD_SYMBOL_LIMIT = 1e4;
|
|
13845
|
+
function bodyFoldNotice(name, firstLine, lastLine, shownPath) {
|
|
13846
|
+
const n = lastLine - firstLine + 1;
|
|
13847
|
+
return `... ${n} more lines of ${name} (${firstLine}-${lastLine}) folded -- token-goat read "${shownPath}::${name}"`;
|
|
13848
|
+
}
|
|
13849
|
+
function commentFoldNotice(firstLine, lastLine, shownPath) {
|
|
13850
|
+
const n = lastLine - firstLine + 1;
|
|
13851
|
+
return `... ${n} more comment lines (${firstLine}-${lastLine}) folded -- Read "${shownPath}" with offset=${firstLine}, limit=${n}`;
|
|
13852
|
+
}
|
|
13853
|
+
function foldCodeBodies(event, respText) {
|
|
13854
|
+
if (!loadConfig().hints.fold_code_bodies) return null;
|
|
13855
|
+
const filePath = getFilePath(event);
|
|
13856
|
+
if (filePath === void 0) return null;
|
|
13857
|
+
const normalized = normalizePath(filePath);
|
|
13858
|
+
if (isImagePath(normalized)) return null;
|
|
13859
|
+
if (readIntToolInput(event, "offset") !== void 0 || readIntToolInput(event, "limit") !== void 0) return null;
|
|
13860
|
+
if (respText.includes("[Truncated:") || respText.includes("Truncated: PARTIAL view")) return null;
|
|
13861
|
+
if (redactSecrets(respText).count > 0) return null;
|
|
13862
|
+
const parsed = parseNumberedReadResult(respText, readStartLine(event));
|
|
13863
|
+
if (parsed === null) return null;
|
|
13864
|
+
let spans = [];
|
|
13865
|
+
try {
|
|
13866
|
+
const entry = getFileEntry(normalized);
|
|
13867
|
+
if (entry !== null && entry.sha !== "" && entry.sha === fingerprintFile(normalized) && entry.parserSha === PARSER_FINGERPRINT) {
|
|
13868
|
+
spans = querySymbols({ filePath: normalized, limit: BODY_FOLD_SYMBOL_LIMIT });
|
|
13869
|
+
}
|
|
13870
|
+
} catch {
|
|
13871
|
+
spans = [];
|
|
13872
|
+
}
|
|
13873
|
+
const bodyFolds = planBodyFolds(parsed.rows, spans, BODY_FOLD_KEEP_LINES, BODY_FOLD_MIN_SPAN);
|
|
13874
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
13875
|
+
for (const fold of bodyFolds) for (let i = fold.startIdx; i < fold.startIdx + fold.len; i++) claimed.add(i);
|
|
13876
|
+
const commentFolds = planCommentFolds(
|
|
13877
|
+
parsed.rows,
|
|
13878
|
+
commentSyntaxFor(normalized),
|
|
13879
|
+
COMMENT_FOLD_KEEP_LINES,
|
|
13880
|
+
COMMENT_FOLD_MIN_BLOCK,
|
|
13881
|
+
claimed
|
|
13882
|
+
);
|
|
13883
|
+
const folds = mergeFolds(bodyFolds, commentFolds);
|
|
13884
|
+
if (folds.length === 0) return null;
|
|
13885
|
+
const shown = displaySafePath(normalized);
|
|
13886
|
+
const out = [...parsed.header];
|
|
13887
|
+
const rawOut = [];
|
|
13888
|
+
let at = 0;
|
|
13889
|
+
for (const fold of folds) {
|
|
13890
|
+
for (let i = at; i < fold.startIdx; i++) {
|
|
13891
|
+
out.push(parsed.rows[i]?.raw ?? "");
|
|
13892
|
+
rawOut.push(parsed.rows[i]?.text ?? "");
|
|
13893
|
+
}
|
|
13894
|
+
out.push(fold.kind === "comment" ? commentFoldNotice(fold.firstLine, fold.lastLine, shown) : bodyFoldNotice(fold.name, fold.firstLine, fold.lastLine, shown));
|
|
13895
|
+
at = fold.startIdx + fold.len;
|
|
13896
|
+
}
|
|
13897
|
+
for (let i = at; i < parsed.rows.length; i++) {
|
|
13898
|
+
out.push(parsed.rows[i]?.raw ?? "");
|
|
13899
|
+
rawOut.push(parsed.rows[i]?.text ?? "");
|
|
13900
|
+
}
|
|
13901
|
+
out.push(...parsed.trailer);
|
|
13902
|
+
const rewritten = out.join("\n");
|
|
13903
|
+
const originalBytes = Buffer.byteLength(respText, "utf-8");
|
|
13904
|
+
if (!isRewriteWorthwhile({
|
|
13905
|
+
originalBytes,
|
|
13906
|
+
rewrittenBytes: Buffer.byteLength(rewritten, "utf-8"),
|
|
13907
|
+
noticeBytes: 0,
|
|
13908
|
+
minNetSavingsBytes: resolveMinNetSavingsBytes()
|
|
13909
|
+
})) {
|
|
13910
|
+
return null;
|
|
13911
|
+
}
|
|
13912
|
+
return {
|
|
13913
|
+
output: emitRewrite(rewritten, "read", { kind: "read:body_fold", originalBytes }),
|
|
13914
|
+
deliveredRaw: rawOut.join("\n")
|
|
13915
|
+
};
|
|
13916
|
+
}
|
|
13697
13917
|
function postReadHandler(event) {
|
|
13698
|
-
const
|
|
13699
|
-
const
|
|
13700
|
-
|
|
13701
|
-
|
|
13918
|
+
const respText = extractReadOutput(event.raw);
|
|
13919
|
+
const elided = elideAlreadyServedLines(event, respText);
|
|
13920
|
+
const folded = elided === null ? foldCodeBodies(event, respText) : null;
|
|
13921
|
+
const rewrite = elided ?? folded?.output ?? null;
|
|
13922
|
+
const out = applyHintTracking(event, postReadHandlerInner(event, rewrite !== null), classifyReadHint);
|
|
13923
|
+
recordReadAsServedOutput(event, folded?.deliveredRaw ?? null);
|
|
13924
|
+
return rewrite ?? out;
|
|
13702
13925
|
}
|
|
13703
13926
|
registerHook("post_tool_use", postReadHandler, { toolName: "Read" });
|
|
13704
13927
|
|
|
@@ -14521,10 +14744,6 @@ function getTrackedFiles(cwd = process.cwd()) {
|
|
|
14521
14744
|
}
|
|
14522
14745
|
}
|
|
14523
14746
|
|
|
14524
|
-
// src/parser_fingerprint.ts
|
|
14525
|
-
init_define_import_meta_env();
|
|
14526
|
-
var PARSER_FINGERPRINT = "4659c517f042a452";
|
|
14527
|
-
|
|
14528
14747
|
// src/parser.ts
|
|
14529
14748
|
init_define_import_meta_env();
|
|
14530
14749
|
import * as fs22 from "node:fs";
|
|
@@ -21387,12 +21606,14 @@ export {
|
|
|
21387
21606
|
detectWalkMode,
|
|
21388
21607
|
getProjectIndexCounts,
|
|
21389
21608
|
getEmbeddingCoverage,
|
|
21609
|
+
getParserFreshness,
|
|
21390
21610
|
isIndexEmptyForProject,
|
|
21391
21611
|
emptyIndexMessage,
|
|
21392
21612
|
isHintCategory,
|
|
21393
21613
|
classifyBashHint,
|
|
21394
21614
|
classifyEditHint,
|
|
21395
21615
|
applyHintTracking,
|
|
21616
|
+
isSuppressionCategory,
|
|
21396
21617
|
meetsSavingsFloor,
|
|
21397
21618
|
getHintStatsSummary,
|
|
21398
21619
|
getHintStatsTotals,
|
|
@@ -21542,6 +21763,7 @@ export {
|
|
|
21542
21763
|
embeddingsDepsAvailable,
|
|
21543
21764
|
searchEvidenceSemantically,
|
|
21544
21765
|
buildDeltaCapsule,
|
|
21766
|
+
PARSER_FINGERPRINT,
|
|
21545
21767
|
buildLineDiff,
|
|
21546
21768
|
confirmAndApply,
|
|
21547
21769
|
findContentDuplicates,
|
|
@@ -21563,7 +21785,6 @@ export {
|
|
|
21563
21785
|
fenceWithMatches,
|
|
21564
21786
|
scanAndRecord,
|
|
21565
21787
|
getTrackedFiles,
|
|
21566
|
-
PARSER_FINGERPRINT,
|
|
21567
21788
|
stripLeadingAttributes2 as stripLeadingAttributes,
|
|
21568
21789
|
IMPORT_RE2 as IMPORT_RE,
|
|
21569
21790
|
yamlOpenQuoteAfter,
|
|
@@ -2,7 +2,7 @@ import { createRequire as __cjsRequire } from 'node:module';
|
|
|
2
2
|
const require = __cjsRequire(import.meta.url);
|
|
3
3
|
import {
|
|
4
4
|
detectHarness
|
|
5
|
-
} from "./token-goat-chunk-
|
|
5
|
+
} from "./token-goat-chunk-3ZR4NLB3.mjs";
|
|
6
6
|
import {
|
|
7
7
|
init_define_import_meta_env
|
|
8
8
|
} from "./token-goat-chunk-A37V4PBF.mjs";
|
|
@@ -4,14 +4,14 @@ import {
|
|
|
4
4
|
buildEvent,
|
|
5
5
|
relay,
|
|
6
6
|
relayInProcess
|
|
7
|
-
} from "./token-goat-chunk-
|
|
7
|
+
} from "./token-goat-chunk-63XEQMNU.mjs";
|
|
8
8
|
import {
|
|
9
9
|
MAX_STDIN_BYTES,
|
|
10
10
|
readStdinJson
|
|
11
|
-
} from "./token-goat-chunk-
|
|
12
|
-
import "./token-goat-chunk-
|
|
13
|
-
import "./token-goat-chunk-
|
|
14
|
-
import "./token-goat-chunk-
|
|
11
|
+
} from "./token-goat-chunk-FSC44QOH.mjs";
|
|
12
|
+
import "./token-goat-chunk-CBO45DYZ.mjs";
|
|
13
|
+
import "./token-goat-chunk-EPX5DWGM.mjs";
|
|
14
|
+
import "./token-goat-chunk-3ZR4NLB3.mjs";
|
|
15
15
|
import "./token-goat-chunk-EEIDFMEM.mjs";
|
|
16
16
|
import "./token-goat-chunk-A37V4PBF.mjs";
|
|
17
17
|
export {
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
isBlobStale,
|
|
10
10
|
loadBlob,
|
|
11
11
|
storeBlob
|
|
12
|
-
} from "./token-goat-chunk-
|
|
12
|
+
} from "./token-goat-chunk-CBO45DYZ.mjs";
|
|
13
13
|
import {
|
|
14
14
|
SYMBOL_BODY_CHAR_CAP,
|
|
15
15
|
copilotCliMcpToolsDir,
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
resolveIndexPath,
|
|
25
25
|
shortFingerprint,
|
|
26
26
|
toDisplayPath
|
|
27
|
-
} from "./token-goat-chunk-
|
|
27
|
+
} from "./token-goat-chunk-3ZR4NLB3.mjs";
|
|
28
28
|
import {
|
|
29
29
|
registerReset
|
|
30
30
|
} from "./token-goat-chunk-EEIDFMEM.mjs";
|
|
@@ -7,11 +7,11 @@ import {
|
|
|
7
7
|
expandGlobs,
|
|
8
8
|
leftoverIntegrations,
|
|
9
9
|
run
|
|
10
|
-
} from "./token-goat-chunk-
|
|
11
|
-
import "./token-goat-chunk-
|
|
12
|
-
import "./token-goat-chunk-
|
|
13
|
-
import "./token-goat-chunk-
|
|
14
|
-
import "./token-goat-chunk-
|
|
10
|
+
} from "./token-goat-chunk-6MU6FHE3.mjs";
|
|
11
|
+
import "./token-goat-chunk-US57I7GN.mjs";
|
|
12
|
+
import "./token-goat-chunk-FSC44QOH.mjs";
|
|
13
|
+
import "./token-goat-chunk-CBO45DYZ.mjs";
|
|
14
|
+
import "./token-goat-chunk-3ZR4NLB3.mjs";
|
|
15
15
|
import "./token-goat-chunk-EEIDFMEM.mjs";
|
|
16
16
|
import "./token-goat-chunk-A37V4PBF.mjs";
|
|
17
17
|
export {
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
runSkeleton,
|
|
26
26
|
runSymbol,
|
|
27
27
|
withPinnedReads
|
|
28
|
-
} from "./token-goat-chunk-
|
|
28
|
+
} from "./token-goat-chunk-US57I7GN.mjs";
|
|
29
29
|
import {
|
|
30
30
|
buildProjectMap,
|
|
31
31
|
embeddingsDepsAvailable,
|
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
getProjectIndexCounts,
|
|
35
35
|
isWorkerRunning,
|
|
36
36
|
mapLookupBytesSaved
|
|
37
|
-
} from "./token-goat-chunk-
|
|
37
|
+
} from "./token-goat-chunk-CBO45DYZ.mjs";
|
|
38
38
|
import {
|
|
39
39
|
ENV_KEYS,
|
|
40
40
|
VERSION,
|
|
@@ -48,7 +48,7 @@ import {
|
|
|
48
48
|
recordStat,
|
|
49
49
|
resolveProjectRoot,
|
|
50
50
|
savedTokensFromBytes
|
|
51
|
-
} from "./token-goat-chunk-
|
|
51
|
+
} from "./token-goat-chunk-3ZR4NLB3.mjs";
|
|
52
52
|
import "./token-goat-chunk-EEIDFMEM.mjs";
|
|
53
53
|
import {
|
|
54
54
|
init_define_import_meta_env
|
|
@@ -68,7 +68,7 @@ import {
|
|
|
68
68
|
walkProject,
|
|
69
69
|
yamlLineClosesQuote,
|
|
70
70
|
yamlOpenQuoteAfter
|
|
71
|
-
} from "./token-goat-chunk-
|
|
71
|
+
} from "./token-goat-chunk-CBO45DYZ.mjs";
|
|
72
72
|
import {
|
|
73
73
|
Database,
|
|
74
74
|
PER_FILE_COUNTERFACTUAL_CEILING,
|
|
@@ -122,7 +122,7 @@ import {
|
|
|
122
122
|
unsupportedLanguageName,
|
|
123
123
|
windowsCmdQuoteArg,
|
|
124
124
|
withExtension
|
|
125
|
-
} from "./token-goat-chunk-
|
|
125
|
+
} from "./token-goat-chunk-3ZR4NLB3.mjs";
|
|
126
126
|
import {
|
|
127
127
|
registerReset
|
|
128
128
|
} from "./token-goat-chunk-EEIDFMEM.mjs";
|
package/dist/token-goat-hook.mjs
CHANGED
|
@@ -2,11 +2,11 @@ import { createRequire as __cjsRequire } from 'node:module';
|
|
|
2
2
|
const require = __cjsRequire(import.meta.url);
|
|
3
3
|
import {
|
|
4
4
|
relayInProcess
|
|
5
|
-
} from "./token-goat-chunk-
|
|
6
|
-
import "./token-goat-chunk-
|
|
7
|
-
import "./token-goat-chunk-
|
|
8
|
-
import "./token-goat-chunk-
|
|
9
|
-
import "./token-goat-chunk-
|
|
5
|
+
} from "./token-goat-chunk-63XEQMNU.mjs";
|
|
6
|
+
import "./token-goat-chunk-FSC44QOH.mjs";
|
|
7
|
+
import "./token-goat-chunk-CBO45DYZ.mjs";
|
|
8
|
+
import "./token-goat-chunk-EPX5DWGM.mjs";
|
|
9
|
+
import "./token-goat-chunk-3ZR4NLB3.mjs";
|
|
10
10
|
import "./token-goat-chunk-EEIDFMEM.mjs";
|
|
11
11
|
import {
|
|
12
12
|
init_define_import_meta_env
|
package/dist/token-goat.core.mjs
CHANGED
|
@@ -2,13 +2,13 @@ import { createRequire as __cjsRequire } from 'node:module';
|
|
|
2
2
|
const require = __cjsRequire(import.meta.url);
|
|
3
3
|
import {
|
|
4
4
|
run
|
|
5
|
-
} from "./token-goat-chunk-
|
|
6
|
-
import "./token-goat-chunk-
|
|
7
|
-
import "./token-goat-chunk-
|
|
8
|
-
import "./token-goat-chunk-
|
|
5
|
+
} from "./token-goat-chunk-6MU6FHE3.mjs";
|
|
6
|
+
import "./token-goat-chunk-US57I7GN.mjs";
|
|
7
|
+
import "./token-goat-chunk-FSC44QOH.mjs";
|
|
8
|
+
import "./token-goat-chunk-CBO45DYZ.mjs";
|
|
9
9
|
import {
|
|
10
10
|
installEpipeGuard
|
|
11
|
-
} from "./token-goat-chunk-
|
|
11
|
+
} from "./token-goat-chunk-3ZR4NLB3.mjs";
|
|
12
12
|
import "./token-goat-chunk-EEIDFMEM.mjs";
|
|
13
13
|
import {
|
|
14
14
|
init_define_import_meta_env
|
package/docs/cli.md
CHANGED
|
@@ -167,10 +167,10 @@ token-goat pdf-extract manual.pdf --pages 12-15 --layout --head 120
|
|
|
167
167
|
| `token-goat project exclude <path>` | Add a project root to the blocklist so the worker never indexes it. Writes the resolved absolute path to `[worker] blocked_roots` in `config.toml`; idempotent. It also removes anything already indexed under that path and says how many files went, so excluding a directory means its contents stop being readable through `symbol` rather than merely stopping future indexing. Remove the entry from the config to re-enable indexing, then run `token-goat index` to bring the contents back. |
|
|
168
168
|
| `token-goat project prune [--dry-run]` | Remove blocked/excluded roots that no longer exist on disk. `--dry-run` previews removals without touching the config file. Useful after deleting or moving projects. |
|
|
169
169
|
| `token-goat install` | Wire up hooks (and, with the harness flags below, other AI tool integrations). No `--dry-run` or `--verify` flag — run `token-goat doctor` after install to audit the result. |
|
|
170
|
-
| `token-goat doctor` | Confirm everything is wired correctly. Surfaces install state, cold-import timing, cache hit rates, compaction-budget telemetry, opt-in flag status, and canonical-root sanity. A **Tool names** check reports any tool name a harness sent that reached no handler wanting it, and calls out the ones that differ from a handled name only by capitalisation or punctuation — the signature of a bridge that forgot to rename something, which is otherwise invisible. A **Security** section reports the posture in one place: whether offline mode is on, whether injection scanning is on, whether the Google Drive integration is enabled, whether fetching runs against an allow list or a deny list, whether MCP reads are confined to the project root, and whether the data directory is readable by other local users. It only warns when a protection that ships on has been switched off, so a default install stays quiet. It read-only audits `~/.copilot/mcp-config.json` for globally configured Chrome DevTools or Playwright `npx` launchers, recommending project scope or removal when inactive; it never prints server configuration or secrets. On Windows, it also reports duplicate Chrome DevTools/Playwright MCP launchers and orphaned Node processes without terminating anything. Pass `--context` to show the **Context footprint** section: a fill bar with severity (ok / warn / high / URGENT), per-component breakdown (skills catalog, loaded skill bodies, CLAUDE.md+MEMORY.md, conversation estimate), session-to-session growth trend with sessions-to-URGENT projection, and tiered compaction recommendations (Tier 0–4) naming the exact commands to run. Auto-shown when fill > 40 % or any loaded skill > 2 K tokens lacks a compact. `--json` emits the check results (one entry per check, with `ok`/`warn`/`fail` status) as JSON instead of text. |
|
|
170
|
+
| `token-goat doctor` | Confirm everything is wired correctly. Surfaces install state, cold-import timing, cache hit rates, compaction-budget telemetry, opt-in flag status, and canonical-root sanity. A **Parser freshness** check reports how much of the index for this project was written by a different build of the extractor. The stamp only refreshes when something touches a file, so after an upgrade a project goes on answering `symbol`, `read`, `outline` and `skeleton` from the previous extractor with nothing saying so. It warns past a quarter and names the fix: a plain `token-goat index` in that project, which is enough on its own, since a parser mismatch reparses without `--force`. A **Tool names** check reports any tool name a harness sent that reached no handler wanting it, and calls out the ones that differ from a handled name only by capitalisation or punctuation — the signature of a bridge that forgot to rename something, which is otherwise invisible. A **Security** section reports the posture in one place: whether offline mode is on, whether injection scanning is on, whether the Google Drive integration is enabled, whether fetching runs against an allow list or a deny list, whether MCP reads are confined to the project root, and whether the data directory is readable by other local users. It only warns when a protection that ships on has been switched off, so a default install stays quiet. It read-only audits `~/.copilot/mcp-config.json` for globally configured Chrome DevTools or Playwright `npx` launchers, recommending project scope or removal when inactive; it never prints server configuration or secrets. On Windows, it also reports duplicate Chrome DevTools/Playwright MCP launchers and orphaned Node processes without terminating anything. Pass `--context` to show the **Context footprint** section: a fill bar with severity (ok / warn / high / URGENT), per-component breakdown (skills catalog, loaded skill bodies, CLAUDE.md+MEMORY.md, conversation estimate), session-to-session growth trend with sessions-to-URGENT projection, and tiered compaction recommendations (Tier 0–4) naming the exact commands to run. Auto-shown when fill > 40 % or any loaded skill > 2 K tokens lacks a compact. `--json` emits the check results (one entry per check, with `ok`/`warn`/`fail` status) as JSON instead of text. |
|
|
171
171
|
| `token-goat capabilities` | List every capability that can send data off this machine or leave data on it, with whether it is currently on, the config key that decides that, and the exact `file::symbol` where the decision is made — so a reviewer can open the code rather than take the list's word for it. `--json` emits the same thing for a pipeline to assert on, which is the point: the answer comes from the binary installed on your machine, not from documentation that may describe a different build. A test in the suite fails the build when a module that can open a network connection is missing from this list, and equally when the list names one that no longer connects anywhere. |
|
|
172
172
|
| `token-goat baseline` | Emit a project map: file count, per-language file counts, the top indexed symbols (by name/kind/location), and the most recently modified files. `--subagent` emits a terser variant (fewer symbols, fewer recent files) for context handed to a freshly spawned subagent; `--json` for the machine-readable form. |
|
|
173
|
-
| `token-goat compact-doc <path>` | Build an extractive compact sidecar for a large reference doc (`.md`/`.markdown`). The compact is stored in the token-goat data dir as a SHA-keyed sidecar; `pre_read` serves it in place of the full file when it exists and is fresh, saving
|
|
173
|
+
| `token-goat compact-doc <path>` | Build an extractive compact sidecar for a large reference doc (`.md`/`.markdown`). The compact is stored in the token-goat data dir as a SHA-keyed sidecar; `pre_read` serves it in place of the full file when it exists and is fresh, typically saving 60–95% of the tokens the full read would cost (measured across this repo's own 44 docs: median 67%, range 5–99%, depending on how much of the doc is prose under headings). Use `--force` to rebuild, `--sentences N` to control lines per section (default 2), `--show` to print the result. The sidecar is automatically marked stale when you edit the source file. Config: `[hints] stable_doc_compacts = true` (default on). |
|
|
174
174
|
|
|
175
175
|
Missed lookups recover surgically: `read` and `section` print a "Did you mean…?" list on a miss, and `section` auto-redirects on an unambiguous heading-prefix match — a typo costs at most one extra glance, not a re-read.
|
|
176
176
|
|
|
@@ -302,12 +302,12 @@ that category — the real cost of emitting it, not just how often it fired):
|
|
|
302
302
|
|
|
303
303
|
```
|
|
304
304
|
$ token-goat hint-stats
|
|
305
|
-
category emitted acted-on efficacy
|
|
306
|
-
bash_redirect 42 9 21.4%
|
|
307
|
-
bash_recall 18 15 83.3%
|
|
308
|
-
read_reread_dedup 11 2 18.2%
|
|
309
|
-
read_structural_nav 7 1 14.3%
|
|
310
|
-
edit_reread_suggest 3 0 0%
|
|
305
|
+
category emitted acted-on efficacy suppressed manual+ manual- spent
|
|
306
|
+
bash_redirect 42 9 21.4% no 0 0 3150
|
|
307
|
+
bash_recall 18 15 83.3% no 0 0 1080
|
|
308
|
+
read_reread_dedup 11 2 18.2% * no 0 0 660
|
|
309
|
+
read_structural_nav 7 1 14.3% yes 0 1 420
|
|
310
|
+
edit_reread_suggest 3 0 0% * no 0 0 180
|
|
311
311
|
|
|
312
312
|
TOTAL saved=48200 spent=5490 net=42710
|
|
313
313
|
```
|
|
@@ -325,6 +325,13 @@ emissions (default 5) AND its efficacy falls below `hint_stats.suppress_threshol
|
|
|
325
325
|
emission. Once suppressed, that hook stops emitting that category until `token-goat hint-stats
|
|
326
326
|
--reset` clears the tracked data. Configure both knobs with `token-goat config set hint_stats.min_sample_size <n>` / `token-goat config set hint_stats.suppress_threshold_pct <pct>`.
|
|
327
327
|
|
|
328
|
+
A `*` on an efficacy figure means that category is scored on an absence. Those hints ask the
|
|
329
|
+
agent *not* to do something, so a window that expires with no re-read counts as compliance, while
|
|
330
|
+
an unmarked category only scores when the agent actually runs the command the hint named. The two
|
|
331
|
+
percentages sit on different scales and comparing them straight across gives the wrong answer: a
|
|
332
|
+
high starred figure means the warned-against read was not seen, not that the hint persuaded
|
|
333
|
+
anyone.
|
|
334
|
+
|
|
328
335
|
"Acted on" is a real, session-scoped signal (the exact file path or cached-output id the hint's
|
|
329
336
|
own text pointed at is checked against the next few tool calls in that session) — not a guess —
|
|
330
337
|
but it is a proxy for correlation, not proof of causation: a match means the agent ran the
|