token-goat 2.9.6 → 2.9.8

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 CHANGED
@@ -129,7 +129,7 @@ The fastest way to reduce AI token costs is fixing these five, not writing short
129
129
  | `claude-in-chrome`'s `computer`/`browser_batch` return a raw, full-resolution base64 screenshot in-band, with no destination-file option to redirect through image-shrink | Inline screenshot blocks are shrunk via the same image-shrink pipeline in place, and a repeated `Tab Context:` listing (appended to nearly every call, often unchanged) collapses to a placeholder once seen unchanged this session |
130
130
  | Agent tool spawns a subagent with no orientation and no reuse hints | A `PreToolUse` handler appends a compact briefing pack to the prompt: a one-line project-map summary, 2-3 recent cached-output IDs, and a surgical-read reminder (~300 tokens) |
131
131
  | Subagent's own final report runs long and gets discarded once the parent moves on | Agent tool results ≥8000 characters get a recall pointer appended (`token-goat recall`); the original report always reaches the parent untouched |
132
- | Agent tool spawns with no `subagent_type` (or an explicit `general-purpose`), so the lane starts by paying for every tool and MCP schema on the machine | Once per session, a `PostToolUse` advisory names up to three `tools:`-restricted agent definitions found in `~/.claude/agents` and suggests passing one as `subagent_type` on a future spawn that fits. It fires only when at least one restricted definition exists, states outright that the observed spawn already ran and nothing was saved, and records a zero-credit stat |
132
+ | Agent tool spawns with no `subagent_type` (or an explicit `general-purpose`), so the lane starts by paying for every tool and MCP schema on the machine | Once per session, a `PostToolUse` advisory names up to three `tools:`-restricted agent definitions found in `~/.claude/agents` or the current project's `.claude/agents` and suggests passing one as `subagent_type` on a future spawn that fits. It fires only when at least one restricted definition exists, states outright that the observed spawn already ran and nothing was saved, and records a zero-credit stat |
133
133
  | Large MCP tool result (≥2 KB) is a homogeneous array of objects, e.g. a list/search result | Deterministic structural compression: table-ified into one header row + tab-delimited rows, with columns constant across every row hoisted into a single `constant:` line instead of repeated per row; only applied when it saves ≥15%. Full original always recoverable via `token-goat bash-output <id>` (labeled `[token-goat: compressed, full via mcp-output <id>]`). Disable with `TOKEN_GOAT_MCP_COMPRESS=0` |
134
134
  | Large MCP tool result (≥2 KB) doesn't table-ify (an object-rooted config dump, a single-resource response, a nested settings tree) but most of its bytes are `null`/`""`/`[]`/`{}` | Deterministic empty-value pruning: recursively drops those four empty shapes to a fixed point (a container left empty by its own dropped children is dropped too) and renders the remainder as compact JSON with a trailing `dropped N empty value(s) (null, "", [], {})` summary line; `0` and `false` are never dropped; only applied when it saves ≥15%. Same full-recovery-by-id guarantee and `TOKEN_GOAT_MCP_COMPRESS=0` opt-out as the table pass |
135
135
  | GitHub MCP tool result (`list_pull_requests`, `list_issues`, `search_code`, `get_file_contents`, `pull_request_read`, …) carries dozens of boilerplate fields per object | GitHub compression pack strips `_links`, `node_id`, `gravatar_id`, `site_admin`, and every `*_url` field (`avatar_url`, `html_url`, `events_url`, `gists_url`, `followers_url`, …) except `download_url`/`git_url`/`clone_url`/`ssh_url`, before handing the shrunk JSON to the same table-ifying pass — same `TOKEN_GOAT_MCP_COMPRESS=0` opt-out and full-recovery-by-id guarantee |
@@ -169,6 +169,9 @@ The fastest way to reduce AI token costs is fixing these five, not writing short
169
169
  | Fetched web content lands raw in model context | Always wrapped in an untrusted-content fence; scanned for attack patterns, and a matched pattern name is written to the log and into the fence's notice |
170
170
  | Chatty log repeats the same error or event thousands of times | `token-goat logfold` collapses consecutive duplicates to `[Nx]` counts; same event logged with different timestamps or request IDs folds correctly — ~90–95% smaller on repetitive logs |
171
171
  | Reading poetry.lock or package-lock.json to find a pinned version | `token-goat lockdeps` returns a name/version table of direct dependencies; optional packages and transitive entries excluded |
172
+ | Large SVG / diagram file (≥8 KB) read in full | Coordinate path flooding blocked; extracted layer/group IDs and title shown; redirects to `token-goat xml-outline` and `xml-query` |
173
+ | Broad recursive Glob sweep (`*`, `**/*`) on root directory | Pre-Glob hook warns against tree-dumping and points at `token-goat map --compact` for fast, lightweight structure inspection |
174
+ | Compound test/build pipeline (`npm run build && npm run typecheck && npm test`) | Post-Bash hook routes chained build/test/lint commands to `generic-ci` compression, dropping verbose passing steps and compiler noise |
172
175
 
173
176
  On a per-token API plan, 100K wasted tokens per session runs about $0.30. Five sessions a week is ~$450/year. AI coding cost reduction at that scale comes from fixing the waste, not from using the product less. Token-goat is free. And on subscription plans, it can result in limits feeling 10x higher.
174
177
 
@@ -422,7 +425,7 @@ never drift on where `mcp.json` lives or what key name it looks for.
422
425
 
423
426
  **What the index actually holds, in plain terms.** The point of a surgical read is returning a function body without the file around it, which means the database stores those bodies. `symbols.body` holds the source text of every indexed symbol, `symbols.docstring` its doc comment, `refs.context` the line around each reference, and `chunks.text` the passages that semantic search embeds. There is also a full-text index over the bodies and docstrings. So the database is not a list of names and line numbers: it is a substantial copy of your source, sitting in a plain unencrypted SQLite file outside the repository.
424
427
 
425
- The file-by-file table for each harness, and the path that file sits at: **[What gets installed](docs/install.md#what-gets-installed)**.
428
+ The file-by-file table for each harness, and the path that file sits at: **[What gets installed](docs/install.md#what-gets-installed)** (see also **[Permissions & auto-approval](docs/install.md#command-auto-approval-and-permissions)**).
426
429
 
427
430
  ## Zero maintenance
428
431
 
@@ -86,7 +86,7 @@ import {
86
86
  runZipRead,
87
87
  symbolNamesInFile,
88
88
  upsertNote
89
- } from "./token-goat-chunk-46VKOCUH.mjs";
89
+ } from "./token-goat-chunk-6RAUVAKW.mjs";
90
90
  import {
91
91
  DEFAULT_RECONCILE_BUDGET_MS,
92
92
  GEMINI_TOOL_NAME_MAP,
@@ -108,7 +108,7 @@ import {
108
108
  runReconcile,
109
109
  storeWebOutput,
110
110
  summarizeResidentContext
111
- } from "./token-goat-chunk-7GJBID7S.mjs";
111
+ } from "./token-goat-chunk-VEEHPNBV.mjs";
112
112
  import {
113
113
  AGENT_SALT_MARKER,
114
114
  BASH_OUTPUT_SUBDIR,
@@ -190,6 +190,7 @@ import {
190
190
  isParseSkipEligible,
191
191
  isRecallCacheType,
192
192
  isSuppressionCategory,
193
+ isTreeSitterAvailable,
193
194
  isWorkerRunning,
194
195
  listBlobs,
195
196
  listOutputs,
@@ -230,12 +231,14 @@ import {
230
231
  storeCompact,
231
232
  storeOutput,
232
233
  tokenGoatHome,
234
+ treeSitterCoreAvailable,
235
+ treeSitterCoreLoadError,
233
236
  trimToBudget,
234
237
  urlPolicyDenialReason,
235
238
  visionTokensSavedByText,
236
239
  walkProject,
237
240
  writeCompact
238
- } from "./token-goat-chunk-3HJQR4OO.mjs";
241
+ } from "./token-goat-chunk-TXZY7C24.mjs";
239
242
  import {
240
243
  C,
241
244
  CONFIG_KEY_ENV_OVERRIDES,
@@ -346,6 +349,7 @@ import {
346
349
  savedTokensFromBytes,
347
350
  sleepSync,
348
351
  stripAnsi,
352
+ stripLower,
349
353
  stripOwnHooksFromMap,
350
354
  stripStaleGroupHooks,
351
355
  suggestPackageNames,
@@ -368,7 +372,7 @@ import {
368
372
  withFileLock,
369
373
  withRetryOnLock,
370
374
  writeJsonSettings
371
- } from "./token-goat-chunk-T2OE7MYM.mjs";
375
+ } from "./token-goat-chunk-JZYJH76S.mjs";
372
376
  import {
373
377
  __export,
374
378
  init_define_import_meta_env
@@ -6313,6 +6317,29 @@ function checkTsCompiler() {
6313
6317
  message: err2 !== null ? `unavailable: ${extractErrorMessage(err2)}` : "unavailable (not attempted)"
6314
6318
  };
6315
6319
  }
6320
+ var TREE_SITTER_LANGUAGES = ["typescript", "javascript", "python", "go", "rust", "ruby", "java", "c", "cpp"];
6321
+ function checkTreeSitter() {
6322
+ const name = "Tree-sitter";
6323
+ if (!treeSitterCoreAvailable()) {
6324
+ const err2 = treeSitterCoreLoadError();
6325
+ const cause = err2 !== null ? `: ${extractErrorMessage(err2)}` : " (not attempted)";
6326
+ return {
6327
+ name,
6328
+ status: "warn",
6329
+ message: "unavailable -- the source skeleton fold, the code body fold's disk-parse fallback, and tree-sitter indexing are all disabled; likely cause: the optional native dependency `tree-sitter` is not installed, or is not resolvable from the bundle's location" + cause
6330
+ };
6331
+ }
6332
+ const available = TREE_SITTER_LANGUAGES.filter((lang) => isTreeSitterAvailable(lang));
6333
+ if (available.length === TREE_SITTER_LANGUAGES.length) {
6334
+ return { name, status: "ok", message: `available (${available.length}/${TREE_SITTER_LANGUAGES.length} grammars)` };
6335
+ }
6336
+ const missing = TREE_SITTER_LANGUAGES.filter((lang) => !available.includes(lang));
6337
+ return {
6338
+ name,
6339
+ status: "warn",
6340
+ message: `partially available (${available.length}/${TREE_SITTER_LANGUAGES.length} grammars); missing: ${missing.join(", ")}`
6341
+ };
6342
+ }
6316
6343
  function checkEmbeddings(config) {
6317
6344
  const name = "Embeddings";
6318
6345
  if ((config.indexing?.embeddings_enabled ?? true) === false) {
@@ -6688,6 +6715,7 @@ function runDoctor(dataDir2, configPath2, rootDir, processes) {
6688
6715
  const actualDataDir = dataDir2 || dataDir();
6689
6716
  results.push(checkInstall());
6690
6717
  results.push(checkTsCompiler());
6718
+ results.push(checkTreeSitter());
6691
6719
  results.push(checkStrayClaudeMdBlocks());
6692
6720
  results.push(checkWorkerRunning(actualDataDir) ? { name: "Worker", status: "ok", message: "running" } : { name: "Worker", status: "warn", message: "not running" });
6693
6721
  results.push(checkDbExists(actualDataDir));
@@ -14937,11 +14965,14 @@ function readClaudeJsonConfig(claudeJsonPath, projectRoot) {
14937
14965
  function discoverMcpConfig(projectRoot, home) {
14938
14966
  const mcpJsonPath = path28.join(projectRoot, ".mcp.json");
14939
14967
  const claudeJsonPath = path28.join(home, ".claude.json");
14940
- const sourcesChecked = [mcpJsonPath, claudeJsonPath];
14968
+ const copilotJsonPath = path28.join(home, ".copilot", "mcp-config.json");
14969
+ const sourcesChecked = [mcpJsonPath, claudeJsonPath, copilotJsonPath];
14941
14970
  const fromMcpJson = readMcpJsonFile(mcpJsonPath);
14942
14971
  if (fromMcpJson !== null) return { servers: fromMcpJson, sourcePath: mcpJsonPath, sourcesChecked };
14943
14972
  const fromClaudeJson = readClaudeJsonConfig(claudeJsonPath, projectRoot);
14944
14973
  if (fromClaudeJson !== null) return { servers: fromClaudeJson, sourcePath: claudeJsonPath, sourcesChecked };
14974
+ const fromCopilotJson = readMcpJsonFile(copilotJsonPath);
14975
+ if (fromCopilotJson !== null) return { servers: fromCopilotJson, sourcePath: copilotJsonPath, sourcesChecked };
14945
14976
  return { servers: null, sourcePath: null, sourcesChecked };
14946
14977
  }
14947
14978
  function analyzeMcpCache() {
@@ -15730,7 +15761,7 @@ async function cmdMcpServe() {
15730
15761
  let StdioServerTransport;
15731
15762
  try {
15732
15763
  ;
15733
- ({ createMcpServer } = await import("./token-goat-chunk-C7LLUVUT.mjs"));
15764
+ ({ createMcpServer } = await import("./token-goat-chunk-EZZ7IJYM.mjs"));
15734
15765
  ({ StdioServerTransport } = await import("./token-goat-chunk-EVC4TOLE.mjs"));
15735
15766
  } catch (err2) {
15736
15767
  process.stderr.write(
@@ -15755,7 +15786,7 @@ async function cmdHook(event, opts) {
15755
15786
  if (typeof opts.harness === "string" && opts.harness.length > 0) {
15756
15787
  process.env[ENV_KEYS.HARNESS_OVERRIDE] = opts.harness;
15757
15788
  }
15758
- const { relay } = await import("./token-goat-chunk-TX64YIIV.mjs");
15789
+ const { relay } = await import("./token-goat-chunk-Q4VCAIBR.mjs");
15759
15790
  await relay(event);
15760
15791
  }
15761
15792
  async function cmdInstall(opts) {
@@ -16688,7 +16719,7 @@ function emitExtraFileArgsNote(command, first, extras, opts = {}) {
16688
16719
  }
16689
16720
  async function cmdCompress(opts) {
16690
16721
  try {
16691
- const bashRunner = await import("./token-goat-chunk-UJORWTI4.mjs");
16722
+ const bashRunner = await import("./token-goat-chunk-WT5OLTT6.mjs");
16692
16723
  if (opts.compress === false) {
16693
16724
  process.exitCode = bashRunner.runRaw(opts.cmd, parseTimeout(opts.timeout, bashRunner.DEFAULT_TIMEOUT_SECONDS));
16694
16725
  return;
@@ -16961,8 +16992,12 @@ async function cmdSkillSection(nameHeading, headingArg) {
16961
16992
  const body = decodeSource(fs29.readFileSync(filePath));
16962
16993
  const extracted = extractNamedSection(body, heading);
16963
16994
  if (!extracted) {
16964
- const messages = [`Section '${heading}' not found in skill '${skillName}'`];
16965
16995
  const allHeadings = listSections(filePath);
16996
+ const wanted = stripLower(heading);
16997
+ if (allHeadings.some((h) => stripLower(h) === wanted)) {
16998
+ throw new CliError(`Section '${heading}' in skill '${skillName}' is present but empty`);
16999
+ }
17000
+ const messages = [`Section '${heading}' not found in skill '${skillName}'`];
16966
17001
  const available = filterSimilarHeadings(allHeadings, heading);
16967
17002
  if (available.length > 0) messages.push(didYouMean(available));
16968
17003
  else if (allHeadings.length === 0) messages.push(`skill '${skillName}' has no headings`);
@@ -7,11 +7,11 @@ import {
7
7
  expandGlobs,
8
8
  leftoverIntegrations,
9
9
  run
10
- } from "./token-goat-chunk-J7LGMKF3.mjs";
11
- import "./token-goat-chunk-46VKOCUH.mjs";
12
- import "./token-goat-chunk-7GJBID7S.mjs";
13
- import "./token-goat-chunk-3HJQR4OO.mjs";
14
- import "./token-goat-chunk-T2OE7MYM.mjs";
10
+ } from "./token-goat-chunk-2SBHUPBF.mjs";
11
+ import "./token-goat-chunk-6RAUVAKW.mjs";
12
+ import "./token-goat-chunk-VEEHPNBV.mjs";
13
+ import "./token-goat-chunk-TXZY7C24.mjs";
14
+ import "./token-goat-chunk-JZYJH76S.mjs";
15
15
  import "./token-goat-chunk-EEIDFMEM.mjs";
16
16
  import "./token-goat-chunk-A37V4PBF.mjs";
17
17
  export {
@@ -67,7 +67,7 @@ import {
67
67
  walkProject,
68
68
  yamlLineClosesQuote,
69
69
  yamlOpenQuoteAfter
70
- } from "./token-goat-chunk-3HJQR4OO.mjs";
70
+ } from "./token-goat-chunk-TXZY7C24.mjs";
71
71
  import {
72
72
  Database,
73
73
  PER_FILE_COUNTERFACTUAL_CEILING,
@@ -125,7 +125,7 @@ import {
125
125
  unsupportedLanguageName,
126
126
  windowsCmdQuoteArg,
127
127
  withExtension
128
- } from "./token-goat-chunk-T2OE7MYM.mjs";
128
+ } from "./token-goat-chunk-JZYJH76S.mjs";
129
129
  import {
130
130
  registerReset
131
131
  } from "./token-goat-chunk-EEIDFMEM.mjs";
@@ -25,7 +25,7 @@ import {
25
25
  runSkeleton,
26
26
  runSymbol,
27
27
  withPinnedReads
28
- } from "./token-goat-chunk-46VKOCUH.mjs";
28
+ } from "./token-goat-chunk-6RAUVAKW.mjs";
29
29
  import {
30
30
  buildProjectMap,
31
31
  embeddingsDepsAvailable,
@@ -34,7 +34,7 @@ import {
34
34
  getProjectIndexCounts,
35
35
  isWorkerRunning,
36
36
  mapLookupBytesSaved
37
- } from "./token-goat-chunk-3HJQR4OO.mjs";
37
+ } from "./token-goat-chunk-TXZY7C24.mjs";
38
38
  import {
39
39
  ENV_KEYS,
40
40
  VERSION,
@@ -48,7 +48,7 @@ import {
48
48
  recordStat,
49
49
  resolveProjectRoot,
50
50
  savedTokensFromBytes
51
- } from "./token-goat-chunk-T2OE7MYM.mjs";
51
+ } from "./token-goat-chunk-JZYJH76S.mjs";
52
52
  import "./token-goat-chunk-EEIDFMEM.mjs";
53
53
  import {
54
54
  init_define_import_meta_env