token-goat 2.9.9 → 2.9.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -11
- package/SECURITY.md +17 -19
- package/THIRD_PARTY_NOTICES.md +667 -0
- package/dist/token-goat-chunk-2ZUPQYLO.mjs +3728 -0
- package/dist/{token-goat-chunk-LQ3SGWIW.mjs → token-goat-chunk-3ZALKJ23.mjs} +69 -37
- package/dist/{token-goat-chunk-WBPVYAEO.mjs → token-goat-chunk-4NZUXC4F.mjs} +3 -2
- package/dist/{token-goat-chunk-KIEOFWLL.mjs → token-goat-chunk-AAEYU2U2.mjs} +5307 -6350
- package/dist/{token-goat-chunk-OBDTBOQA.mjs → token-goat-chunk-FDURVZQD.mjs} +10 -2
- package/dist/{token-goat-chunk-DRH4CVGF.mjs → token-goat-chunk-HDL77BN3.mjs} +11 -7
- package/dist/{token-goat-chunk-LYWIRYCF.mjs → token-goat-chunk-LHLQFGWQ.mjs} +1 -1
- package/dist/{token-goat-chunk-L6Q2XM5X.mjs → token-goat-chunk-LKOYKSID.mjs} +1275 -655
- package/dist/{token-goat-chunk-P5SQX2VK.mjs → token-goat-chunk-MKITQ5RY.mjs} +354 -180
- package/dist/{token-goat-chunk-PNMGVJ4C.mjs → token-goat-chunk-MRZ555B3.mjs} +2699 -2175
- package/dist/{token-goat-chunk-K5GKX6ND.mjs → token-goat-chunk-PJPFOOGM.mjs} +6 -5
- package/dist/{token-goat-chunk-FQD3OB5W.mjs → token-goat-chunk-QCQUIPIP.mjs} +6068 -1687
- package/dist/{token-goat-chunk-BYDDWQ2T.mjs → token-goat-chunk-SY7WTZMW.mjs} +5043 -7721
- package/dist/{token-goat-chunk-PXWBHSFB.mjs → token-goat-chunk-T6M7DAW3.mjs} +31 -4
- package/dist/{token-goat-chunk-QCHD2ENV.mjs → token-goat-chunk-V53Z47ZH.mjs} +85 -328
- package/dist/token-goat-chunk-Y5VKNLVD.mjs +5942 -0
- package/dist/token-goat-chunk-YCKCFYTM.mjs +3564 -0
- package/dist/token-goat-hook.mjs +6 -5
- package/dist/token-goat.core.mjs +8 -6
- package/docs/C4_RUNTIME_ARCHITECTURE.md +1 -1
- package/docs/cli.md +4 -1
- package/docs/install.md +131 -11
- package/docs/security.md +3 -1
- package/package.json +8 -3
package/dist/token-goat-hook.mjs
CHANGED
|
@@ -2,12 +2,13 @@ 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-MKITQ5RY.mjs";
|
|
6
|
+
import "./token-goat-chunk-FDURVZQD.mjs";
|
|
7
|
+
import "./token-goat-chunk-V53Z47ZH.mjs";
|
|
8
|
+
import "./token-goat-chunk-SY7WTZMW.mjs";
|
|
9
|
+
import "./token-goat-chunk-AAEYU2U2.mjs";
|
|
10
10
|
import "./token-goat-chunk-EEIDFMEM.mjs";
|
|
11
|
+
import "./token-goat-chunk-YCKCFYTM.mjs";
|
|
11
12
|
import {
|
|
12
13
|
init_define_import_meta_env
|
|
13
14
|
} from "./token-goat-chunk-A37V4PBF.mjs";
|
package/dist/token-goat.core.mjs
CHANGED
|
@@ -2,14 +2,16 @@ 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-MRZ555B3.mjs";
|
|
6
|
+
import "./token-goat-chunk-LKOYKSID.mjs";
|
|
7
|
+
import "./token-goat-chunk-2ZUPQYLO.mjs";
|
|
8
|
+
import "./token-goat-chunk-V53Z47ZH.mjs";
|
|
9
|
+
import "./token-goat-chunk-SY7WTZMW.mjs";
|
|
10
|
+
import "./token-goat-chunk-AAEYU2U2.mjs";
|
|
11
|
+
import "./token-goat-chunk-EEIDFMEM.mjs";
|
|
9
12
|
import {
|
|
10
13
|
installEpipeGuard
|
|
11
|
-
} from "./token-goat-chunk-
|
|
12
|
-
import "./token-goat-chunk-EEIDFMEM.mjs";
|
|
14
|
+
} from "./token-goat-chunk-YCKCFYTM.mjs";
|
|
13
15
|
import {
|
|
14
16
|
init_define_import_meta_env
|
|
15
17
|
} from "./token-goat-chunk-A37V4PBF.mjs";
|
|
@@ -191,7 +191,7 @@ flowchart TB
|
|
|
191
191
|
subgraph ParserAdapters ["Parser Language Adapters"]
|
|
192
192
|
TreeSitter["Inline Tree-Sitter Extractors<br/><small>TS, JS, Python, Go, Rust, Java, C/C++, Ruby</small>"]:::adapter
|
|
193
193
|
RegexInline["Inline Regex Extractors<br/><small>Markdown, JSON, YAML, TOML, CSS, Dockerfile</small>"]:::adapter
|
|
194
|
-
LangAdapters["src/languages/
|
|
194
|
+
LangAdapters["Regex Adapters, inline and src/languages/ (74 non-tree-sitter languages)<br/><small>C#, PHP, Kotlin, GraphQL, SQL, Proto, Apex, etc.</small>"]:::adapter
|
|
195
195
|
end
|
|
196
196
|
|
|
197
197
|
Parser --> TreeSitter
|
package/docs/cli.md
CHANGED
|
@@ -45,6 +45,9 @@ token-goat pdf-extract manual.pdf --pages 12-15 --layout --head 120
|
|
|
45
45
|
| `token-goat yaml-query <file> <path>` | Extract one value or a projected/filtered subset from a YAML document by dot-path instead of a raw Read (same grammar as `json-query`: `[n]` index, `[*]` wildcard, `[field=value]` filter — e.g. `items[status=active].name`). `--head <n>` caps a projected/filtered result. |
|
|
46
46
|
| `token-goat xml-outline <file>` | Structural summary of an XML document (element tag hierarchy, attribute keys, child counts) instead of a raw Read. |
|
|
47
47
|
| `token-goat xml-query <file> <path>` | Extract one value, element text/XML, or a projected/filtered subset from an XML document by XPath-like dot-path instead of a raw Read (same grammar as `json-query`/`yaml-query`: element tags, `@attr`, `[n]` index, `[*]` wildcard, `[attr=value]` filter, `--head <n>`). |
|
|
48
|
+
| `token-goat html-outline <file>` | Structural outline of an HTML document (DOM hierarchy, tags, IDs, classes, element counts, depth, landmarks, tables, forms) instead of reading thousands of lines of markup. Supports `--json`. |
|
|
49
|
+
| `token-goat html-query <file> <selector>` | Extract matching HTML elements or text using standard CSS selectors (tags, `#id`, `.class`, attribute operators `[attr]`, `[attr=val]`, `[attr*=val]`, `[attr^=val]`, `[attr$=val]`, child `>` and descendant combinators) without a headless browser or heavy DOM dependencies. Supports `--text` to strip tags, `--head <n>`, and `--json`. |
|
|
50
|
+
| `token-goat html-lint <file>` | Fast, zero-dependency HTML5 structural linter checking for unclosed tags, void element violations, duplicate IDs, missing viewport/charset, missing alt attributes, and inline script bloat. Supports `--json`. |
|
|
48
51
|
| `token-goat json-outline <file>` | Structural summary of a JSON document (array shape / object key types) instead of a raw Read. |
|
|
49
52
|
| `token-goat json-query <file> <path>` | Extract one value or a projected/filtered subset from a JSON document by dot-path instead of a raw Read: dot-separated keys with optional bracket segments — `[n]` index, `[*]` wildcard (projects every element/value), `[field=value]` filter. Examples: `data.items[3].name`, `items[*].id`, `items[status=active]`. |
|
|
50
53
|
| `token-goat brief "file::symbol"` | Bundle a symbol's body, resolved callers (grouped by enclosing function), and its containing doc section into one round-trip instead of three separate `read`/`callers`/`section` calls. `--limit <n>` caps the callers shown per symbol (default 20; the true caller count is reported even when truncated). Comma-separated `"file::a,b"` fetches several symbols' bundles from one file in a single call, mirroring `read`'s `file::a,b` multi-symbol grammar. Cross-file `"a.ts::x,b.ts::y"` bundles symbols from several files in one call, mirroring `read`'s cross-file grammar — a bare segment inherits the file to its left, and once more than one file is involved each bundle is keyed by the full `file::symbol` so two files contributing the same symbol name stay distinct. Also accepts `read`'s `symbol@LINE` anchor to pick out an otherwise-ambiguous candidate. `-C, --context <n>` adds N lines of real call-site source around each entry of the caller block. `--json`'s `symbol.filePath` and `callers[].file` render root-relative when a project root resolves, absolute when none does — matching the plain-text block above. `--exclude-tests` hides callers whose call site is in a test file, matching `refs`/`callers`; the caller count and the elided tail both count the filtered set, so they never disagree with the rows shown, and when the filter empties the block it says so instead of reporting a bare zero that would read as "nothing calls this". `--json` adds `hiddenByExcludeTests` only when the filter actually hid something. `--grep <pattern>` narrows the caller block to callers whose enclosing symbol name matches this regex (literal substring if it is not valid regex), the same filter `refs --grep`/`call-chain --grep` apply to their own results — useful for a high-fanout symbol whose default 20-caller window is otherwise mostly noise; composes with `--exclude-tests`, and reports `hiddenByGrep` under `--json` only when it hid something. |
|
|
@@ -119,7 +122,7 @@ token-goat pdf-extract manual.pdf --pages 12-15 --layout --head 120
|
|
|
119
122
|
| `token-goat pdf-locate <file> <pattern>` | Find which pages of a PDF match a regex, with a snippet per match, so you can `pdf-extract --pages` only those pages instead of pulling the whole document. `-i`/`--ignore-case` for case-insensitive matching; `--max-matches <n>` caps how many page matches to collect (default 50); `--context <n>` sets the snippet length around each match (default 80); `--pages <spec>` narrows the scan to a page range; `-j`/`--json` emits `{ file, pattern, matchCount, pages, matches }`. |
|
|
120
123
|
| `token-goat pdf-outline <file>` | List a PDF's bookmark/outline tree with page numbers instead of a raw Read. |
|
|
121
124
|
| `token-goat pdf-meta <file> [--json]` | Page count, title/author, and whether a PDF has an extractable text layer (so you know before extracting whether it's scanned/image-only). `--json` emits `{ pageCount, title, author, hasTextLayer }` — `hasTextLayer` as a real boolean rather than a prose sentence, and an absent title/author as `null` rather than the literal `(none)`. |
|
|
122
|
-
| `token-goat image-meta <file> [--json]` | Dimensions, format, byte size, and what a `shrinkImage` pass would cost — a cheap "should I even look at this" probe that reads
|
|
125
|
+
| `token-goat image-meta <file> [--json]` | Dimensions, format, byte size, and what a `shrinkImage` pass would cost — a cheap "should I even look at this" probe that reads image metadata only and never runs OCR. Needs no optional package: the image pipeline is pure TypeScript and ships in the bundle. Says so plainly when the bytes are not a format token-goat can read. |
|
|
123
126
|
| `token-goat image-text <file> [--json]` | OCR text for an image instead of a raw Read. Reports confidence and character count either way; below the usefulness threshold it says so plainly instead of printing low-confidence noise as content. Requires `tesseract.js`; degrades with a clear message when it's missing. |
|
|
124
127
|
| `token-goat csv-query <file>` | Project columns and/or filter rows from a CSV instead of a raw Read. `--columns <cols>` selects a comma-separated subset; `--where <spec>` is repeatable and ANDed, supporting `col=value`, `col!=value`, `col>value`, `col<value`, and `col~=regex`; `--head <n>` caps rows; `--json` emits rows as a JSON array of objects instead of a formatted table; `--delimiter <char>` and `--no-header` handle non-comma or headerless files. |
|
|
125
128
|
| `token-goat csv-profile <file>` | Per-column type inference (number/date/string), null/distinct counts, and min/max or top values for low-cardinality columns, instead of a raw Read. Same `--delimiter`/`--no-header` flags as `csv-query`. |
|
package/docs/install.md
CHANGED
|
@@ -15,7 +15,7 @@ image: /token-goat/assets/goat-social.png
|
|
|
15
15
|
```
|
|
16
16
|
npm install -g token-goat
|
|
17
17
|
token-goat install
|
|
18
|
-
token-goat doctor # confirms hooks and
|
|
18
|
+
token-goat doctor # confirms the hooks, index, and integrations are healthy
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
Three commands. Done. Hooks register and start working immediately; no terminal popups, no tray icon, no service to babysit.
|
|
@@ -39,7 +39,7 @@ The commands stay separate so every retrieval is visible, repeatable, and easy t
|
|
|
39
39
|
|
|
40
40
|
For bounded archive/document comparisons after setup, see the [CLI comparison workflow](cli.md#archivedocument-comparison-workflow).
|
|
41
41
|
|
|
42
|
-
**
|
|
42
|
+
**Image shrinking needs nothing extra.** The biggest single win (~39% smaller than JPEG, ~97% smaller than raw PNG) comes from WebP encoding, and the encoder is pure TypeScript inside the published bundle. There is no native image library to build and no platform where the image pipeline has to be installed separately, so a standard `npm install -g token-goat` already has it. See [Image support](../README.md#image-support) in the README for which formats it converts.
|
|
43
43
|
|
|
44
44
|
Two things change how Claude Code sessions behave: hooks fire automatically (image shrink, re-read dedup, compact manifests), and a delimited routing block written to `~/.claude/CLAUDE.md` plus a registered skill gate the agent's reads — before any file read it must ask whether a `token-goat read` / `symbol` / `section` returns just what it needs, and the block explicitly subordinates the harness's own Read/Grep tool-preference rules to the *fallback* choice once token-goat is ruled out. Install writes no permission entry: whether `token-goat` commands need a per-call approval prompt is left to your own `settings.json`, unchanged.
|
|
45
45
|
|
|
@@ -178,10 +178,73 @@ What works: **the command-routing reminder** (`sessionStart` returns `additional
|
|
|
178
178
|
|
|
179
179
|
**Why the background-shell compression matters most on Copilot.** Copilot runs shell commands in the background: a build or a test suite is started once, and the model then checks on it repeatedly while it runs. Each check hands back everything the command has printed since it started, from the first line. So the second check re-sends the whole first check, the third re-sends the first two, and a check ten minutes into a slow build re-sends the same output for the tenth time. The model has already read all of it and pays again for every word, every time. Token-goat sends the first check through untouched, then returns only the new part on each later check, with one line saying that is what it is; a check that found nothing new comes back as a single short line instead of the whole output again. Measured through the installed hook: a second check of 5,200 characters came back as about 1,250, and a third check that added nothing came back as 60 — roughly a quarter of the cost for the second look and about one percent for the third, improving the longer the command runs. Nothing is lost, because what is cut is what was already sent. It only shortens a check when the new output genuinely continues the last one seen; anything else passes straight through, so the worst case is a saving that does not happen rather than a wrong answer.
|
|
180
180
|
|
|
181
|
-
No ambient environment variable documents "this process is running under Copilot CLI" the way Codex/opencode set one, so the shim sets `TOKEN_GOAT_HARNESS_OVERRIDE=copilot_cli` itself before calling `token-goat hook` (same workaround `--pi` uses). Install also writes a token-goat routing block into `~/.copilot/copilot-instructions.md` (the same delimited-block gate written to `~/.claude/CLAUDE.md` and `~/.codex/AGENTS.md`), merged idempotently so any hand-written content outside the markers is preserved byte-for-byte. If you set `COPILOT_HOME`, install follows it — hooks go to `$COPILOT_HOME/hooks/` and the routing block to `$COPILOT_HOME/copilot-instructions.md`, matching where Copilot CLI actually reads them. To install for one project instead of user scope: `token-goat install --copilot --local` (writes `.github/hooks/token-goat.json` and `.github/copilot-instructions.md` in the current project). To remove: `token-goat uninstall --copilot`.
|
|
181
|
+
No ambient environment variable documents "this process is running under Copilot CLI" the way Codex/opencode set one, so the shim sets `TOKEN_GOAT_HARNESS_OVERRIDE=copilot_cli` itself before calling `token-goat hook` (same workaround `--pi` uses). Install also writes a token-goat routing block into `~/.copilot/copilot-instructions.md` (the same delimited-block gate written to `~/.claude/CLAUDE.md` and `~/.codex/AGENTS.md`), merged idempotently so any hand-written content outside the markers is preserved byte-for-byte. If you set `COPILOT_HOME`, install follows it — hooks go to `$COPILOT_HOME/hooks/` and the routing block to `$COPILOT_HOME/copilot-instructions.md`, matching where Copilot CLI actually reads them. To install for one project instead of user scope: `token-goat install --copilot --local` (writes `.github/hooks/token-goat.json` and `.github/copilot-instructions.md` in the current project). `.github/hooks/token-goat.json` holds absolute paths to node and token-goat on your machine, so do not commit it or the `token-goat-shim.js` next to it: list them in `.git/info/exclude` (just for you) or `.gitignore`. Install prints this reminder. To remove: `token-goat uninstall --copilot`.
|
|
182
182
|
|
|
183
183
|
**If Copilot CLI starts denying every tool call with `Denied by preToolUse hook ... (hook errored)`:** this is Copilot's own fail-closed behavior for a `preToolUse` hook that crashes, exits non-zero, or returns unparseable output -- it isn't limited to token-goat's own tool calls, since a fail-closed `preToolUse` hook blocks the whole session. Copilot caches hook configs at session start, so **renaming or reinstalling the hook mid-session has no effect** -- the only recovery is: run `token-goat install --copilot` (or `token-goat doctor`, which now checks the installed hook end-to-end and calls out a stale node-binary path from an nvm/fnm/volta upgrade specifically), then **fully restart Copilot CLI**.
|
|
184
184
|
|
|
185
|
+
### VS Code (Copilot agent) users
|
|
186
|
+
|
|
187
|
+
```
|
|
188
|
+
token-goat install --vscode
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
This installs into the **current project**: the MCP entry goes to `.vscode/mcp.json` (a timestamped `.bak` is written before any change to an existing file), the agent hooks to `.github/hooks/` (`token-goat.json` plus the `token-goat-shim.js` it runs), and the routing guidance to `.github/copilot-instructions.md`. Run it once in each project you want token-goat in. VS Code's Copilot agent reads hooks from that folder, so token-goat sees the agent's built-in tool calls: `read_file`, `view_image`, `list_dir`, `grep_search`, `file_search`, `create_file`, `replace_string_in_file`, `insert_edit_into_file`, `edit_notebook_file`, and `run_in_terminal`. Both `.vscode/mcp.json` and `.github/hooks/token-goat.json` hold absolute paths to node and token-goat on your machine, so do not commit them or the `token-goat-shim.js` next to them: list them in `.git/info/exclude` (just for you) or `.gitignore`. Install prints this reminder.
|
|
192
|
+
|
|
193
|
+
`--vscode` is the one integration that installs into the project rather than user scope by default, and the reason is a limitation of VS Code itself. VS Code resolves an agent hook's working directory from the hook **file's** own location: the folder of the workspace that contains it, or the workspace's first folder when no workspace contains it. A user-scope hooks file lives in `~/.copilot/hooks/`, which is inside no workspace folder, so it always runs with the **first** folder of a multi-root workspace as its working directory — and since token-goat confines every pre-approval hook to that directory, read hints, image shrinking and edit interception silently do nothing for every other folder. A project-scope hooks file is inside its own folder, so each folder gets its own.
|
|
194
|
+
|
|
195
|
+
Add `--user` for the old behaviour — one install covering every project, at `%APPDATA%\Code\User\mcp.json` (or the platform equivalent), `~/.copilot/hooks/`, and `~/.copilot/instructions/token-goat.instructions.md` — with the multi-root limitation above. `-p`/`--project` is still accepted and selects what is now the default. Running `token-goat install --vscode` on a machine that has the old user-scope install **moves** it into the project and prints a note saying so, because VS Code runs every hooks file it finds in both scopes and leaving both in place would fire every hook twice. `token-goat doctor` reports a user-scope install that is still around.
|
|
196
|
+
|
|
197
|
+
What works: a repeated read of a large file is denied, with a pointer to what the agent already has; hints ride along with reads and edits; a large image inside the workspace is shrunk before `view_image` loads it; edited files are queued for reindexing; and the session-start reminder tells the agent token-goat exists. The hook runs before VS Code asks you to approve a call, so token-goat does not look at any file or folder on a network share or outside the workspace until then. What does not: VS Code gives hooks no way to change what a tool returns, so token-goat cannot fold or trim what `read_file` returns the way it does in Claude Code. Terminal output is not compressed either: VS Code does not tell the hook which shell will run a `run_in_terminal` command, so rewriting it safely is not possible, and token-goat leaves the command as it is. VS Code also never fires a pre-compaction hook, so the compaction manifest has no route there.
|
|
198
|
+
|
|
199
|
+
The hooks folder and files are the same ones `token-goat install --copilot` uses, and one shim serves both: it tells a VS Code payload from a Copilot CLI one and answers each in its own format. token-goat records which install owns the files (`token-goat.owners` next to them), so `token-goat uninstall --vscode` leaves the hooks in place while `--copilot` still needs them, and the other way round. To remove: `token-goat uninstall --vscode` (add `--user` for a user-scope install).
|
|
200
|
+
|
|
201
|
+
If VS Code's `chat.useClaudeHooks` setting is on, VS Code also runs the Claude Code hooks in `~/.claude/settings.json`, so each token-goat hook fires twice. `token-goat install --vscode` prints a note when it sees that setting, and `token-goat doctor` reports it. Turn the setting off to keep only the VS Code hooks. token-goat never edits your VS Code settings.
|
|
202
|
+
|
|
203
|
+
### Visual Studio users
|
|
204
|
+
|
|
205
|
+
```
|
|
206
|
+
token-goat install --visualstudio
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
This is for the full Visual Studio IDE with GitHub Copilot agent mode: Visual Studio 2022 17.14 or later, or Visual Studio 2026. It registers token-goat's MCP server under the `servers` key of `%USERPROFILE%\.mcp.json`, the user-level file Visual Studio reads ([Microsoft's MCP servers page](https://learn.microsoft.com/en-us/visualstudio/ide/mcp-servers)), and adds a routing block to `%USERPROFILE%\copilot-instructions.md`, which Visual Studio 2026 reads as user-level custom instructions ([Microsoft's chat context page](https://learn.microsoft.com/en-us/visualstudio/ide/copilot-chat-context)). A user install never writes into the folder you run it from. Add `-p`/`--project` to install for the solution in the current folder instead: the entry goes to `.mcp.json` and the block to `.github/copilot-instructions.md`, which Visual Studio 2022 reads too. `.mcp.json` then holds absolute paths to node and token-goat on your machine, so do not commit it.
|
|
210
|
+
|
|
211
|
+
In Visual Studio, token-goat works through its MCP tools and instructions only. Visual Studio has no documented agent hooks (GitHub's [hooks page](https://docs.github.com/en/copilot/concepts/agents/hooks) lists only Copilot cloud agent and Copilot CLI), so `--visualstudio` writes no hooks file, and there is no read dedup, no hints, no image shrink, and no output folding. The agent gets narrow reads only when it calls the token-goat tools.
|
|
212
|
+
|
|
213
|
+
Two steps in Visual Studio after installing:
|
|
214
|
+
|
|
215
|
+
1. Tools > Options: turn on "Enable custom instructions to be loaded from .github/copilot-instructions.md files and added to requests". Without it, Visual Studio ignores the routing block.
|
|
216
|
+
2. In Copilot Chat agent mode, open the Tools picker and tick the token-goat tools. New MCP tools start disabled. Visual Studio 18.7 and later also asks you to trust the server when its command or arguments change, for example after a reinstall.
|
|
217
|
+
|
|
218
|
+
Both `.mcp.json` files are also read by Claude Code, under a different key (`mcpServers`), and Claude Code stops with an error on a `.mcp.json` that has no `mcpServers` key. So token-goat writes its entry under `servers` and, when the file has no `mcpServers` key yet, adds an empty one. It registers nothing for Claude Code and leaves your own `mcpServers` entries as they were. A timestamped `.bak` is written before any change to an existing `.mcp.json`. Visual Studio also reads `.vscode/mcp.json`, so if `token-goat install --vscode` (project scope, the default) put token-goat there too, Visual Studio lists the server twice. The install prints a note when that happens, and `token-goat doctor` warns about it; keep one of the two. If `--vscode` (project scope) or `--copilot --local` already put a token-goat block in `.github/copilot-instructions.md`, the Visual Studio block shrinks to a short addendum rather than repeating it, and grows back to the full text when that other block is removed.
|
|
219
|
+
|
|
220
|
+
`token-goat doctor` reports the entry and warns when its node or token-goat path no longer exists. To remove: `token-goat uninstall --visualstudio` (add `-p` for the project install). It removes only token-goat's entry and block.
|
|
221
|
+
|
|
222
|
+
### Zed users
|
|
223
|
+
|
|
224
|
+
```
|
|
225
|
+
token-goat install --zed
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Zed's first-party agent has no agent-hooks API at all ([zed-industries/zed#52688](https://github.com/zed-industries/zed/issues/52688) is still open), so `--zed` does not install hooks: it registers token-goat as an MCP context server instead, the only integration surface Zed offers. This is user scope only — there is no `-p`/`--project` option, since Zed has no documented project-local equivalent of VS Code's `.vscode/mcp.json`.
|
|
229
|
+
|
|
230
|
+
`--zed` writes two files: a small generated shim script (`token-goat-mcp.cmd` on Windows, `token-goat-mcp.sh` elsewhere) that launches `token-goat mcp-serve`, and an entry in Zed's `settings.json` (`%APPDATA%\Zed\settings.json` on Windows, `~/.config/zed/settings.json` elsewhere) pointing `context_servers.token-goat` at that shim. Any other content already in your `settings.json` — comments, your theme, other context servers — is left exactly as it was, and a timestamped `.bak` is written before any change to an existing file. To use it, open Zed's Agent panel and enable the token-goat server under its MCP tools list; new servers start disabled the same way VS Code's do.
|
|
231
|
+
|
|
232
|
+
Like Visual Studio, this is MCP tools only: no read dedup, no hints, no image shrink, no output folding, since there is no hook to fire them from. The agent gets narrow reads only when it calls the token-goat tools directly.
|
|
233
|
+
|
|
234
|
+
`token-goat doctor` reports whether the entry is present. To remove: `token-goat uninstall --zed`, which deletes the shim and the `context_servers.token-goat` entry (and the whole `settings.json`, but only if token-goat created it and nothing else was ever added to it).
|
|
235
|
+
|
|
236
|
+
### Cursor users
|
|
237
|
+
|
|
238
|
+
```
|
|
239
|
+
token-goat install --cursor
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
`--cursor` registers token-goat as an MCP server in `~/.cursor/mcp.json` (root `mcpServers`, confirmed against the installed Cursor 3.19.7 bundle's own JSON schema for that file). Add `-p`/`--project` to write `<project>/.cursor/mcp.json` instead — Cursor reads both. Cursor's schema rejects unknown properties on a server entry (`additionalProperties: false`) and has no `type` field, unlike VS Code's and Visual Studio's `servers` entries, so the entry token-goat writes is `command` + `args` only. A timestamped `.bak` is written before any change to an existing `mcp.json`.
|
|
243
|
+
|
|
244
|
+
`--cursor` never writes `~/.cursor/hooks.json` (or `.cursor/hooks.json`), on purpose. Cursor's own shipped code loads `~/.claude/settings.json` by default (`thirdPartyExtensibilityEnabled` defaults to on) and translates Claude Code's hook step names into its own before deduping against anything already in `hooks.json` by exact command string. So a plain `token-goat install` for Claude Code already makes those same hooks fire once inside Cursor, automatically, with no separate Cursor hooks file to install or keep in sync. Writing a second copy into `hooks.json` would only add a way for the two copies to drift and fire twice, and `~/.cursor/hooks.json` may already be a real file some other tool manages — token-goat never touches it. If you have not run a plain `token-goat install` yet, Cursor's MCP tools still work, but no hooks fire there until you do.
|
|
245
|
+
|
|
246
|
+
`token-goat doctor` reports the MCP entry and whether Claude Code hooks are installed for Cursor to pick up. To remove: `token-goat uninstall --cursor` (add `-p` for the project install). It removes only token-goat's MCP entry.
|
|
247
|
+
|
|
185
248
|
### Grok CLI (xAI Grok Build) users
|
|
186
249
|
|
|
187
250
|
Grok Build already reads Claude Code's `~/.claude/settings.json` as a "Harness Compatibility" source out of the box (confirmed against grok 0.2.93 and its own [hooks doc](https://github.com/xai-org/grok-build/blob/main/crates/codegen/xai-grok-pager/docs/user-guide/10-hooks.md)), so `token-goat install` alone already gets most of the integration working — image shrinking, session hints, post-edit indexing, and bash output compression all fire. The one gap: Grok's own `PreToolUse` hook contract documents only `{"decision":"allow"}` / `{"decision":"deny","reason":"..."}`, never token-goat's harness-independent `{"decision":"block","reason":"..."}` shape (unlike Gemini CLI, whose docs explicitly confirm `"block"` as an accepted alias for `"deny"`), so re-read denial and oversized-first-read redirects don't reliably block on the Claude Code compat path alone.
|
|
@@ -198,7 +261,15 @@ To remove: `token-goat uninstall --grok`.
|
|
|
198
261
|
|
|
199
262
|
No separate install step needed. Token-goat compresses the terminal output of these tools automatically as soon as they appear on your PATH. Run `token-goat doctor` to confirm they are detected — the "Third-party AI tools" section will show `detected — bash output compression active`.
|
|
200
263
|
|
|
201
|
-
Filters are built in for: **Cline** (`cline` / `claude-dev`), **Windsurf** (`windsurf`, including Cascade AI patterns), **Cursor** (`cursor`), **GitHub Copilot CLI** (`gh copilot explain/suggest` and the standalone `copilot` binary — this passive output filter is separate from the `--copilot` hook bridge above; it works with no install step and covers Copilot CLI's own terminal chrome, not the hook-driven read/index integrations), **Aider** (`aider`), **Continue** (`continue`), **OpenCode** (`opencode`). Each filter strips version banners, spinner/thinking lines, token-usage boilerplate, and tool-call progress noise while keeping the AI response body, error signals, and any user-approval prompts verbatim.
|
|
264
|
+
Filters are built in for: **Cline** (`cline` / `claude-dev`), **Windsurf** (`windsurf`, including Cascade AI patterns), **Cursor** (`cursor` — this passive terminal filter is separate from the `--cursor` MCP bridge in [Cursor users](#cursor-users) above; it needs no install step), **GitHub Copilot CLI** (`gh copilot explain/suggest` and the standalone `copilot` binary — this passive output filter is separate from the `--copilot` hook bridge above; it works with no install step and covers Copilot CLI's own terminal chrome, not the hook-driven read/index integrations), **Aider** (`aider`), **Continue** (`continue`), **OpenCode** (`opencode`). Each filter strips version banners, spinner/thinking lines, token-usage boilerplate, and tool-call progress noise while keeping the AI response body, error signals, and any user-approval prompts verbatim.
|
|
265
|
+
|
|
266
|
+
Windsurf gets terminal-output compression only — not the read/index hook integration Claude Code, Codex, Copilot CLI, Gemini, Qwen, Kimi, VS Code, Visual Studio and Grok get above. Windsurf's Cascade agent hooks (`cascadeHooksJson`) are configured on Windsurf's own servers, per team, not from a file on your machine, so there is no local hook config for token-goat to install into. There is no `--windsurf` flag, and none is planned unless that changes.
|
|
267
|
+
|
|
268
|
+
### JetBrains IDEs (WebStorm, IntelliJ, PyCharm, Rider, PhpStorm) users
|
|
269
|
+
|
|
270
|
+
There is no `--junie` or `--jetbrains` flag, and no JetBrains integration of any kind today: no hooks, no MCP registration, no terminal-output filter (JetBrains IDEs have no CLI binary for token-goat to detect on your PATH). Junie, the JetBrains ACP agent, reads guidelines from `.junie/AGENTS.md`, MCP servers from `~/.junie/mcp/mcp.json`, and — per JetBrains' own docs, not a live-verified source — hooks from `~/.junie/config.json`, but no `~/.junie` directory exists without Junie itself creating one, and no live Junie CLI was available to confirm the on-disk shape of any of those files. Writing one on documentation alone risks shipping a config Junie ignores or rejects, so token-goat writes nothing there.
|
|
271
|
+
|
|
272
|
+
Separately, GitHub's Copilot-for-JetBrains plugin documents (as of its March 2026 changelog) previewing agent hooks placed in `.github/hooks/` — the same folder `--copilot` and `--vscode` already write for their own hook integrations. If that holds, a project that has already run `token-goat install --copilot` or `--vscode` may get some Copilot-for-JetBrains hook pickup for free, with no JetBrains-specific code in token-goat at all. This is unconfirmed: the real Copilot-for-JetBrains agent plugin is not installed on the machine this was checked from (only its theme jar is present), so the claim rests on GitHub's changelog, not a live run, and is not something token-goat currently relies on or tests for.
|
|
202
273
|
|
|
203
274
|
### Updating
|
|
204
275
|
|
|
@@ -207,7 +278,7 @@ There is no auto-update mechanism — token-goat never schedules or runs anythin
|
|
|
207
278
|
| When | Command |
|
|
208
279
|
|------|---------|
|
|
209
280
|
| Update now | `npm install -g token-goat@latest` |
|
|
210
|
-
| Reinstall from scratch (broken
|
|
281
|
+
| Reinstall from scratch (broken or partial install) | `npm install -g token-goat@latest` |
|
|
211
282
|
|
|
212
283
|
### Upgrading from the Python version
|
|
213
284
|
|
|
@@ -285,18 +356,44 @@ For VS Code using the token-goat MCP server (`token-goat install --vscode`), ena
|
|
|
285
356
|
}
|
|
286
357
|
```
|
|
287
358
|
|
|
359
|
+
## Supported languages
|
|
360
|
+
|
|
361
|
+
One table in `src/language_specs.ts` drives every per-language list token-goat uses, so this is the whole set. **Symbols** means `symbol`, `read "file::Name"`, `skeleton` and `outline` return named declarations from the file. **Structure only** means the file is indexed for headings, keys or sections rather than code symbols.
|
|
362
|
+
|
|
363
|
+
**Symbols:** ABAP (`.abap`), Apex (`.cls`, `.trigger`), Assembly (`.s`, `.asm`, `.nasm`), Bash and compatible shells (`.sh`, `.bash`, `.zsh`, `.ksh`, `.bats`), C (`.c`, `.h`), C# (`.cs`), C++ (`.cpp`, `.cc`, `.cxx`, `.hpp`, `.hxx`), Clojure (`.clj`, `.cljs`, `.cljc`), CMake (`.cmake`, `CMakeLists.txt`), COBOL (`.cbl`, `.cob`, `.cobol`, `.cpy`), Common Lisp (`.lisp`, `.lsp`, `.cl`), Dart (`.dart`), Elixir (`.ex`, `.exs`), Emacs Lisp (`.el`), Erlang (`.erl`, `.hrl`), F# (`.fs`, `.fsi`, `.fsx`), Fortran (`.f`, `.for`, `.f77`, `.f90`, `.f95`, `.f03`, `.f08`), GLSL (`.glsl`, `.vert`, `.frag`, `.comp`, `.geom`, `.tesc`, `.tese`), Go (`.go`), GraphQL (`.graphql`, `.gql`), Groovy (`.groovy`, `.gvy`, `.gradle`, `Jenkinsfile`), Haskell (`.hs`), HLSL (`.hlsl`, `.hlsli`), Java (`.java`), JavaScript (`.js`, `.jsx`, `.mjs`, `.cjs`), JCL (`.jcl`), Kotlin (`.kt`, `.kts`), Lua (`.lua`), MATLAB, Metal (`.metal`), Natural (`.nsp`, `.nsn`, `.nss`, `.nsa`, `.nsl`, `.nsg`, `.nsc`, `.nsh`), Nix (`.nix`), Objective-C (`.mm`, and a `.h` that declares an `@interface` or `@protocol`), OCaml (`.ml`, `.mli`), OpenEdge ABL, Pascal (`.pas`, `.dpr`, `.dpk`, `.lpr`, `.dfm`), Perl (`.pl`, `.pm`), PHP (`.php`), PL/I (`.pli`, `.pl1`), PowerShell (`.ps1`, `.psm1`), Protocol Buffers (`.proto`), Python and Starlark (`.py`, `.pyi`, `.bzl`, `.star`, `BUILD`, `WORKSPACE`, `MODULE.bazel`), R (`.r`), Racket (`.rkt`, `.rktl`), RPG (`.rpgle`, `.sqlrpgle`), Ruby (`.rb`, `.ruby`, `.rake`, `Gemfile`, `Rakefile`, and the other extensionless Ruby DSL files), Rust (`.rs`), Salesforce markup (`.cmp`, `.app`, `.evt`, `.intf`, `.design`, `.auradoc`, `.tokens`, `.page`, `.component`, `.email`), Salesforce metadata, SAS (`.sas`), Scala (`.scala`, `.sc`), Scheme (`.scm`, `.ss`), Solidity (`.sol`), SQL and PL/SQL (`.sql`, `.pks`, `.pkb`, `.pls`, `.plsql`, `.pck`, `.prc`, `.fnc`, `.trg`, `.tps`, `.tpb`), Swift (`.swift`), Terraform (`.tf`, `.tfvars`, `.hcl`), Thrift (`.thrift`), TypeScript (`.ts`, `.tsx`, `.mts`, `.cts`), VHDL (`.vhd`, `.vhdl`), Visual Basic (`.vb`, `.bas`, `.vbs`, `.frm`), WGSL (`.wgsl`), Windows batch (`.bat`, `.cmd`), Zig (`.zig`).
|
|
364
|
+
|
|
365
|
+
**Structure only:** Astro (`.astro`), CSS and its preprocessors (`.css`, `.scss`, `.sass`, `.less`), Dockerfile, environment files (`.env`, `.envrc`), HTML (`.html`, `.htm`), INI (`.ini`, `.cfg`, `.conf`), JSON including JSON with comments and Avro schemas (`.json`, `.jsonc`, `.avsc`), Jupyter notebooks (`.ipynb`), Makefiles (`.mk`, `Makefile`), Markdown (`.md`, `.markdown`, `.mdx`), Svelte (`.svelte`), TOML (`.toml`), Vue (`.vue`), YAML (`.yaml`, `.yml`), and seven template-engine dialects read as HTML: Liquid (`.liquid`), Jinja2 (`.j2`, `.jinja`, `.jinja2`), Handlebars (`.hbs`, `.handlebars`), ERB (`.erb`), EJS (`.ejs`), Nunjucks (`.njk`), Twig (`.twig`).
|
|
366
|
+
|
|
367
|
+
MATLAB, OpenEdge ABL and Salesforce metadata share their file extensions with other languages, so token-goat picks them by looking at the file's contents rather than its name.
|
|
368
|
+
|
|
369
|
+
## Troubleshooting
|
|
370
|
+
|
|
371
|
+
### tree-sitter unavailable
|
|
372
|
+
|
|
373
|
+
`token-goat doctor` warns when the optional `tree-sitter` package does not load. Without it, TypeScript, JavaScript, Python, Go, Rust, Ruby, Java, C and C++ files are read by a rougher scan that finds fewer symbols and no references, and the skeleton fold is off. Every other language indexes as usual. The doctor line says which of three causes applies:
|
|
374
|
+
|
|
375
|
+
- **Not installed.** The install left out optional dependencies. Run `npm install -g token-goat --include=optional`.
|
|
376
|
+
- **No native build for this platform.** `tree-sitter` and each grammar ship prebuilt binaries inside the npm package for Windows, Linux and macOS on x64 and arm64, and pick the matching one when they load, so no compiler runs and nothing is downloaded when one matches. Their install script (`node-gyp-build`) compiles a binary only when none matches, which needs a C++ toolchain. npm 12 skips dependency install scripts unless you allow them, so reinstall with `npm install -g token-goat --allow-scripts=tree-sitter`.
|
|
377
|
+
- **Binary will not load.** The binary was built for a different Node version or CPU. Reinstall with the same Node you run token-goat with: `npm install -g token-goat`.
|
|
378
|
+
|
|
379
|
+
### A file type shows no symbols
|
|
380
|
+
|
|
381
|
+
`outline`, `skeleton` and `read "file::Name"` say when token-goat has no symbol extractor for a file type. The [supported languages](#supported-languages) list above says which file types are indexed; anything outside it, and any extension token-goat does not recognize, has no extractor. Grep and plain reads still work on those files. To ask for support for another file type, [open an issue](https://github.com/DFKHelper/token-goat/issues) or email token-goat@dfkhelper.com.
|
|
382
|
+
|
|
288
383
|
## What gets installed?
|
|
289
384
|
|
|
290
385
|
`token-goat install` writes the following on your machine — nothing else, anywhere. Every entry is reversed by `token-goat uninstall`. Integrations for other harnesses are additive on the way out as well as in, so a plain uninstall does not touch one you installed with `--codex`, `--copilot`, or a sibling flag: rather than undo something you did not ask about, it names each one still present and the flag that removes it. Run `token-goat doctor` at any time to see which of these are currently present.
|
|
291
386
|
|
|
292
|
-
|
|
387
|
+
A bare `token-goat install` (no other flag) installs the Claude Code integration below. Passing a harness flag — `--vscode`, `--codex`, `--gemini`, and so on — installs only that harness's own files, listed in its own section further down: it never also touches `~/.claude/` on the side. If you want both, run `install` again with the other flag, or pass both flags in the same command. The one exception is `--hermes`, which delegates to `claude -p` and so genuinely needs the Claude Code hooks below; it installs them the same way a bare `install` does.
|
|
388
|
+
|
|
389
|
+
**Claude Code integration** (`~/.claude/`; written by a bare `install`, or by `--hermes`)
|
|
293
390
|
|
|
294
391
|
| Path | What |
|
|
295
392
|
|------|------|
|
|
296
393
|
| `~/.claude/settings.json` | Hook entries for `SessionStart`, `PreToolUse` (Read/Grep/Bash, Drive/WebFetch), `PostToolUse` (Edit/Write/MultiEdit, Read/Grep/Glob, Bash, WebFetch, Skill), and `PreCompact`. Hook entries only: install writes nothing under `permissions`, so it never grants the agent unprompted execution of anything. Existing hooks are preserved; a timestamped `.bak` is written before any change.<br><br>The `PreToolUse` and `PostToolUse` matchers are narrowed to exactly the tools token-goat handles (plus `^mcp__`), generated from the live hook registry rather than a fixed list, so they can't fall out of date as handlers change. Claude Code starts a new process per matcher hit and most of that cost is process startup, so a catch-all matcher would make every unrelated tool call — `TodoWrite`, `TaskUpdate`, and friends — pay for a hook that has nothing to do. |
|
|
297
394
|
| `~/.claude/hooks/token-goat-shim.js` | The hook script those `settings.json` commands invoke (`"<node>" "<shim>" <event> "<entry>"`). It imports the hook library in-process instead of spawning a second process, and naming the node binary directly skips the npm bin wrapper — on Windows a `cmd.exe` layer every hook would otherwise pay for. Measured 480 ms → 324 ms per hook call. Regenerated on every `install` run. Always written here even for a `--project` install, since the command bakes in machine-specific absolute paths; a project-scope `settings.json` just points at this one. |
|
|
298
|
-
| `~/.claude/CLAUDE.md` | A delimited block (`<!-- token-goat-begin -->` … `<!-- token-goat-end -->`) telling the agent to prefer `token-goat read` / `symbol` / `section` over `Read` / `Grep`. Any existing content is preserved. |
|
|
299
|
-
| `~/.claude/skills/token-goat/SKILL.md` | The token-goat skill — the same routing guidance in skill form. |
|
|
395
|
+
| `~/.claude/CLAUDE.md` | A delimited block (`<!-- token-goat-begin -->` … `<!-- token-goat-end -->`) telling the agent to prefer `token-goat read` / `symbol` / `section` over `Read` / `Grep`. Any existing content is preserved; a timestamped `.bak` is written before any change. |
|
|
396
|
+
| `~/.claude/skills/token-goat/SKILL.md` | The token-goat skill — the same routing guidance in skill form. A timestamped `.bak` is written before any change. |
|
|
300
397
|
|
|
301
398
|
**Background worker.** token-goat does not register any persistent OS-level autostart entry — no Windows registry `Run` key, no systemd user unit, no XDG `.desktop` entry, and no macOS launchd `.plist`. The worker that drains the reindex queue is started manually as a detached child process: `token-goat worker start` launches `node <npm-prefix>/lib/node_modules/token-goat/dist/token-goat.mjs --worker-daemon` and returns immediately, and the child keeps running independent of the parent shell. `token-goat worker status` reports whether it's running; `token-goat worker stop` kills it. If it crashes or is killed while the machine stays up, the next edit hook detects it's gone and respawns it automatically (checked on every edit, rate-limited to roughly once every 5 minutes). It does not survive a reboot or logout, though — re-run `token-goat worker start` after either.
|
|
302
399
|
|
|
@@ -372,12 +469,35 @@ Three things follow, and they are worth knowing before you decide. It never leav
|
|
|
372
469
|
| `~/.grok/hooks/token-goat.json` | Hook config (`{ hooks }`) registering `PreToolUse`, `PostToolUse`, `PreCompact`, `UserPromptSubmit`, and `SubagentStop` with an empty (match-everything) matcher, each pointing at the shim script below. Existing files elsewhere in the hooks directory are untouched; global scope only (Grok's project-scoped `.grok/hooks/` requires a separate manual `/hooks-trust` grant). |
|
|
373
470
|
| `~/.grok/hooks/token-goat-shim.js` | The shim `token-goat.json`'s hook commands invoke. Translates `PreToolUse`'s deny shape only (`{"decision":"block",...}` → Grok's documented `{"decision":"deny",...}`, plus exit code 2); every other event's response is forwarded unmodified. Regenerated on every `install --grok` run. |
|
|
374
471
|
|
|
375
|
-
**With `--vscode`** (VS Code MCP configuration;
|
|
472
|
+
**With `--vscode`** (VS Code MCP configuration; **project scope by default** — the only integration that inverts the usual default, because VS Code pins a user-scope hook to the first folder of a multi-root workspace — with `--user` for the old every-project install)
|
|
473
|
+
|
|
474
|
+
| Path | What |
|
|
475
|
+
|------|------|
|
|
476
|
+
| `<project>/.vscode/mcp.json` — or `%APPDATA%\Code\User\mcp.json` (Windows) / `~/Library/Application Support/Code/User/mcp.json` (macOS) / `~/.config/Code/User/mcp.json` (Linux) with `--user` | Merges the `token-goat` stdio entry under VS Code's `servers` root key, preserving unrelated servers and settings. `--user` refuses to write when the project scope already has a token-goat-managed entry, to avoid a duplicate registration; the reverse direction is a migration instead, and removes the user-scope install. |
|
|
477
|
+
| `<project>/.github/copilot-instructions.md` (`~/.copilot/instructions/token-goat.instructions.md` with `--user`) | A delimited VS Code routing block that documents supported MCP selection and what the agent hooks can and cannot do with built-in file reads. The user-scope file is a personal instructions file with `applyTo: '**'`, so VS Code applies it in every workspace; install creates it with that frontmatter if it is missing and otherwise merges the block in, and uninstall deletes it again when nothing else is left in it. A user-scope install never writes into the folder you run it from. |
|
|
478
|
+
| `<project>/.github/hooks/token-goat.json`, `token-goat-shim.js`, `token-goat.owners` (`~/.copilot/hooks/` with `--user`) | VS Code agent hooks, shared with `--copilot`; the owners file records which of the two installs still uses them. |
|
|
479
|
+
|
|
480
|
+
**With `--visualstudio`** (Visual Studio MCP configuration; user scope by default, `-p`/`--project` for the solution folder)
|
|
481
|
+
|
|
482
|
+
| Path | What |
|
|
483
|
+
|------|------|
|
|
484
|
+
| `%USERPROFILE%\.mcp.json` (`<project>/.mcp.json` with `-p`) | Merges the `token-goat` stdio entry under the `servers` root key, preserving other servers, comments, and Claude Code's `mcpServers` key. Refuses to write if the other scope already has a token-goat-managed entry. Uninstall removes the entry, and deletes the file if nothing else is left in it. |
|
|
485
|
+
| `%USERPROFILE%\copilot-instructions.md` (`<project>/.github/copilot-instructions.md` with `-p`) | A delimited Visual Studio routing block (`<!-- token-goat-visualstudio-begin -->` … `<!-- token-goat-visualstudio-end -->`). Everything outside the markers is preserved. |
|
|
486
|
+
|
|
487
|
+
**With `--zed`** (Zed MCP context server; user scope only, no project option)
|
|
488
|
+
|
|
489
|
+
| Path | What |
|
|
490
|
+
|------|------|
|
|
491
|
+
| `%APPDATA%\Zed\settings.json` (Windows) / `~/.config/zed/settings.json` (macOS/Linux) | Merges the `token-goat` entry (`command`, `timeout` only) under Zed's `context_servers` root key, preserving unrelated keys, comments, and other context servers. Refuses to write if a non-token-goat `token-goat` entry is already there. Uninstall removes the entry, and deletes the file if nothing else is left in it. |
|
|
492
|
+
| `%APPDATA%\Zed\token-goat-mcp.cmd` (Windows) / `~/.config/zed/token-goat-mcp.sh` (macOS/Linux) | The shim script the `settings.json` entry's `command` points at; runs `node <bundle path> mcp-serve`. Regenerated on every `install --zed` run. |
|
|
493
|
+
|
|
494
|
+
**With `--cursor`** (Cursor MCP server; user scope by default, `-p`/`--project` for `<project>/.cursor/mcp.json`)
|
|
376
495
|
|
|
377
496
|
| Path | What |
|
|
378
497
|
|------|------|
|
|
379
|
-
|
|
|
380
|
-
|
|
498
|
+
| `~/.cursor/mcp.json` (`<project>/.cursor/mcp.json` with `-p`) | Merges the `token-goat` entry (`command`, `args` only -- no `type` key) under Cursor's `mcpServers` root key, preserving unrelated keys, comments, and other servers. Refuses to write if a non-token-goat `token-goat` entry is already there. Uninstall removes the entry, and deletes the file if nothing else is left in it. |
|
|
499
|
+
|
|
500
|
+
Nothing is ever written to `~/.cursor/hooks.json` by `--cursor` -- see [Cursor users](#cursor-users) above.
|
|
381
501
|
|
|
382
502
|
**With `--hermes`** (Hermes Agent integration)
|
|
383
503
|
|
package/docs/security.md
CHANGED
|
@@ -15,7 +15,7 @@ Outbound network is reserved to these explicit cases:
|
|
|
15
15
|
- Google Drive API calls, only if you already authorized Drive in Claude Code. Token-goat never prompts for its own auth.
|
|
16
16
|
- Image fetches from URLs: either explicit via `token-goat fetch-image <url>`, or when the AI agent issues a WebFetch call that returns image content — the hook intercepts and shrinks the image. The URL always originates from the agent's work, not from token-goat itself.
|
|
17
17
|
- `token-goat screenshot <url>` navigates a headless browser to the URL you give it, subject to the target restrictions described below.
|
|
18
|
-
- The first `token-goat semantic` run on a machine downloads the embedding model from `huggingface.co`, pinned to an immutable commit rather than a mutable branch and checked against a recorded SHA-256 and byte length before it is used, and only once `onnxruntime-node` has been installed (see below — it is not part of a default install). Subsequent runs use the local cache, re-verify it, and make no network call. Setting `TOKEN_GOAT_MODEL_CACHE_DIR` adds one more place to look before the network: a directory you name that outlives any single data root, which is how a continuous-integration job stops refetching the weights once per worker. A file taken from there is checked against the same recorded digest as a downloaded one and dropped from the shared directory if it fails, so a tampered copy costs a download and cannot substitute different weights. It is an environment variable only, so no project config file can point it anywhere. Skip the download entirely by setting `indexing.embeddings_enabled = false` (it is on by default), in which case `semantic` falls back to full-text search.
|
|
18
|
+
- The first `token-goat semantic` run on a machine downloads the embedding model from `huggingface.co`, pinned to an immutable commit rather than a mutable branch and checked against a recorded SHA-256 and byte length before it is used, and only once `onnxruntime-node` has been installed (see below — it is not part of a default install). Subsequent runs use the local cache, re-verify it, and make no network call. Setting `TOKEN_GOAT_MODEL_CACHE_DIR` adds one more place to look before the network: a directory you name that outlives any single data root, which is how a continuous-integration job stops refetching the weights once per worker. A file taken from there is checked against the same recorded digest as a downloaded one and dropped from the shared directory if it fails, and an entry that is not a plain file of the expected size is passed over without being read at all, so a tampered copy costs a download and cannot substitute different weights, stall the read, or fill the disk. Token-goat also writes there, and treats the directory as somebody else's: it creates its temporary file exclusively rather than through whatever is already sitting at that name, and removes only what it created. Point it at a directory you control all the same, since nothing token-goat does can stop another writer filling it with entries that will simply be rejected. It is an environment variable only, so no project config file can point it anywhere. Skip the download entirely by setting `indexing.embeddings_enabled = false` (it is on by default), in which case `semantic` falls back to full-text search.
|
|
19
19
|
- The first optical-character read of an image downloads the English language data (about 4 MB) from `cdn.jsdelivr.net`, at a fixed version path. Subsequent reads use the local cache. This happens for an explicit `token-goat image-text`, and also for the automatic text extraction the image-shrink hook performs when the agent reads a screenshot; turn the automatic one off with `image_shrink.ocr_enabled = false`.
|
|
20
20
|
|
|
21
21
|
**One switch for all of it.** Set `network.offline = true` (env `TOKEN_GOAT_OFFLINE`) and every one of the paths above refuses instead of connecting, saying so rather than failing quietly. Anything already cached keeps working: a machine that has the embedding model still runs `semantic`, and one that has the language data still reads text out of images. This is one of the settings a per-project config file may not touch, so cloning a repository cannot switch it back off.
|
|
@@ -76,6 +76,8 @@ Note what that flag does and does not cover. It stops a caller traversing *out o
|
|
|
76
76
|
|
|
77
77
|
**Secret redaction in cached content.** Token-goat caches command output, fetched pages, and MCP results so it can serve them back later instead of re-running the work. Anything it writes to those caches is passed through a redactor first, so a credential that appeared in output does not sit on disk in plain text and does not get replayed into a later session. This is unconditional — there is no flag to turn it on, and it applies to cached Bash and Task output (including the command string itself, which is where an inline `--token=...` would otherwise land), fetched web content, MCP tool results and their labels, `compress-text`/`handoff` payloads, and the raw JSON disk cache. Recognized shapes: Anthropic, OpenAI, AWS, GitHub, Slack, Stripe, npm, and Google keys; JWTs; `Authorization: Bearer`/`Basic` headers; PEM private-key blocks; presigned-url signatures (AWS `X-Amz-Signature`, Google Cloud Storage `X-Goog-Signature`, Azure SAS `sig`); and generic `password=`/`secret=`/`api_key=` assignments in `.env`, connection-string, and query-string shape. A match is replaced by a `[REDACTED:<kind>]` marker naming which pattern fired.
|
|
78
78
|
|
|
79
|
+
The same redactor covers three further sinks: the per-session ledger of failed tool calls, which records the error text a failing call returned and so can carry a key an authentication error echoed back; `pr-slice`'s diff, comment bodies and description; and the output of the document-extraction commands (`pdf-outline`, `pdf-locate`, the `xlsx-*` and `pptx-*` commands, and `docx-outline`), since a spreadsheet cell or a review comment carries a credential as readily as a fetched page does. Redaction runs before any length limit is applied, so a credential sitting near a cutoff cannot survive as an unrecognized fragment.
|
|
80
|
+
|
|
79
81
|
Session state gets the same treatment. The per-session file records which urls were fetched and which `curl -o` downloads landed where, and a url carries credentials as readily as output does. The fetched-url list is redacted, so the compaction manifest can still name what was fetched without naming the key; the download list is keyed by a digest of the url instead, because its only consumer is an exact-match check and a redaction there would make two urls differing only in their key look identical. The fetched-url entry also redacts the prompt that was sent with the page, and carries a digest of the pair so redaction cannot merge two entries that differ only inside the redacted span. An entry written by an older version is rewritten into this shape the first time the file is read, so upgrading clears the credentials an old file was holding rather than keeping them for the life of the session.
|
|
80
82
|
|
|
81
83
|
Two honest limits. It is a pattern matcher, not a classifier: a credential in a format it does not recognize — an internal token shape, a bare high-entropy string with no `key=` prefix — is cached as-is. And it protects what token-goat *stores*, not what your agent reads in real time; a secret printed to the terminal was already in the model's context before any caching happened. Treat it as damage control on the cache layer, not a reason to relax about printing secrets.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "token-goat",
|
|
3
|
-
"version": "2.9.
|
|
3
|
+
"version": "2.9.11",
|
|
4
4
|
"description": "Surgical token-reduction companion for Claude Code and other AI coding agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/token-goat.mjs",
|
|
@@ -19,14 +19,18 @@
|
|
|
19
19
|
"scripts": {
|
|
20
20
|
"build": "node esbuild.config.mjs",
|
|
21
21
|
"sbom": "npm sbom --sbom-format cyclonedx --omit=dev",
|
|
22
|
+
"notices": "node scripts/generate-third-party-notices.mjs",
|
|
23
|
+
"notices:check": "node scripts/generate-third-party-notices.mjs --check",
|
|
24
|
+
"deps:refresh": "node scripts/refresh-dependabot-lock.mjs",
|
|
22
25
|
"typecheck": "tsc --noEmit",
|
|
23
26
|
"typecheck:tests": "tsc -p tsconfig.tests.json --noEmit",
|
|
24
27
|
"test": "vitest run",
|
|
28
|
+
"model:warm": "tsx scripts/warm-model-cache.ts",
|
|
25
29
|
"test:guards": "vitest run tests/guards",
|
|
26
30
|
"test:matrix": "vitest run tests/command_matrix_e2e",
|
|
27
31
|
"test:watch": "vitest",
|
|
28
32
|
"test:vscode-extension": "npm --prefix vscode-extension test",
|
|
29
|
-
"lint": "eslint src tests vscode-extension/src vscode-extension/tests",
|
|
33
|
+
"lint": "eslint src tests scripts vscode-extension/src vscode-extension/tests",
|
|
30
34
|
"typecheck:vscode-extension": "npm --prefix vscode-extension run compile",
|
|
31
35
|
"typecheck:vscode-extension:tests": "npm --prefix vscode-extension run typecheck:tests",
|
|
32
36
|
"dev": "tsx src/main.ts",
|
|
@@ -60,6 +64,7 @@
|
|
|
60
64
|
"docs/security.md",
|
|
61
65
|
"docs/C4_RUNTIME_ARCHITECTURE.md",
|
|
62
66
|
"LICENSE",
|
|
67
|
+
"THIRD_PARTY_NOTICES.md",
|
|
63
68
|
"scripts/install-git-hooks.mjs"
|
|
64
69
|
],
|
|
65
70
|
"license": "PolyForm-Noncommercial-1.0.0",
|
|
@@ -91,6 +96,7 @@
|
|
|
91
96
|
"lefthook": "^2.1.10",
|
|
92
97
|
"omggif": "^1.0.10",
|
|
93
98
|
"onnxruntime-node": "^1.27.0",
|
|
99
|
+
"sharp": "^0.35.3",
|
|
94
100
|
"smol-toml": "^1.8.0",
|
|
95
101
|
"tsx": "^4.23.12",
|
|
96
102
|
"typescript": "^6.0.3",
|
|
@@ -102,7 +108,6 @@
|
|
|
102
108
|
"fflate": "^0.8.3",
|
|
103
109
|
"pdfjs-dist": "^6.1.200",
|
|
104
110
|
"puppeteer-core": "^25.8.0",
|
|
105
|
-
"sharp": "^0.35.3",
|
|
106
111
|
"sqlite-vec": "^0.1.9",
|
|
107
112
|
"tesseract.js": "^7.0.0",
|
|
108
113
|
"tree-sitter": "^0.25.1",
|