token-goat 2.9.3 → 2.9.5

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.
@@ -2,11 +2,11 @@ import { createRequire as __cjsRequire } from 'node:module';
2
2
  const require = __cjsRequire(import.meta.url);
3
3
  import {
4
4
  relayInProcess
5
- } from "./token-goat-chunk-2VVXTAGD.mjs";
6
- import "./token-goat-chunk-A4VBNWSL.mjs";
7
- import "./token-goat-chunk-3XVGPLDS.mjs";
8
- import "./token-goat-chunk-OKJX2JWW.mjs";
9
- import "./token-goat-chunk-KKHKXUVS.mjs";
5
+ } from "./token-goat-chunk-ZLP6TCGN.mjs";
6
+ import "./token-goat-chunk-JO5JX72D.mjs";
7
+ import "./token-goat-chunk-U4FTM2SB.mjs";
8
+ import "./token-goat-chunk-HTJP6FHK.mjs";
9
+ import "./token-goat-chunk-ZOKNDG6V.mjs";
10
10
  import "./token-goat-chunk-EEIDFMEM.mjs";
11
11
  import {
12
12
  init_define_import_meta_env
@@ -2,13 +2,13 @@ import { createRequire as __cjsRequire } from 'node:module';
2
2
  const require = __cjsRequire(import.meta.url);
3
3
  import {
4
4
  run
5
- } from "./token-goat-chunk-6FQMEMWX.mjs";
6
- import "./token-goat-chunk-7A7SBE6R.mjs";
7
- import "./token-goat-chunk-A4VBNWSL.mjs";
8
- import "./token-goat-chunk-3XVGPLDS.mjs";
5
+ } from "./token-goat-chunk-5T2K7DEE.mjs";
6
+ import "./token-goat-chunk-4P6GTCMM.mjs";
7
+ import "./token-goat-chunk-JO5JX72D.mjs";
8
+ import "./token-goat-chunk-U4FTM2SB.mjs";
9
9
  import {
10
10
  installEpipeGuard
11
- } from "./token-goat-chunk-KKHKXUVS.mjs";
11
+ } from "./token-goat-chunk-ZOKNDG6V.mjs";
12
12
  import "./token-goat-chunk-EEIDFMEM.mjs";
13
13
  import {
14
14
  init_define_import_meta_env
package/docs/cli.md CHANGED
@@ -167,10 +167,10 @@ token-goat pdf-extract manual.pdf --pages 12-15 --layout --head 120
167
167
  | `token-goat project exclude <path>` | Add a project root to the blocklist so the worker never indexes it. Writes the resolved absolute path to `[worker] blocked_roots` in `config.toml`; idempotent. It also removes anything already indexed under that path and says how many files went, so excluding a directory means its contents stop being readable through `symbol` rather than merely stopping future indexing. Remove the entry from the config to re-enable indexing, then run `token-goat index` to bring the contents back. |
168
168
  | `token-goat project prune [--dry-run]` | Remove blocked/excluded roots that no longer exist on disk. `--dry-run` previews removals without touching the config file. Useful after deleting or moving projects. |
169
169
  | `token-goat install` | Wire up hooks (and, with the harness flags below, other AI tool integrations). No `--dry-run` or `--verify` flag — run `token-goat doctor` after install to audit the result. |
170
- | `token-goat doctor` | Confirm everything is wired correctly. Surfaces install state, cold-import timing, cache hit rates, compaction-budget telemetry, opt-in flag status, and canonical-root sanity. A **Tool names** check reports any tool name a harness sent that reached no handler wanting it, and calls out the ones that differ from a handled name only by capitalisation or punctuation — the signature of a bridge that forgot to rename something, which is otherwise invisible. A **Security** section reports the posture in one place: whether offline mode is on, whether injection scanning is on, whether the Google Drive integration is enabled, whether fetching runs against an allow list or a deny list, whether MCP reads are confined to the project root, and whether the data directory is readable by other local users. It only warns when a protection that ships on has been switched off, so a default install stays quiet. It read-only audits `~/.copilot/mcp-config.json` for globally configured Chrome DevTools or Playwright `npx` launchers, recommending project scope or removal when inactive; it never prints server configuration or secrets. On Windows, it also reports duplicate Chrome DevTools/Playwright MCP launchers and orphaned Node processes without terminating anything. Pass `--context` to show the **Context footprint** section: a fill bar with severity (ok / warn / high / URGENT), per-component breakdown (skills catalog, loaded skill bodies, CLAUDE.md+MEMORY.md, conversation estimate), session-to-session growth trend with sessions-to-URGENT projection, and tiered compaction recommendations (Tier 0–4) naming the exact commands to run. Auto-shown when fill > 40 % or any loaded skill > 2 K tokens lacks a compact. `--json` emits the check results (one entry per check, with `ok`/`warn`/`fail` status) as JSON instead of text. |
170
+ | `token-goat doctor` | Confirm everything is wired correctly. Surfaces install state, cold-import timing, cache hit rates, compaction-budget telemetry, opt-in flag status, and canonical-root sanity. A **Parser freshness** check reports how much of the index for this project was written by a different build of the extractor. The stamp only refreshes when something touches a file, so after an upgrade a project goes on answering `symbol`, `read`, `outline` and `skeleton` from the previous extractor with nothing saying so. It warns past a quarter and names the fix: a plain `token-goat index` in that project, which is enough on its own, since a parser mismatch reparses without `--force`. A **Tool names** check reports any tool name a harness sent that reached no handler wanting it, and calls out the ones that differ from a handled name only by capitalisation or punctuation — the signature of a bridge that forgot to rename something, which is otherwise invisible. A **Security** section reports the posture in one place: whether offline mode is on, whether injection scanning is on, whether the Google Drive integration is enabled, whether fetching runs against an allow list or a deny list, whether MCP reads are confined to the project root, and whether the data directory is readable by other local users. It only warns when a protection that ships on has been switched off, so a default install stays quiet. It read-only audits `~/.copilot/mcp-config.json` for globally configured Chrome DevTools or Playwright `npx` launchers, recommending project scope or removal when inactive; it never prints server configuration or secrets. On Windows, it also reports duplicate Chrome DevTools/Playwright MCP launchers and orphaned Node processes without terminating anything. Pass `--context` to show the **Context footprint** section: a fill bar with severity (ok / warn / high / URGENT), per-component breakdown (skills catalog, loaded skill bodies, CLAUDE.md+MEMORY.md, conversation estimate), session-to-session growth trend with sessions-to-URGENT projection, and tiered compaction recommendations (Tier 0–4) naming the exact commands to run. Auto-shown when fill > 40 % or any loaded skill > 2 K tokens lacks a compact. `--json` emits the check results (one entry per check, with `ok`/`warn`/`fail` status) as JSON instead of text. |
171
171
  | `token-goat capabilities` | List every capability that can send data off this machine or leave data on it, with whether it is currently on, the config key that decides that, and the exact `file::symbol` where the decision is made — so a reviewer can open the code rather than take the list's word for it. `--json` emits the same thing for a pipeline to assert on, which is the point: the answer comes from the binary installed on your machine, not from documentation that may describe a different build. A test in the suite fails the build when a module that can open a network connection is missing from this list, and equally when the list names one that no longer connects anywhere. |
172
172
  | `token-goat baseline` | Emit a project map: file count, per-language file counts, the top indexed symbols (by name/kind/location), and the most recently modified files. `--subagent` emits a terser variant (fewer symbols, fewer recent files) for context handed to a freshly spawned subagent; `--json` for the machine-readable form. |
173
- | `token-goat compact-doc <path>` | Build an extractive compact sidecar for a large reference doc (`.md`/`.markdown`). The compact is stored in the token-goat data dir as a SHA-keyed sidecar; `pre_read` serves it in place of the full file when it exists and is fresh, saving 80–95% of context tokens. Use `--force` to rebuild, `--sentences N` to control lines per section (default 2), `--show` to print the result. The sidecar is automatically marked stale when you edit the source file. Config: `[hints] stable_doc_compacts = true` (default on). |
173
+ | `token-goat compact-doc <path>` | Build an extractive compact sidecar for a large reference doc (`.md`/`.markdown`). The compact is stored in the token-goat data dir as a SHA-keyed sidecar; `pre_read` serves it in place of the full file when it exists and is fresh, typically saving 60–95% of the tokens the full read would cost (measured across this repo's own 44 docs: median 67%, range 5–99%, depending on how much of the doc is prose under headings). Use `--force` to rebuild, `--sentences N` to control lines per section (default 2), `--show` to print the result. The sidecar is automatically marked stale when you edit the source file. Config: `[hints] stable_doc_compacts = true` (default on). |
174
174
 
175
175
  Missed lookups recover surgically: `read` and `section` print a "Did you mean…?" list on a miss, and `section` auto-redirects on an unambiguous heading-prefix match — a typo costs at most one extra glance, not a re-read.
176
176
 
@@ -302,12 +302,12 @@ that category — the real cost of emitting it, not just how often it fired):
302
302
 
303
303
  ```
304
304
  $ token-goat hint-stats
305
- category emitted acted-on efficacy suppressed manual+ manual- spent
306
- bash_redirect 42 9 21.4% no 0 0 3150
307
- bash_recall 18 15 83.3% no 0 0 1080
308
- read_reread_dedup 11 2 18.2% no 0 0 660
309
- read_structural_nav 7 1 14.3% yes 0 1 420
310
- edit_reread_suggest 3 0 0% no 0 0 180
305
+ category emitted acted-on efficacy suppressed manual+ manual- spent
306
+ bash_redirect 42 9 21.4% no 0 0 3150
307
+ bash_recall 18 15 83.3% no 0 0 1080
308
+ read_reread_dedup 11 2 18.2% * no 0 0 660
309
+ read_structural_nav 7 1 14.3% yes 0 1 420
310
+ edit_reread_suggest 3 0 0% * no 0 0 180
311
311
 
312
312
  TOTAL saved=48200 spent=5490 net=42710
313
313
  ```
@@ -325,6 +325,13 @@ emissions (default 5) AND its efficacy falls below `hint_stats.suppress_threshol
325
325
  emission. Once suppressed, that hook stops emitting that category until `token-goat hint-stats
326
326
  --reset` clears the tracked data. Configure both knobs with `token-goat config set hint_stats.min_sample_size <n>` / `token-goat config set hint_stats.suppress_threshold_pct <pct>`.
327
327
 
328
+ A `*` on an efficacy figure means that category is scored on an absence. Those hints ask the
329
+ agent *not* to do something, so a window that expires with no re-read counts as compliance, while
330
+ an unmarked category only scores when the agent actually runs the command the hint named. The two
331
+ percentages sit on different scales and comparing them straight across gives the wrong answer: a
332
+ high starred figure means the warned-against read was not seen, not that the hint persuaded
333
+ anyone.
334
+
328
335
  "Acted on" is a real, session-scoped signal (the exact file path or cached-output id the hint's
329
336
  own text pointed at is checked against the next few tool calls in that session) — not a guess —
330
337
  but it is a proxy for correlation, not proof of causation: a match means the agent ran the
package/docs/security.md CHANGED
@@ -44,6 +44,10 @@ The line that decides is substitution: wherever token-goat replaces a tool resul
44
44
 
45
45
  In every one of those cases the fence wraps the third-party bytes and stops there. Token-goat's own notice, the filter's marker, and the pointer telling the model how to recall the full output all sit outside the closing tag, because the fence is the model's one signal for where token-goat stops speaking. Fold them inside and that signal is gone, and anyone who guesses the marker's wording can write a line the model reads as token-goat's own.
46
46
 
47
+ Guessing the wording is not hard, so the fence does not rely on it staying secret: inside the fence, a line shaped like either voice token-goat speaks in has its opening bracket escaped. That covers `[token-goat: ...]`, the marker hooks sign a rewrite with, and `[tg]`, the prefix on every denial, which is the one that matters more because a denial is the only message token-goat sends that is shaped as an instruction to obey. The escape is narrow enough to leave ordinary bracketed words alone. Fenced file content is also placed below token-goat's own notice rather than above it, so no byte of a file can arrive ahead of token-goat speaking and be read as its preamble.
48
+
49
+ `token-goat ask` is fenced for a sharper reason than the rest. It retrieves indexed symbol bodies and pipes them to whatever `TOKEN_GOAT_ASK_BACKEND` names, normally `claude` or `codex`, so it is the one place where third-party text reaches a model that holds tools instead of a model reading a tool result. The snippets are redacted and then fenced before they enter the prompt; the question, which is yours, stays outside the fence.
50
+
47
51
  Read is the exception: file content passes through to the model unfiltered, because filtering it would silently break legitimate use cases. Where token-goat splices a piece of a file into its own hint or denial message, that excerpt is fenced. Outside of the fence, the primary defense is the model's own training to treat tool output as data, not as commands from a trusted party.
48
52
 
49
53
  Bash output that token-goat does not rewrite is the same kind of exception, and worth stating plainly rather than leaving to be inferred from the paragraph above. A command whose output is short, or too incompressible to be worth touching, reaches the model exactly as the harness delivered it, with no fence. So does output whose only change was stripping the colour codes a terminal would have rendered, since that path emits the command's own bytes and adds nothing of token-goat's to delimit. Fencing those cases would mean rewriting the result of every shell command an agent runs, a permanent cost on the most-used path in the tool, to re-label bytes the model was going to receive in that form anyway. The fence is worth its bytes where token-goat has substituted its own account of the output and the model can no longer tell whose words are whose. Where token-goat has stayed out of the way, the defense is the same as for Read: the model's training to treat tool output as data.
@@ -56,13 +60,13 @@ A third case needs no fence, because the danger is the line break rather than th
56
60
 
57
61
  The MCP tools (`symbol` when given a `file` filter, `read`, `section`, `skeleton`, `outline`, `refs`, `brief`, `grep`, `imports`, `exports`) are confined to the project root, resolving symlinks before the check. Set `mcp.confine_reads_to_project_root = false` (env `TOKEN_GOAT_MCP_CONFINE_READS`) in your global config if you genuinely need cross-root reads from an MCP client; a per-project file cannot set it. The CLI is deliberately unconfined and unchanged. This is defense in depth for one sink, not a sandbox: an agent that can call these tools can usually call its own read tool too.
58
62
 
59
- Note what that flag does and does not cover. It stops a caller traversing *out of* the root it is given; it does not constrain *which* root the caller supplies. Every MCP tool takes an optional `projectRoot`, and it exists for a reason — the server's cwd is often not the workspace root for MCP clients — but tool arguments are model-generated, so that choice is untrusted input like any other. If your deployment treats MCP as the only path to the filesystem, set `mcp.allowed_roots` (env `TOKEN_GOAT_MCP_ALLOWED_ROOTS`, delimiter-separated like `PATH`) to the roots that may legitimately be named; a resolved root outside every entry is then refused. It is empty by default, which keeps the multi-root behavior above unchanged.
63
+ Note what that flag does and does not cover. It stops a caller traversing *out of* the root it is given; it does not constrain *which* root the caller supplies. Every MCP tool takes an optional `projectRoot`, and it exists for a reason — the server's cwd is often not the workspace root for MCP clients — but tool arguments are model-generated, so that choice is untrusted input like any other. If your deployment treats MCP as the only path to the filesystem, set `mcp.allowed_roots` (env `TOKEN_GOAT_MCP_ALLOWED_ROOTS`, delimiter-separated like `PATH`) to the roots that may legitimately be named; a resolved root outside every entry is then refused. It is empty by default, which keeps the multi-root behavior above unchanged. Because empty means "any root may be named", `token-goat doctor` says that outright on its `Security mcp roots` line rather than reporting the confinement flag alone, which on its own reads as a stronger guarantee than it is.
60
64
 
61
65
  **Restricting what token-goat may fetch.** `webfetch.allow` and `webfetch.deny` (env `TOKEN_GOAT_WEBFETCH_ALLOW` / `TOKEN_GOAT_WEBFETCH_DENY`, comma-separated) are wildcard URL patterns that decide which addresses may be reached. Deny is checked first and wins; a non-empty allow list refuses anything it does not name. Patterns are matched against the address as it will actually be sent, not only as you typed it, so a trailing dot on the host, `..` path segments, a default port written out, and percent-encoded path characters cannot be used to step around a rule. Writing a default port in a pattern (`https://example.com:443/*`) and omitting it are equivalent. Both are empty by default, which permits everything, exactly as before. They apply to the WebFetch call your AI makes, to the fetches token-goat performs itself (`fetch-image`, `gdrive-sections`), and to the headless browser behind `screenshot` (whose page sub-resources are checked too), including every redirect hop, so an allowed site cannot redirect the request on to a denied one.
62
66
 
63
67
  **Where cached content lives, and who can read it.** Cached command output, fetched pages, MCP results, session state and the source index all sit under one data directory (`~/.local/share/token-goat` on Linux, `~/Library/Application Support/token-goat` on macOS, `%LOCALAPPDATA%\dfk-helper\token-goat` on Windows). On POSIX that directory is created owner-only (mode 0700), and an existing one is tightened on the next run, so other local users on a shared build host cannot read it. Windows uses inherited ACLs instead. Individual JSON blobs are additionally written 0600.
64
68
 
65
- **Confining `symbol` to one project.** `token-goat symbol` is the one read command that answers from the machine-wide index (`global.db`) rather than the current project, so by default `symbol <name>` and `symbol --grep .` return matching symbols, bodies included, from every project ever indexed on the host. That is deliberate and useful on a personal machine: it is how you find a helper you wrote in another repo. On a shared build host, or under an agent you have confined to one directory, it is a read channel that the directory sandbox does not close, because the answer comes out of the index instead of the filesystem. Set `indexing.cross_project_symbols = false` (env `TOKEN_GOAT_CROSS_PROJECT_SYMBOLS`) and `symbol` only answers from the project it is run in. `--project` and `--file` pointing outside that project are refused rather than honored, so the setting cannot be stepped around from inside the confined process. Every other read command (`read`, `refs`, `callers`, `types`, `dead`, `find`, `semantic`) is already project-scoped and is unaffected.
69
+ **Confining `symbol` to one project.** `token-goat symbol` is the one read command that answers from the machine-wide index (`global.db`) rather than the current project, so by default `symbol <name>` and `symbol --grep .` return matching symbols, bodies included, from every project ever indexed on the host. That is deliberate and useful on a personal machine: it is how you find a helper you wrote in another repo. On a shared build host, or under an agent you have confined to one directory, it is a read channel that the directory sandbox does not close, because the answer comes out of the index instead of the filesystem. Set `indexing.cross_project_symbols = false` (env `TOKEN_GOAT_CROSS_PROJECT_SYMBOLS`) and `symbol` only answers from the project it is run in. `--project` and `--file` pointing outside that project are refused rather than honored, so the setting cannot be stepped around from inside the confined process. Every other read command (`read`, `refs`, `callers`, `types`, `dead`, `find`, `semantic`) is already project-scoped and is unaffected. Because the permissive setting is the default and says nothing about itself, `token-goat doctor` prints a `Security symbol scope` line stating which way it is currently set, and names the switch when lookups are unconfined.
66
70
 
67
71
  **Redacting credentials this build has never heard of.** Before anything is written to disk or handed back to the model, token-goat redacts the credential shapes it recognises — 19 patterns covering AWS, GitHub, Slack, Stripe, OpenAI, Anthropic, Google, npm and Azure keys, private key blocks, JWTs, bearer and basic auth headers, presigned URL signatures, and credentials embedded in a URL. Two settings cover what a fixed list cannot. `redaction.custom_patterns` (env `TOKEN_GOAT_REDACTION_CUSTOM_PATTERNS`, one pattern per line) is a list of your own regular expressions, redacted as `[REDACTED:custom]` — for an in-house token prefix, an employee number, an internal account id. A pattern that does not compile is skipped and named by `token-goat doctor` rather than failing silently, because a redaction rule you believe is running and which is not is worse than none at all. `redaction.strict` (env `TOKEN_GOAT_REDACTION_STRICT`, off by default) additionally redacts long high-entropy strings that match nothing known: the shape of a credential nobody wrote a rule for. It is a heuristic, and it says so — it needs three of the four character classes and genuine randomness, which spares git SHAs and hex digests, but it will sometimes redact a base64 blob that was not a secret. Both are settings a per-project config file may not touch, so a checked-in `.token-goat.toml` cannot weaken redaction; an environment variable still can, which is what the `Security config overrides` line above exists to surface.
68
72
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "token-goat",
3
- "version": "2.9.3",
3
+ "version": "2.9.5",
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",