token-goat 2.0.3 → 2.1.0
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 +3 -2
- package/dist/token-goat.mjs +189 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ permalink: /
|
|
|
9
9
|
|
|
10
10
|

|
|
11
11
|
|
|
12
|
-
**85%** smaller reads · **97.4%** image compression · **
|
|
12
|
+
**85%** smaller reads · **97.4%** image compression · **160+** filter & interception rules · **94–99%** skill overhead cut · compaction memory · **prompt injection** guard · **3.7 GB** never reached the model · **1.1 Gt** tokens saved
|
|
13
13
|
|
|
14
14
|
**Reduces AI token use/costs by 40–90%, and improves its focus. Fully automated, always online.**
|
|
15
15
|
|
|
@@ -72,6 +72,7 @@ The fastest way to reduce AI token costs is fixing these five, not writing short
|
|
|
72
72
|
| Same docs URL fetched twice in a session | Re-fetch blocked at warm+ context pressure; cached body available via `token-goat web-output <id>` |
|
|
73
73
|
| `cat src/auth.py` or `Get-Content module.py` run via Bash | Pre-Bash hook detects whole-file reads of indexed source files and suggests `token-goat read "file::Symbol"`, `skeleton`, or `section` — covers `cat`, `bat`, `type`, PowerShell `Get-Content`/`gc` |
|
|
74
74
|
| `rg pattern src/` or `grep -rn` run via Bash (first time) | Pre-Bash hook suggests `token-goat symbol <name>` and `token-goat semantic "<query>"` as indexed alternatives to a full directory walk |
|
|
75
|
+
| `rg "^def" src/file.py` or `grep "class " module.ts` — structural search on a single source file | Pre-Bash hook redirects to `token-goat skeleton "file"` or `outline "file"` — all symbols with line numbers, no full-file read |
|
|
75
76
|
| `rg` or `grep` run twice with the same pattern | Pre-Bash dedup hint fires on repeated `rg`/`grep`/`ag` calls the same way it fires on the native Grep tool; repeat searches return a cached match-count hint instead of re-running |
|
|
76
77
|
| Read tool targets `tool-results/<id>.txt` or `tasks/<id>.output` | Pre-Read hook suggests `token-goat bash-output <id> --tail N` / `--grep PATTERN` / `--section H`; the filename stem is the output ID |
|
|
77
78
|
| Repeated monitoring command run again (`gh run watch`, `next dev`, `vitest`, `docker logs`) | Pre-bash recall hint: when a prior run is cached and its output exceeds 2 KB, a pointer to `token-goat bash-output <id> --grep PATTERN` is injected instead of re-running the command |
|
|
@@ -239,7 +240,7 @@ FAILED tests/test_x.py::test_one
|
|
|
239
240
|
[token-goat: pytest filter compressed 4.8 KiB to 0.1 KiB (97% saved)]
|
|
240
241
|
```
|
|
241
242
|
|
|
242
|
-
|
|
243
|
+
Built-in output compression covers 130+ dev tool CLIs: `pytest`, `jest` / `vitest`, `cargo`, `npm` / `pnpm` / `yarn` / `bun`, `docker`, `kubectl` / `helm`, `aws`, `ruff` / `eslint` / `mypy` / `pylint` / `oxlint`, `git`, `make` / `gradle` / `mvn` / `ant` / `bazel`, `go test` / `golangci-lint`, `terraform` / `pulumi` / `cdk`, `pip` / `uv` / `conda`, `python`, `gh`, `ansible`, `pre-commit`, `grep`, `eza` / `ls`, `fd`, `bat`, `jq`, `yq`, `curl` / `wget`, `rsync`, `dotnet`, `cmake` / `ctest`, `swift` / `xcodebuild`, `ruby` / `bundler`, `elixir` / `mix`, `php` / `composer`, `flutter` / `dart`, `rust` / `cargo`, `kotlin` / `ktlint`, `zig`, `crystal`, `haskell` / `cabal`, `nix`, `R`, `c++` (conan / vcpkg / cppcheck / clang-tidy), `wrangler` / `hardhat` / `serverless`, `erlang`, `fly.io`, `forge`, `elm`, `julia`, `tox`, `vault`, `packer`, `nx` / `lerna` / `turbo`, `prettier` / `biome`, `sass`, `wasm-pack`, `deno`, **and AI tool CLIs**: `aider`, `gemini`, `claude`, `gh copilot`, `copilot`, `cursor`, `windsurf` (incl. Cascade), `opencode`, `continue`, `cline`. Each filter strips ANSI escapes, collapses `\r` progress bars, dedupes repeated lines, groups linter issues by rule, keeps every error block verbatim, and caps total output at 1000 lines / 64 KiB. Compound commands (`cmd1 && cmd2`) are wrapped per segment, so `git diff && git log` compresses both halves. Disable globally with `TOKEN_GOAT_BASH_COMPRESS=0`, per-filter via `[bash_compress] disabled_filters = ["docker"]` in config.toml, or preview the output of any command with `token-goat compress --cmd '<your command>'`. To exclude project-specific directories from indexing (temporary venvs, build sandboxes), add `[indexing] skip_dirs = ["my-tmpdir"]` to config.toml.
|
|
243
244
|
|
|
244
245
|
`gh api` responses get an extra pass: boilerplate `*_url` fields (`followers_url`, `gists_url`, `starred_url`, and around a dozen others) are stripped from JSON objects; `html_url`, `avatar_url`, `clone_url`, and `ssh_url` are kept. User and repo objects typically shrink 60–80%. When token-goat sees a GitHub permission error in the output or a non-zero exit on a security endpoint, it injects a system message suggesting `gh auth refresh -s security_events`.
|
|
245
246
|
|
package/dist/token-goat.mjs
CHANGED
|
@@ -9493,7 +9493,7 @@ init_define_import_meta_env();
|
|
|
9493
9493
|
import { createRequire } from "node:module";
|
|
9494
9494
|
function resolveVersion() {
|
|
9495
9495
|
if (true) {
|
|
9496
|
-
return "2.0
|
|
9496
|
+
return "2.1.0";
|
|
9497
9497
|
}
|
|
9498
9498
|
const require2 = createRequire(import.meta.url);
|
|
9499
9499
|
const pkg = require2("../package.json");
|
|
@@ -10567,6 +10567,10 @@ var BUILD_COMMAND_PATTERNS = [
|
|
|
10567
10567
|
// Turbo
|
|
10568
10568
|
/^\s*turbo\s+(build|dev)\b/i
|
|
10569
10569
|
];
|
|
10570
|
+
var LOCK_FILE_COUNT = LOCK_FILE_NAMES.size;
|
|
10571
|
+
var MANIFEST_FILE_COUNT = MANIFEST_FILE_NAMES.size + MANIFEST_EXTENSIONS.size + MANIFEST_BASENAME_PATTERNS.length;
|
|
10572
|
+
var BUILD_DIR_COUNT = BUILD_DIR_NAMES.size;
|
|
10573
|
+
var GENERATED_EXT_COUNT = ALWAYS_GENERATED_EXTS.size + CONDITIONALLY_GENERATED_EXTS.size;
|
|
10570
10574
|
function isLockFile(basename7) {
|
|
10571
10575
|
return LOCK_FILE_NAMES.has(basename7.toLowerCase());
|
|
10572
10576
|
}
|
|
@@ -11845,6 +11849,8 @@ function isTsConfigFile(basename7) {
|
|
|
11845
11849
|
return /^tsconfig(\..+)?\.json$/i.test(lower) || lower === "jsconfig.json";
|
|
11846
11850
|
}
|
|
11847
11851
|
var LARGE_FILE_BYTES = 100 * 1024;
|
|
11852
|
+
var REREAD_DENY_BYTES = 50 * 1024;
|
|
11853
|
+
var LARGE_FILE_DENY_BYTES = 500 * 1024;
|
|
11848
11854
|
function isNodeModulesPath(path15) {
|
|
11849
11855
|
const isWindows = process.platform === "win32";
|
|
11850
11856
|
const check = isWindows ? path15.toLowerCase() : path15;
|
|
@@ -11925,6 +11931,13 @@ function preReadHandler(event) {
|
|
|
11925
11931
|
}
|
|
11926
11932
|
}
|
|
11927
11933
|
}
|
|
11934
|
+
if (/^\.env(\.\w+)?$/.test(basename7) && wasFileReadThisSession(normalized)) {
|
|
11935
|
+
recordFileRead(normalized);
|
|
11936
|
+
recordStat("session_hint", 0, 0);
|
|
11937
|
+
return denyOutput(
|
|
11938
|
+
normalized + " was already read this session. Environment files rarely change mid-session. Use `token-goat config-get " + normalized + " KEY_NAME` to extract a specific variable."
|
|
11939
|
+
);
|
|
11940
|
+
}
|
|
11928
11941
|
if (wasFileReadThisSession(normalized)) {
|
|
11929
11942
|
const entry = getSessionFiles().get(normalized);
|
|
11930
11943
|
const reads = entry?.readCount ?? 1;
|
|
@@ -11933,6 +11946,11 @@ function preReadHandler(event) {
|
|
|
11933
11946
|
const hint = _isDocFile(normalized) ? 'Use `token-goat section "' + normalized + '::SectionName"` to read one section.' : "Use token-goat read/section/symbol to re-read surgically.";
|
|
11934
11947
|
const rereadBytes = statSize(normalized) ?? 0;
|
|
11935
11948
|
recordStat("session_hint", rereadBytes, Math.round(rereadBytes / 4));
|
|
11949
|
+
if (rereadBytes >= REREAD_DENY_BYTES || reads >= 2) {
|
|
11950
|
+
return denyOutput(
|
|
11951
|
+
normalized + " was already read this session (" + reads + " " + plural + "). " + hint
|
|
11952
|
+
);
|
|
11953
|
+
}
|
|
11936
11954
|
return contextOutput(
|
|
11937
11955
|
"Note: " + normalized + " was already read this session (" + reads + " " + plural + "). " + hint
|
|
11938
11956
|
);
|
|
@@ -11943,6 +11961,11 @@ function preReadHandler(event) {
|
|
|
11943
11961
|
recordFileRead(normalized);
|
|
11944
11962
|
const hint = _isDocFile(normalized) ? 'Use `token-goat section "' + normalized + '::SectionName"` to read one section.' : "Consider token-goat skeleton or token-goat section.";
|
|
11945
11963
|
recordStat("session_hint", size, Math.round(size / 4));
|
|
11964
|
+
if (size >= LARGE_FILE_DENY_BYTES) {
|
|
11965
|
+
return denyOutput(
|
|
11966
|
+
normalized + " is very large (" + kb + "KB). " + hint + " Use Read with offset/limit to sample specific sections."
|
|
11967
|
+
);
|
|
11968
|
+
}
|
|
11946
11969
|
return contextOutput(
|
|
11947
11970
|
"Note: " + normalized + " is large (" + kb + "kb). " + hint
|
|
11948
11971
|
);
|
|
@@ -12489,10 +12512,125 @@ function extractCommand(event) {
|
|
|
12489
12512
|
const cmd = event.toolInput["command"];
|
|
12490
12513
|
return typeof cmd === "string" && cmd.trim() !== "" ? cmd.trim() : void 0;
|
|
12491
12514
|
}
|
|
12515
|
+
function isTempPath(fp) {
|
|
12516
|
+
const norm = fp.replace(/\\/g, "/");
|
|
12517
|
+
return /^\/tmp\//i.test(norm) || /\/var\/folders\//i.test(norm) || /AppData\/Local\/Temp\//i.test(norm) || norm.startsWith("/c/Users/") && norm.includes("/AppData/Local/Temp/");
|
|
12518
|
+
}
|
|
12519
|
+
function isOrchestratorStateFile(filePath) {
|
|
12520
|
+
const basename7 = (filePath.includes("/") ? filePath.split("/").at(-1) : filePath.split("\\").at(-1)) ?? filePath;
|
|
12521
|
+
return /^\.improve-state-/.test(basename7);
|
|
12522
|
+
}
|
|
12492
12523
|
function extractCatSourceFile(cmd) {
|
|
12493
12524
|
const m = /^cat\s+(\S+\.(?:java|py|ts|tsx|js|jsx|go|rb|rs|cpp|cc|cxx|c|h|hpp|kt|swift|cs|php|scala|clj))\s*$/.exec(cmd);
|
|
12494
12525
|
return m?.[1] ?? null;
|
|
12495
12526
|
}
|
|
12527
|
+
function extractCatFile(cmd) {
|
|
12528
|
+
const m = /^cat(?:\s+(?:-[a-zA-Z]+|--[a-zA-Z-]+))*\s+(?:"([^"]+)"|'([^']+)'|(\S+))\s*$/.exec(cmd);
|
|
12529
|
+
if (!m) return null;
|
|
12530
|
+
const filePath = m[1] ?? m[2] ?? m[3];
|
|
12531
|
+
if (filePath === void 0) return null;
|
|
12532
|
+
if (isTempPath(filePath)) return null;
|
|
12533
|
+
const basename7 = filePath.includes("/") ? filePath.split("/").at(-1) : filePath.split("\\").at(-1) ?? filePath;
|
|
12534
|
+
const isEnvFile = /^\.env(\.\w+)?$/i.test(basename7);
|
|
12535
|
+
const hasKnownExt = /\.(?:java|py|ts|tsx|js|jsx|go|rb|rs|cpp|cc|cxx|c|h|hpp|kt|swift|cs|php|scala|clj|md|mdx|rst|txt|json|yaml|yml|toml|xml|conf|cfg|ini|properties|sql|env)$/i.test(filePath);
|
|
12536
|
+
if (!hasKnownExt && !isEnvFile) return null;
|
|
12537
|
+
const isDoc = /\.(?:md|mdx|rst|txt|sql)$/i.test(filePath);
|
|
12538
|
+
const isEnv = isEnvFile || /\.env$/i.test(filePath);
|
|
12539
|
+
const isConfig = /\.(?:json|yaml|yml|toml|conf|cfg|ini|properties)$/i.test(filePath);
|
|
12540
|
+
return { filePath, isDoc, isEnv, isConfig };
|
|
12541
|
+
}
|
|
12542
|
+
function extractWslCatFile(cmd) {
|
|
12543
|
+
const wslMatch = /^wsl(?:\s+-d\s+\S+)?\s+bash\s+-c\s+"cat(?:\s+(?:-[a-zA-Z]+|--[a-zA-Z-]+))*\s+\/mnt\/([a-z])\/([^"]*)"/.exec(cmd);
|
|
12544
|
+
if (!wslMatch) return null;
|
|
12545
|
+
const drive = wslMatch[1]?.toUpperCase();
|
|
12546
|
+
const pathRest = wslMatch[2];
|
|
12547
|
+
if (!drive || !pathRest) return null;
|
|
12548
|
+
const filePath = drive + ":/" + pathRest;
|
|
12549
|
+
if (isTempPath(filePath)) return null;
|
|
12550
|
+
const basename7 = filePath.includes("/") ? filePath.split("/").at(-1) : filePath.split("\\").at(-1) ?? filePath;
|
|
12551
|
+
const isEnvFile = /^\.env(\.\w+)?$/i.test(basename7);
|
|
12552
|
+
const hasKnownExt = /\.(?:java|py|ts|tsx|js|jsx|go|rb|rs|cpp|cc|cxx|c|h|hpp|kt|swift|cs|php|scala|clj|md|mdx|rst|txt|json|yaml|yml|toml|xml|conf|cfg|ini|properties|sql|env)$/i.test(filePath);
|
|
12553
|
+
if (!hasKnownExt && !isEnvFile) return null;
|
|
12554
|
+
const isDoc = /\.(?:md|mdx|rst|txt|sql)$/i.test(filePath);
|
|
12555
|
+
const isEnv = isEnvFile || /\.env$/i.test(filePath);
|
|
12556
|
+
const isConfig = /\.(?:json|yaml|yml|toml|conf|cfg|ini|properties)$/i.test(filePath);
|
|
12557
|
+
return { filePath, isDoc, isEnv, isConfig };
|
|
12558
|
+
}
|
|
12559
|
+
function extractPythonFileRead(cmd) {
|
|
12560
|
+
if (!/python3?/.test(cmd)) return null;
|
|
12561
|
+
const EXT = /\.(?:java|py|ts|tsx|js|jsx|go|rb|rs|cpp|cc|cxx|c|h|hpp|kt|swift|cs|php|scala|clj|md|mdx|rst|txt|json|yaml|yml|toml|xml|conf|cfg|ini|properties)/i;
|
|
12562
|
+
const direct = /open\(['"]([^'"]+\.(?:java|py|ts|tsx|js|jsx|go|rb|rs|cpp|cc|cxx|c|h|hpp|kt|swift|cs|php|scala|clj|md|mdx|rst|txt|json|yaml|yml|toml|xml|conf|cfg|ini|properties))['"]/i.exec(cmd);
|
|
12563
|
+
if (direct) {
|
|
12564
|
+
const filePath = direct[1] ?? "";
|
|
12565
|
+
if (!filePath) return null;
|
|
12566
|
+
if (isOrchestratorStateFile(filePath)) return null;
|
|
12567
|
+
const isDoc = /\.(?:md|mdx|rst|txt)$/i.test(filePath);
|
|
12568
|
+
return { filePath, isDoc };
|
|
12569
|
+
}
|
|
12570
|
+
if (/open\s*\(/.test(cmd)) {
|
|
12571
|
+
const literal = /['"]([^'"]+\.(?:java|py|ts|tsx|js|jsx|go|rb|rs|cpp|cc|cxx|c|h|hpp|kt|swift|cs|php|scala|clj|md|mdx|rst|txt|json|yaml|yml|toml|xml|conf|cfg|ini|properties))['"]/i.exec(cmd);
|
|
12572
|
+
if (literal) {
|
|
12573
|
+
const filePath = literal[1] ?? "";
|
|
12574
|
+
if (filePath) {
|
|
12575
|
+
if (isOrchestratorStateFile(filePath)) return null;
|
|
12576
|
+
if (EXT.test(filePath)) {
|
|
12577
|
+
const isDoc = /\.(?:md|mdx|rst|txt)$/i.test(filePath);
|
|
12578
|
+
return { filePath, isDoc };
|
|
12579
|
+
}
|
|
12580
|
+
}
|
|
12581
|
+
}
|
|
12582
|
+
}
|
|
12583
|
+
return null;
|
|
12584
|
+
}
|
|
12585
|
+
function extractHeadFile(cmd) {
|
|
12586
|
+
const m = /^head(?:\s+-n\s+(\d+)|\s+-(\d+))?\s+(?:"([^"]+)"|'([^']+)'|(\S+))\s*$/.exec(cmd);
|
|
12587
|
+
if (!m) return null;
|
|
12588
|
+
const n = parseInt(m[1] ?? m[2] ?? "0", 10);
|
|
12589
|
+
if (n > 0 && n < 10) return null;
|
|
12590
|
+
const filePath = m[3] ?? m[4] ?? m[5];
|
|
12591
|
+
if (filePath === void 0) return null;
|
|
12592
|
+
if (isTempPath(filePath)) return null;
|
|
12593
|
+
if (!/\.(?:ts|tsx|js|jsx|py|go|java|rs|rb|cs|md|mdx|rst|txt|json|yaml|yml|toml|sql|sh)$/i.test(filePath)) return null;
|
|
12594
|
+
const isDoc = /\.(?:md|mdx|rst|txt|sql)$/i.test(filePath);
|
|
12595
|
+
const isConfig = /\.(?:json|yaml|yml|toml|conf|cfg|ini|properties)$/i.test(filePath);
|
|
12596
|
+
return { filePath, isDoc, isConfig };
|
|
12597
|
+
}
|
|
12598
|
+
function extractNodeFileRead(cmd) {
|
|
12599
|
+
if (!/^node\s+-e/.test(cmd)) return null;
|
|
12600
|
+
const m = /readFileSync\(['"]([^'"]+\.(?:ts|tsx|js|jsx|py|go|java|rs|rb|cs|md|mdx|rst|txt|json|yaml|yml|toml|xml|conf|cfg|ini|properties|sql))['"]/i.exec(cmd);
|
|
12601
|
+
if (!m || !m[1]) return null;
|
|
12602
|
+
const filePath = m[1];
|
|
12603
|
+
if (isOrchestratorStateFile(filePath)) return null;
|
|
12604
|
+
if (isTempPath(filePath)) return null;
|
|
12605
|
+
const isDoc = /\.(?:md|mdx|rst|txt|sql)$/i.test(filePath);
|
|
12606
|
+
return { filePath, isDoc };
|
|
12607
|
+
}
|
|
12608
|
+
function extractTailFile(cmd) {
|
|
12609
|
+
if (/-f\b/.test(cmd)) return null;
|
|
12610
|
+
if (/-c\b/.test(cmd)) return null;
|
|
12611
|
+
if (/-n\s*\+/.test(cmd)) return null;
|
|
12612
|
+
const m = /^tail(?:\s+-n\s+(\d+)|\s+-(\d+))?\s+(?:"([^"]+)"|'([^']+)'|(\S+))\s*$/.exec(cmd);
|
|
12613
|
+
if (!m) return null;
|
|
12614
|
+
const n = parseInt(m[1] ?? m[2] ?? "0", 10);
|
|
12615
|
+
if (n <= 10) return null;
|
|
12616
|
+
const filePath = m[3] ?? m[4] ?? m[5];
|
|
12617
|
+
if (!filePath) return null;
|
|
12618
|
+
if (isTempPath(filePath)) return null;
|
|
12619
|
+
if (!/\.(?:ts|tsx|js|jsx|py|go|java|rs|rb|cs|md|mdx|rst|txt|json|yaml|yml|toml|sql|sh)$/i.test(filePath)) return null;
|
|
12620
|
+
const isDoc = /\.(?:md|mdx|rst|txt|sql)$/i.test(filePath);
|
|
12621
|
+
return { filePath, isDoc };
|
|
12622
|
+
}
|
|
12623
|
+
function extractRgStructuralSearch(cmd) {
|
|
12624
|
+
if (!/^(?:rg|grep)\s+/.test(cmd)) return null;
|
|
12625
|
+
const hasStructural = /["']?\^?(?:def\s|class\s|function\s|func\s|fn\s|pub fn\s|import\s|from\s)/.test(cmd) || /["']\^(?:def|class|function|func|import|from)["']/.test(cmd) || /\\bdef\\b|\\bclass\\b/.test(cmd);
|
|
12626
|
+
if (!hasStructural) return null;
|
|
12627
|
+
const fileMatch = /(?:^|\s)(?:"([^"]+\.(?:py|ts|tsx|js|jsx|go|rs|rb|cs|java|cpp|cc|cxx|c|h|sh|bash))"|('([^']+\.(?:py|ts|tsx|js|jsx|go|rs|rb|cs|java|cpp|cc|cxx|c|h|sh|bash))')|([^\s"']+\.(?:py|ts|tsx|js|jsx|go|rs|rb|cs|java|cpp|cc|cxx|c|h|sh|bash)))\s*$/.exec(cmd);
|
|
12628
|
+
if (!fileMatch) return null;
|
|
12629
|
+
const filePath = fileMatch[1] ?? fileMatch[3] ?? fileMatch[4];
|
|
12630
|
+
if (!filePath) return null;
|
|
12631
|
+
if (isTempPath(filePath)) return null;
|
|
12632
|
+
return { filePath };
|
|
12633
|
+
}
|
|
12496
12634
|
function isTscCommand(cmd) {
|
|
12497
12635
|
return /^\s*tsc(\s|$)/i.test(cmd);
|
|
12498
12636
|
}
|
|
@@ -12512,6 +12650,56 @@ function buildRecallHint(cmd, outputId) {
|
|
|
12512
12650
|
function preBashHandler(event) {
|
|
12513
12651
|
const cmd = extractCommand(event);
|
|
12514
12652
|
if (cmd === void 0) return passOutput();
|
|
12653
|
+
const catResult = extractCatFile(cmd);
|
|
12654
|
+
if (catResult !== null) {
|
|
12655
|
+
const { filePath, isDoc, isEnv, isConfig } = catResult;
|
|
12656
|
+
const hint = isEnv ? 'Use `token-goat config-get "' + filePath + '" KEY_NAME` to read a specific variable.' : isConfig ? 'Use `token-goat config-get "' + filePath + '" KEY_NAME` or `token-goat section "' + filePath + '::sectionName"` to read a specific value.' : isDoc ? 'Use `token-goat section "' + filePath + '::SectionHeading"` to read one section.' : 'Use `token-goat read "' + filePath + '::SymbolName"` to read one function or class.';
|
|
12657
|
+
recordStat("session_hint", 0, 0);
|
|
12658
|
+
return denyOutput("`cat` loads the entire file into context. " + hint);
|
|
12659
|
+
}
|
|
12660
|
+
const wslCatResult = extractWslCatFile(cmd);
|
|
12661
|
+
if (wslCatResult !== null) {
|
|
12662
|
+
const { filePath, isDoc, isEnv, isConfig } = wslCatResult;
|
|
12663
|
+
const hint = isEnv ? 'Use `token-goat config-get "' + filePath + '" KEY_NAME` to read a specific variable.' : isConfig ? 'Use `token-goat config-get "' + filePath + '" KEY_NAME` or `token-goat section "' + filePath + '::sectionName"` to read a specific value.' : isDoc ? 'Use `token-goat section "' + filePath + '::SectionHeading"` to read one section.' : 'Use `token-goat read "' + filePath + '::SymbolName"` to read one function or class.';
|
|
12664
|
+
recordStat("session_hint", 0, 0);
|
|
12665
|
+
return denyOutput("`cat` loads the entire file into context. " + hint);
|
|
12666
|
+
}
|
|
12667
|
+
const pyRead = extractPythonFileRead(cmd);
|
|
12668
|
+
if (pyRead !== null) {
|
|
12669
|
+
const { filePath, isDoc } = pyRead;
|
|
12670
|
+
const hint = isDoc ? 'Use `token-goat section "' + filePath + '::SectionHeading"` to read one section.' : 'Use `token-goat read "' + filePath + '::SymbolName"` to extract a specific symbol.';
|
|
12671
|
+
recordStat("session_hint", 0, 0);
|
|
12672
|
+
return denyOutput("Python `open()` file reads bypass read hooks. " + hint);
|
|
12673
|
+
}
|
|
12674
|
+
const tailResult = extractTailFile(cmd);
|
|
12675
|
+
if (tailResult !== null) {
|
|
12676
|
+
const { filePath, isDoc } = tailResult;
|
|
12677
|
+
const hint = isDoc ? 'Use `token-goat section "' + filePath + '::SectionHeading"` to read one section.' : 'Use `token-goat read "' + filePath + '::SymbolName"` or `token-goat skeleton "' + filePath + '"` to see the file structure.';
|
|
12678
|
+
recordStat("session_hint", 0, 0);
|
|
12679
|
+
return contextOutput("`tail` bypasses read hooks. " + hint);
|
|
12680
|
+
}
|
|
12681
|
+
const headResult = extractHeadFile(cmd);
|
|
12682
|
+
if (headResult !== null) {
|
|
12683
|
+
const { filePath, isDoc, isConfig } = headResult;
|
|
12684
|
+
const hint = isConfig ? 'Use `token-goat config-get "' + filePath + '" KEY_NAME` or `token-goat section "' + filePath + '::sectionName"` to read a specific value.' : isDoc ? 'Use `token-goat section "' + filePath + '::SectionHeading"` to read one section.' : 'Use `token-goat read "' + filePath + '::SymbolName"` or `token-goat skeleton "' + filePath + '"` to see the file structure.';
|
|
12685
|
+
recordStat("session_hint", 0, 0);
|
|
12686
|
+
return contextOutput("`head` bypasses read hooks. " + hint);
|
|
12687
|
+
}
|
|
12688
|
+
const nodeRead = extractNodeFileRead(cmd);
|
|
12689
|
+
if (nodeRead !== null) {
|
|
12690
|
+
const { filePath, isDoc } = nodeRead;
|
|
12691
|
+
const hint = isDoc ? 'Use `token-goat section "' + filePath + '::SectionHeading"` to read one section.' : 'Use `token-goat read "' + filePath + '::SymbolName"` to extract a specific symbol.';
|
|
12692
|
+
recordStat("session_hint", 0, 0);
|
|
12693
|
+
return denyOutput("Node.js `fs.readFileSync()` bypasses read hooks. " + hint);
|
|
12694
|
+
}
|
|
12695
|
+
const rgStructural = extractRgStructuralSearch(cmd);
|
|
12696
|
+
if (rgStructural !== null) {
|
|
12697
|
+
const { filePath } = rgStructural;
|
|
12698
|
+
recordStat("session_hint", 0, 0);
|
|
12699
|
+
return contextOutput(
|
|
12700
|
+
'Searching for code definitions with `rg`/`grep` is slower than surgical reads. Use `token-goat skeleton "' + filePath + '"` to see all symbols with line numbers, or `token-goat outline "' + filePath + '"` for symbols with docstrings and line ranges.'
|
|
12701
|
+
);
|
|
12702
|
+
}
|
|
12515
12703
|
const monitoringHint = getMonitoringRecallHint(cmd);
|
|
12516
12704
|
if (monitoringHint !== null) {
|
|
12517
12705
|
const monCmdHash = fingerprintContent(cmd).slice(0, 16);
|