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.
Files changed (27) hide show
  1. package/README.md +54 -11
  2. package/SECURITY.md +17 -19
  3. package/THIRD_PARTY_NOTICES.md +667 -0
  4. package/dist/token-goat-chunk-2ZUPQYLO.mjs +3728 -0
  5. package/dist/{token-goat-chunk-LQ3SGWIW.mjs → token-goat-chunk-3ZALKJ23.mjs} +69 -37
  6. package/dist/{token-goat-chunk-WBPVYAEO.mjs → token-goat-chunk-4NZUXC4F.mjs} +3 -2
  7. package/dist/{token-goat-chunk-KIEOFWLL.mjs → token-goat-chunk-AAEYU2U2.mjs} +5307 -6350
  8. package/dist/{token-goat-chunk-OBDTBOQA.mjs → token-goat-chunk-FDURVZQD.mjs} +10 -2
  9. package/dist/{token-goat-chunk-DRH4CVGF.mjs → token-goat-chunk-HDL77BN3.mjs} +11 -7
  10. package/dist/{token-goat-chunk-LYWIRYCF.mjs → token-goat-chunk-LHLQFGWQ.mjs} +1 -1
  11. package/dist/{token-goat-chunk-L6Q2XM5X.mjs → token-goat-chunk-LKOYKSID.mjs} +1275 -655
  12. package/dist/{token-goat-chunk-P5SQX2VK.mjs → token-goat-chunk-MKITQ5RY.mjs} +354 -180
  13. package/dist/{token-goat-chunk-PNMGVJ4C.mjs → token-goat-chunk-MRZ555B3.mjs} +2699 -2175
  14. package/dist/{token-goat-chunk-K5GKX6ND.mjs → token-goat-chunk-PJPFOOGM.mjs} +6 -5
  15. package/dist/{token-goat-chunk-FQD3OB5W.mjs → token-goat-chunk-QCQUIPIP.mjs} +6068 -1687
  16. package/dist/{token-goat-chunk-BYDDWQ2T.mjs → token-goat-chunk-SY7WTZMW.mjs} +5043 -7721
  17. package/dist/{token-goat-chunk-PXWBHSFB.mjs → token-goat-chunk-T6M7DAW3.mjs} +31 -4
  18. package/dist/{token-goat-chunk-QCHD2ENV.mjs → token-goat-chunk-V53Z47ZH.mjs} +85 -328
  19. package/dist/token-goat-chunk-Y5VKNLVD.mjs +5942 -0
  20. package/dist/token-goat-chunk-YCKCFYTM.mjs +3564 -0
  21. package/dist/token-goat-hook.mjs +6 -5
  22. package/dist/token-goat.core.mjs +8 -6
  23. package/docs/C4_RUNTIME_ARCHITECTURE.md +1 -1
  24. package/docs/cli.md +4 -1
  25. package/docs/install.md +131 -11
  26. package/docs/security.md +3 -1
  27. package/package.json +8 -3
package/README.md CHANGED
@@ -11,7 +11,7 @@ permalink: /
11
11
 
12
12
  ***Give the model what it needs, not everything you have.***
13
13
 
14
- **85%** smaller reads · **49%** of first-read bytes withheld · **97.4%** image compression · **180+** 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
14
+ **85%** smaller reads · **49%** of first-read bytes withheld · **97.4%** image compression · **205+** 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
15
15
 
16
16
  **Reduces AI token use/costs by 40–90%, and improves its focus. Fully automated, always online.**
17
17
 
@@ -21,7 +21,7 @@ permalink: /
21
21
 
22
22
  Token-Goat sits silently between your AI and your tools. Re-read a file? It gets a one-line hint and a narrow-slice suggestion instead of the full file again. Grab a screenshot? A 100 KB copy reaches the model instead of 10 MB. Run `pytest`, `npm install`, `docker build`, or `cargo`? The thousands of progress bars and passing-test names are stripped to the failures before the output even reaches the context window. Open a PDF, a large Markdown doc, or a CSV? The hook intercepts it — heading tree, page count, or column preview — so the model never pays for the full file. Run `gh run watch` or `next dev` a second time? Prior output is recalled rather than re-run. Compact a long session? It gets a clean structured manifest of edited files and key symbols so nothing important is forgotten. Sessions drop 40–90%+ in cost. You change nothing about how you work.
23
23
 
24
- Works with **Claude Code**, **Gemini CLI**, **Qwen Code**, **Codex CLI**, **Aider**, **Cursor**, **Cline**, **Windsurf**, **Copilot CLI**, **Kimi Code**, **Grok CLI** (xAI Grok Build), and OpenCode, plus **pi** ([pi-coding-agent](https://github.com/earendil-works/pi-mono)).
24
+ Works with **Claude Code**, **Gemini CLI**, **Qwen Code**, **Codex CLI**, **Aider**, **Cursor**, **Cline**, **Windsurf**, **Copilot CLI**, **VS Code** (Copilot agent), **Visual Studio** (Copilot agent, MCP tools and instructions only), **Zed** (MCP tools only), **Kimi Code**, **Grok CLI** (xAI Grok Build), and OpenCode, plus **pi** ([pi-coding-agent](https://github.com/earendil-works/pi-mono)).
25
25
 
26
26
  **Ask your AI to install it fully (give it this GitHub link), or install in one command:**
27
27
 
@@ -288,7 +288,7 @@ FAILED tests/test_x.py::test_one
288
288
  [token-goat: pytest filter compressed 4.8 KiB to 0.1 KiB (97% saved)]
289
289
  ```
290
290
 
291
- 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. To exclude specific generated files by basename instead (coverage reports, bundler stats), set `[indexing] skip_files = [...]`; it defaults to `["coverage.json", "coverage-final.json"]`, so a project with a legitimately-named `coverage.json` it wants indexed can override the list to drop that entry.
291
+ Built-in output compression covers 150+ 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. To exclude specific generated files by basename instead (coverage reports, bundler stats), set `[indexing] skip_files = [...]`; it defaults to `["coverage.json", "coverage-final.json"]`, so a project with a legitimately-named `coverage.json` it wants indexed can override the list to drop that entry.
292
292
 
293
293
  `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`.
294
294
 
@@ -316,7 +316,7 @@ Per-harness setup for Codex, Gemini, Qwen, Kimi, opencode, OpenClaw, pi, Copilot
316
316
 
317
317
  ## CLI
318
318
 
319
- You rarely type these yourself. Installation writes a routing guide into the agent's instructions, and the agent picks the command that answers its question: a surgical read (`read`, `symbol`, `section`, `skeleton`), a search (`semantic`, `refs`), orientation in an unfamiliar repo (`map`, `arch`, `affected`), recall of output it already has (`bash-output`, `web-output`, `mcp-output`), or a bounded reader for a PDF, Word file, slide deck, workbook, SQLite database or image.
319
+ You rarely type these yourself. Installation writes a routing guide into the agent's instructions, and the agent picks the command that answers its question: a surgical read (`read`, `symbol`, `section`, `skeleton`), a search (`semantic`, `refs`), orientation in an unfamiliar repo (`map`, `arch`, `affected`), recall of output it already has (`bash-output`, `web-output`, `mcp-output`), or a bounded reader for a PDF, Word file, slide deck, workbook, SQLite database, HTML document or image.
320
320
 
321
321
  One command is for people working on token-goat rather than for agents: `token-goat bench` replays a
322
322
  corpus of captured shell output through the real compression path and reports how much smaller the
@@ -365,13 +365,56 @@ token-goat handoff-resolve review-notes --full
365
365
  user-profile `mcp.json` by default (`%APPDATA%\Code\User\mcp.json` on
366
366
  Windows, `~/Library/Application Support/Code/User/mcp.json` on macOS,
367
367
  `~/.config/Code/User/mcp.json` on Linux) — add `-p`/`--project` for the
368
- project-local `.vscode/mcp.json` shown above instead. Either way it also adds
369
- a delimited block to `.github/copilot-instructions.md`, preserving unrelated
370
- JSON and user text. It fails clearly on malformed JSON, and refuses to
368
+ project-local `.vscode/mcp.json` shown above instead. It also adds a delimited
369
+ routing block, preserving unrelated JSON and user text: to
370
+ `~/.copilot/instructions/token-goat.instructions.md` for the user install (a
371
+ personal instructions file VS Code applies in every workspace, so the folder
372
+ you run it from is left alone), or to `.github/copilot-instructions.md` with
373
+ `-p`. It fails clearly on malformed JSON, and refuses to
371
374
  install into one scope if the other scope already has a token-goat-managed
372
375
  entry (registering it twice would duplicate its tool schemas in that
373
- workspace). `token-goat uninstall --vscode` (add `-p`/`--project` for the
374
- project scope) removes only token-goat's server entry and guidance block.
376
+ workspace). It also installs agent hooks in `~/.copilot/hooks/` (or
377
+ `.github/hooks/` with `-p`), the folder VS Code's Copilot agent reads hooks
378
+ from, so token-goat sees the agent's built-in reads, edits, and terminal
379
+ commands. `token-goat uninstall --vscode` (add `-p`/`--project` for the
380
+ project scope) removes only token-goat's server entry, guidance block, and
381
+ hooks, and keeps the hooks if `--copilot` still uses them.
382
+
383
+ **Visual Studio** (2022 17.14 or later, or 2026): `token-goat install --visualstudio`
384
+ adds the same `servers` entry to `%USERPROFILE%\.mcp.json` and a routing block
385
+ to `%USERPROFILE%\copilot-instructions.md` (Visual Studio 2026 reads that
386
+ file). With `-p`/`--project` it writes `.mcp.json` and
387
+ `.github/copilot-instructions.md` in the solution folder instead. Visual Studio
388
+ has no agent hooks, so token-goat works there through its MCP tools and
389
+ instructions only: no read dedup, hints, image shrink, or output folding. Two
390
+ switches in Visual Studio turn it on: the Tools > Options checkbox for custom
391
+ instructions, and the token-goat tools in the chat Tools picker, since new MCP
392
+ tools start disabled. `token-goat uninstall --visualstudio` (add `-p` for the
393
+ project) removes only token-goat's entry and block. See
394
+ [Visual Studio users](docs/install.md#visual-studio-users).
395
+
396
+ **Zed**: `token-goat install --zed` registers token-goat as an MCP context
397
+ server in Zed's `settings.json` (user scope only — Zed has no
398
+ project-local equivalent). Zed's first-party agent has no hooks API at all,
399
+ so this is MCP tools only, the same limits as Visual Studio above: no read
400
+ dedup, hints, image shrink, or output folding. Enable the token-goat server
401
+ under Zed's Agent panel tools list after installing; new MCP servers start
402
+ disabled. `token-goat uninstall --zed` removes the entry and its generated
403
+ shim script. See [Zed users](docs/install.md#zed-users).
404
+
405
+ **Cursor**: `token-goat install --cursor` registers token-goat as an MCP
406
+ server in `~/.cursor/mcp.json` (`-p`/`--project` for
407
+ `<project>/.cursor/mcp.json`). This never touches `~/.cursor/hooks.json`:
408
+ Cursor already imports Claude Code's hooks from `~/.claude/settings.json` by
409
+ default, so a plain `token-goat install` for Claude Code already gets hooks
410
+ running in Cursor too, with no separate Cursor hooks file to maintain or risk
411
+ clobbering. `token-goat uninstall --cursor` removes only the MCP entry. See
412
+ [Cursor users](docs/install.md#cursor-users).
413
+
414
+ **JetBrains** (WebStorm, IntelliJ, PyCharm, Rider, PhpStorm): no integration
415
+ today — no hooks, no MCP, no terminal filter. See
416
+ [JetBrains IDEs users](docs/install.md#jetbrains-ides-webstorm-intellij-pycharm-rider-phpstorm-users)
417
+ for why and what a Copilot-for-JetBrains user might already get for free.
375
418
 
376
419
  The optional source-controlled extension lives in `vscode-extension/`. Build
377
420
  and install its VSIX manually; `--vscode` intentionally does not copy or
@@ -417,7 +460,7 @@ never drift on where `mcp.json` lives or what key name it looks for.
417
460
  }
418
461
  ```
419
462
 
420
- **Caveat.** Registering the server does not force any harness to prefer it. Unlike the hook-based bridges elsewhere in this project — which intercept a `Read`/`Grep`/`Glob` call before it reaches the model and can redirect or deny it outright — an MCP tool is just one more option in the harness's own tool-selection decision. Copilot (or any other MCP-aware client) decides for itself whether to call token-goat's `read` tool or fall back to its own built-in file-read tool; there is no interception mechanism for MCP the way there is for hooks.
463
+ **Caveat.** Registering the server does not force any harness to prefer it. Unlike the hook-based bridges elsewhere in this project — which intercept a `Read`/`Grep`/`Glob` call before it reaches the model and can redirect or deny it outright — an MCP tool is just one more option in the harness's own tool-selection decision. Copilot (or any other MCP-aware client) decides for itself whether to call token-goat's `read` tool or fall back to its own built-in file-read tool; there is no interception mechanism for MCP the way there is for hooks. In VS Code the agent hooks from `install --vscode` do see the built-in reads: they can deny a repeated read, add a hint, and shrink an image before `view_image` loads it. VS Code gives hooks no way to change a tool's result, though, so there is no folding of what a read returns and no compression of a tool's output after it runs. Terminal output is not compressed in VS Code either: elsewhere token-goat does that by rewriting the command before it runs, but VS Code does not tell the hook which shell will run the command, so rewriting it safely is not possible.
421
464
 
422
465
  ## What gets installed?
423
466
 
@@ -664,7 +707,7 @@ Token-Goat is licensed under the PolyForm Noncommercial License 1.0.0. See the L
664
707
 
665
708
  Individual developers may install and use Token-Goat on their own machines for personal productivity without a commercial license, provided the use does not involve providing Token-Goat as a service to others, incorporating it into a commercial product or platform, or deploying it as shared infrastructure across a team or organization. Employment at a for-profit company does not by itself make use commercial — but if your employer is the primary beneficiary of the deployment, a commercial license applies. When in doubt, email token-goat@dfkhelper.com.
666
709
 
667
- Commercial use is reserved. That means copying or incorporating this codebase into a product, charging for access to it, or running it as shared infrastructure across a team at a for-profit company. Commercial licensing: token-goat@dfkhelper.com.
710
+ Commercial use is reserved. That means copying or incorporating this codebase into a product, charging for access to it, or running it as shared infrastructure across a team at a for-profit company. Commercial licenses have been issued and are agreed per organization by email; there is no price list or self-serve purchase. Commercial licensing: token-goat@dfkhelper.com.
668
711
 
669
712
  Copyright (c) 2026 DFK Helper LLC.
670
713
 
package/SECURITY.md CHANGED
@@ -41,13 +41,13 @@ The following are not treated as security issues unless paired with a working pr
41
41
 
42
42
  ## Dependency advisories
43
43
 
44
- `npm audit` reports Token-Goat clean whichever way you scan it, and that has only been true of a default install since the embedding model became opt-in. All three numbers below are reproducible with the commands shown. The one thing that is not clean is the opt-in package itself, and it is described further down rather than folded into these rows.
44
+ `npm audit` reports Token-Goat clean for anyone who installs it, and that has only been true of a default install since the embedding model became opt-in. This repository's own tree, development dependencies included, is not: two moderate advisories currently sit in dev-only transitive dependencies (`adm-zip` under `onnxruntime-node`, `hono` under `@modelcontextprotocol/sdk`), both behind Dependabot's cooldown rather than bumped ad hoc -- see [`.github/dependabot.yml`](.github/dependabot.yml) for why a plain `npm update` isn't the fix. Neither ships to an installer: both packages drop out the moment `--omit=dev` is added, which is what the next two rows scan. All three numbers below are reproducible with the commands shown. The one thing that is not clean in a default install is the opt-in package itself, and it is described further down rather than folded into these rows.
45
45
 
46
46
  | What you scan | Command | Result |
47
47
  | --- | --- | --- |
48
- | this repository | `npm audit` | clean, development dependencies included |
48
+ | this repository | `npm audit` | 2 moderate, development dependencies only (`adm-zip`, `hono`; see above) |
49
49
  | an install without optional packages | `npm install --omit=optional token-goat` then `npm audit --omit=dev --omit=optional` | clean, 2 packages |
50
- | a default install | `npm install token-goat` then `npm audit --omit=dev` | clean, 70 packages |
50
+ | a default install | `npm install token-goat` then `npm audit --omit=dev` | clean, 62 packages |
51
51
 
52
52
  The repository carries an `overrides` block in [`package.json`](package.json) that pins six packages to patched versions, and **npm applies `overrides` only in the root project**, so those pins do not travel to anyone who installs Token-Goat as a dependency. That distinction used to matter a great deal, because a clean repository scan was standing in for an install that was not clean. It decides one thing now, and the section on the embedding runtime below says which. Otherwise the packages those pins were protecting against are not in a consumer's tree at all, whether or not the pins travel; the block stays because the development tree still resolves them.
53
53
 
@@ -61,7 +61,7 @@ The embedding runtime is opt-in, the same way the model was. `semantic` still wo
61
61
  npm install -g onnxruntime-node # drop -g if token-goat is a project dependency
62
62
  ```
63
63
 
64
- **That command is the one thing on this page that is not clean, and the number is two.** `onnxruntime-node` depends on [`adm-zip`](https://www.npmjs.com/package/adm-zip) below 0.6.0, which carries [GHSA-xcpc-8h2w-3j85](https://github.com/advisories/GHSA-xcpc-8h2w-3j85) -- a crafted ZIP file provoking a 4 GB allocation -- and npm reports it twice, once against `adm-zip` and once against `onnxruntime-node` for depending on it. Both are the same finding. It is reachable only from `onnxruntime-node`'s own postinstall script, which uses it to unpack the prebuilt binary it has just downloaded from Microsoft; nothing in Token-Goat passes it an archive, and no file a user supplies reaches it. This repository pins `adm-zip` past it in `overrides`, which is why the repository row above is clean, and by the rule stated above that pin does not travel: run the command and you will resolve the 0.5 line and see those two. If that matters where you are, the fix is an `overrides` entry of your own:
64
+ **That command is the one thing on this page that is not clean, and the number is two.** `onnxruntime-node` depends on [`adm-zip`](https://www.npmjs.com/package/adm-zip) below 0.6.0, which carries [GHSA-xcpc-8h2w-3j85](https://github.com/advisories/GHSA-xcpc-8h2w-3j85) -- a crafted ZIP file provoking a 4 GB allocation -- and npm reports it twice, once against `adm-zip` and once against `onnxruntime-node` for depending on it. Both are the same finding. It is reachable only from `onnxruntime-node`'s own postinstall script, which uses it to unpack the prebuilt binary it has just downloaded from Microsoft; nothing in Token-Goat passes it an archive, and no file a user supplies reaches it. This repository pins `adm-zip` past it in `overrides`, which used to be why the repository row above was clean. It no longer is: a second, unrelated `adm-zip` advisory ([GHSA-vwc7-r8mq-g2x9](https://github.com/advisories/GHSA-vwc7-r8mq-g2x9), extraction following a destination symlink) reaches into the patched version itself, so the `^0.6.0` override no longer clears everything `adm-zip` carries -- that is the `adm-zip` finding the repository row above now discloses, alongside `hono`'s. By the rule stated above the override does not travel regardless: run the command and you will resolve the 0.5 line and see the original ZIP-bomb finding twice. If that matters where you are, the fix is an `overrides` entry of your own:
65
65
 
66
66
  ```json
67
67
  { "overrides": { "adm-zip": "^0.6.0" } }
@@ -73,7 +73,7 @@ Nothing else is needed. The index notices on its own: files skipped while the mo
73
73
 
74
74
  The model weights are no longer a package at all. They are downloaded once, on first use, from a URL whose every component is a constant in [`src/embed_model.ts`](src/embed_model.ts) -- repository, revision and filename alike, with nothing caller-supplied anywhere in it. The revision is an immutable commit rather than a branch, and each file is checked against a recorded SHA-256 and an exact byte length, on download and again on every load, with the length enforced while the body is still streaming so an overrunning response is cut off rather than written out. The digest is what is trusted, not the hostname: `huggingface.co` accepts uploads from anyone, which is the shape of [CVE-2026-54316](https://github.com/advisories/GHSA-fg94-h982-f3mm), so allowlisting the host would decide nothing. Because the digest decides, following the redirect that `resolve` issues to Hugging Face's CDN is safe. With `network.offline` set, the download is refused and the path says so rather than degrading quietly; files copied into the cache directory by hand are still accepted, because they are checked the same way.
75
75
 
76
- The old model had cost something before any of this, through the nested `sharp` it carried. That `sharp` ships its own libvips binaries, and loading the model eagerly put them ahead of Token-Goat's own `sharp` in the Windows DLL search order, which broke image shrinking with `ERR_DLOPEN_FAILED` while `sharp` loaded perfectly well on its own. `onnxruntime-node` carries no `sharp` and no libvips, so that particular collision cannot recur -- but the load is still deferred until something actually asks to embed, in [`src/embed_model.ts`](src/embed_model.ts), because it is a native addon whose DLLs have no business being loaded into every hook invocation that never embeds anything.
76
+ The old model had cost something before any of this, through the nested `sharp` it carried. That `sharp` ships its own libvips binaries, and loading the model eagerly put them ahead of the `sharp` Token-Goat installed for itself at the time in the Windows DLL search order, which broke image shrinking with `ERR_DLOPEN_FAILED` while `sharp` loaded perfectly well on its own. Neither package reaches an install today: image shrinking is pure TypeScript in the bundle, and `sharp` is a development dependency. `onnxruntime-node` carries no `sharp` and no libvips, so that particular collision cannot recur -- but the load is still deferred until something actually asks to embed, in [`src/embed_model.ts`](src/embed_model.ts), because it is a native addon whose DLLs have no business being loaded into every hook invocation that never embeds anything.
77
77
 
78
78
  `npm install --omit=optional` remains available and gives a smaller install still, at 2 packages -- Token-Goat and [`jsonc-parser`](https://www.npmjs.com/package/jsonc-parser), and nothing else. Every command starts either way; the ones that need a package you skipped say so. The `xlsx-*`, `docx-*` and `pptx-*` commands report that fflate is not installed rather than failing oddly, and `zip-list`/`zip-read` do the same.
79
79
 
@@ -87,7 +87,7 @@ The old model had cost something before any of this, through the nested `sharp`
87
87
 
88
88
  `better-sqlite3` stays as a development dependency, and like the MCP SDK that is load-bearing. Three parts of the driver are reimplementations rather than passthroughs, and a test written only against the driver would pin whatever the driver does, bugs included. So [`tests/sqlite_driver.test.ts`](tests/sqlite_driver.test.ts) runs both libraries over the same input and requires the answers to match -- most carefully for the `reader` flag, which is the third defence-in-depth layer in the `sqlite-query` read-only guard and the one thing the driver derives rather than reads. The two libraries differ in exactly one place, deliberately: asked for an integer above 2^53 without big-integer reads switched on, `better-sqlite3` returns the nearest double and `node:sqlite` refuses. Silently rounding is the behaviour `sqlite-query` already had to defend against, so the refusal is kept, and that test pins the difference rather than papering over it. That takes a default install from 106 packages to 70, and an install without optional packages from 40 to 2.
89
89
 
90
- Every package count on this page is a measurement, not a constant, and it was taken the same way each time: `npm install <the package>` into an empty project, then counting the directories under `node_modules`, the package itself included. They are counts as of 2026-08-22 and they drift upward on their own, because the version ranges a dependency declares resolve to whatever is newest at install time and other people's trees grow. This paragraph said 238 and 87 for a while for exactly that reason: both were true when they were written and neither was true a few releases later. Re-measure before quoting them rather than assuming they still hold. The default figure additionally depends on where you stand: `sharp`, `@napi-rs/canvas` and `sqlite-vec` each publish a prebuilt binary per platform, an install takes only the one that matches, and the rest are skipped, so a default install is a few packages larger on Linux than on Windows or macOS. The 70 above was measured on Windows x64, and it is the largest of the three. The no-optional install has no prebuilt binary in it at all, so 2 is 2 everywhere.
90
+ Every package count on this page is a measurement, not a constant, and it was taken the same way each time: `npm install <the package>` into an empty project, then counting the directories under `node_modules`, the package itself included. They are counts as of 2026-09-11 and they drift upward on their own, because the version ranges a dependency declares resolve to whatever is newest at install time and other people's trees grow. This paragraph said 238 and 87 for a while for exactly that reason: both were true when they were written and neither was true a few releases later. Re-measure before quoting them rather than assuming they still hold. The default figure additionally depends on where you stand: `@napi-rs/canvas` and `sqlite-vec` each publish a prebuilt binary per platform, an install takes only the one that matches, and the rest are skipped, so a default install is a package larger on Linux than on Windows or macOS. The 62 above is the Linux x64 figure, which is the largest of the three; Windows and macOS are 61. The no-optional install has no prebuilt binary in it at all, so 2 is 2 everywhere.
91
91
 
92
92
  One half of that drift is checked rather than trusted. [`tests/guards/dependency_advisory_disclosure.test.ts`](tests/guards/dependency_advisory_disclosure.test.ts) resolves both installs out of [`package-lock.json`](package-lock.json) the way npm resolves them, platform gating included, and holds this page to the answer: the no-optional figure has to match exactly, and the default figure may not be smaller than the lock file already proves it must be. That catches every package this project adds or removes on its own. It cannot catch the other half, which is other people's trees growing inside ranges an install already accepts, and that is what the date is for.
93
93
 
@@ -95,7 +95,7 @@ One half of that drift is checked rather than trusted. [`tests/guards/dependency
95
95
 
96
96
  Five more packages were in `dependencies` for the same reason and have moved the same way: `commander`, `csv-parse`, `js-yaml`, `smol-toml` and `zod`. esbuild inlines each one into the bundle, and the published bundle resolves none of them, so a consumer was downloading code the artifact already carried. Moving them takes an install without optional packages from 46 packages to 40. `jsonc-parser` stays, because the bundle really does load it at run time: it is reached through `createRequire` rather than an import esbuild can inline. `zod` is the largest of the five on disk, and when that move was made it saved least in practice, because `@modelcontextprotocol/sdk` and `puppeteer-core` both depended on it and a default install still got it from them. Neither is true any more: the puppeteer-core major bump dropped its `zod` dependency, and the MCP SDK is a development dependency now for the reason below. A default install carries no copy of `zod` at all.
97
97
 
98
- Direct dependencies with a forward patch are kept current rather than pinned: `sharp` and `puppeteer-core` were both moved across a major version to clear their advisories.
98
+ Direct dependencies with a forward patch are kept current rather than pinned: `puppeteer-core` was moved across a major version to clear its advisories, and `sharp` was moved the same way while it was still one. `sharp` is a development dependency now, which is the larger change: nothing under `src/` imports it, the image pipeline is pure TypeScript that esbuild inlines into the bundle, and so a default install no longer downloads it or the libvips binaries behind it. The tests still use it, as an independent encoder to check the bundled one against, so it is kept current there for the repository's own `npm audit`.
99
99
 
100
100
  For a scanner that ingests a bill of materials rather than a lockfile, `npm run sbom` writes CycloneDX 1.5 to stdout.
101
101
 
@@ -122,9 +122,9 @@ Token-Goat is source-available under the PolyForm Noncommercial License 1.0.0. S
122
122
 
123
123
  Every production dependency is permissively licensed, but a scan does not read it that way on its
124
124
  own. Counted from `package-lock.json`, which lists the packages for every platform rather than only
125
- the ones this machine installed, 21 entries need a human answer: 7 declare a license a scanner
126
- cannot resolve, and 14 carry a copyleft term. All 21 arrive through optional dependencies. Install
127
- with `npm install --omit=optional token-goat` and not one of them is present.
125
+ the ones this machine installed, 6 entries need a human answer: all 6 declare a license a scanner
126
+ cannot resolve, and none carries a copyleft term. All 6 arrive through optional dependencies.
127
+ Install with `npm install --omit=optional token-goat` and not one of them is present.
128
128
 
129
129
  **Declarations a scanner cannot resolve.** One remains, and it is an upstream mistake of the same
130
130
  kind this project made in its own manifest and fixed: `MIT OR Apache` is not a valid expression,
@@ -136,15 +136,13 @@ while actually granting Apache-2.0. It arrived through `@xenova/transformers` an
136
136
  | --- | --- | --- | --- |
137
137
  | `sqlite-vec` and its 5 platform packages | `MIT OR Apache` | MIT or Apache-2.0, your choice | direct optional dependency |
138
138
 
139
- **Copyleft terms.** Two families, and neither puts a copyleft obligation on Token-Goat's own code.
140
-
141
- | Package | Declares | Why it is not a problem |
142
- | --- | --- | --- |
143
- | `@img/sharp-libvips-<platform>` (10 packages) | `LGPL-3.0-or-later` | libvips, shipped as a prebuilt shared library and used unmodified. LGPL asks that the library stay replaceable, and it is: it is a separate package that `sharp` loads at runtime. |
144
- | `@img/sharp-<platform>` (4 packages) | `Apache-2.0 AND LGPL-3.0-or-later` | the Apache half is `sharp` itself, the LGPL half is the same libvips |
145
-
146
- `sharp` is optional: it powers image shrinking. `jszip` used to be listed here too; it arrived
147
- through `exceljs`, which is no longer a dependency a consumer installs.
139
+ **Copyleft terms.** None reach an install. Two families used to be here, and both left at once:
140
+ `@img/sharp-libvips-<platform>` (10 packages, `LGPL-3.0-or-later`, the libvips shared library) and
141
+ `@img/sharp-<platform>` (4 packages, `Apache-2.0 AND LGPL-3.0-or-later`, `sharp` itself over that
142
+ same libvips). They arrived through `sharp`, which is a development dependency now: nothing under
143
+ `src/` imports it, image shrinking is pure TypeScript inlined into the bundle, and so no libvips
144
+ is downloaded by an install at all. `jszip` used to be listed here too; it arrived through
145
+ `exceljs`, which is no longer a dependency a consumer installs.
148
146
 
149
147
  **Three packages with no license at all used to be here.** `buffers@0.1.1` and `chainsaw@0.1.0`
150
148
  shipped with neither a `license` field nor a license file, and `traverse@0.3.9` had the file but