token-goat 2.9.16 → 2.9.18

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 (35) hide show
  1. package/README.md +2 -5
  2. package/dist/{token-goat-chunk-ULA4WCED.mjs → token-goat-chunk-2KTZ6J7T.mjs} +7 -7
  3. package/dist/{token-goat-chunk-LJL6FGDS.mjs → token-goat-chunk-34OUJ2IE.mjs} +11 -11
  4. package/dist/{token-goat-chunk-OCI25AXY.mjs → token-goat-chunk-52IMXJ2L.mjs} +3 -3
  5. package/dist/{token-goat-chunk-MIL62GMN.mjs → token-goat-chunk-5MMJAJF3.mjs} +6 -6
  6. package/dist/{token-goat-chunk-NOBZV7PM.mjs → token-goat-chunk-6LGVCCMY.mjs} +12 -12
  7. package/dist/{token-goat-chunk-MA5BZ2KI.mjs → token-goat-chunk-7VLWNQDQ.mjs} +1 -1
  8. package/dist/{token-goat-chunk-2IEIBVVD.mjs → token-goat-chunk-ARNRFR6M.mjs} +1 -1
  9. package/dist/{token-goat-chunk-XOHTFRDH.mjs → token-goat-chunk-BFEWOMA3.mjs} +11 -9
  10. package/dist/{token-goat-chunk-QMZMHQRR.mjs → token-goat-chunk-BWCRC23L.mjs} +18 -18
  11. package/dist/{token-goat-chunk-H3DXI6VD.mjs → token-goat-chunk-CA6V36KW.mjs} +1 -1
  12. package/dist/{token-goat-chunk-5USHO7B6.mjs → token-goat-chunk-CTOYJ232.mjs} +18 -41
  13. package/dist/{token-goat-chunk-GIMGT34T.mjs → token-goat-chunk-EPXNOKIV.mjs} +1 -1
  14. package/dist/{token-goat-chunk-YPS64ACE.mjs → token-goat-chunk-GRI6HWX3.mjs} +1 -1
  15. package/dist/{token-goat-chunk-EJCY6KHO.mjs → token-goat-chunk-H3FONFBO.mjs} +2 -2
  16. package/dist/{token-goat-chunk-VVIYEFBI.mjs → token-goat-chunk-HIZXQVNU.mjs} +3 -3
  17. package/dist/{token-goat-chunk-DMQ32ZSR.mjs → token-goat-chunk-HPOFR6QX.mjs} +3 -3
  18. package/dist/{token-goat-chunk-63QWJ6UM.mjs → token-goat-chunk-I67CPTDD.mjs} +2 -2
  19. package/dist/{token-goat-chunk-M2R4UQ3B.mjs → token-goat-chunk-K4DG6EEF.mjs} +2 -2
  20. package/dist/{token-goat-chunk-R3X5TCPB.mjs → token-goat-chunk-L7VCX33I.mjs} +1 -1
  21. package/dist/{token-goat-chunk-NAMH252H.mjs → token-goat-chunk-LA3O4JGD.mjs} +2 -2
  22. package/dist/{token-goat-chunk-Z5VPAXTC.mjs → token-goat-chunk-LTYWLKAG.mjs} +61 -31
  23. package/dist/{token-goat-chunk-ZEHFYJ7Z.mjs → token-goat-chunk-OWSJJY5R.mjs} +4 -4
  24. package/dist/{token-goat-chunk-3H6FQT62.mjs → token-goat-chunk-QIDLJGXZ.mjs} +1 -1
  25. package/dist/{token-goat-chunk-2PCFAQAS.mjs → token-goat-chunk-QML7ITHJ.mjs} +87 -10
  26. package/dist/{token-goat-chunk-UXHDBVUR.mjs → token-goat-chunk-RHHFEXKF.mjs} +1 -1
  27. package/dist/{token-goat-chunk-NQZCSVN2.mjs → token-goat-chunk-UENEL6KX.mjs} +1 -1
  28. package/dist/{token-goat-chunk-UMXD2M7O.mjs → token-goat-chunk-V3ZWBEHM.mjs} +8 -8
  29. package/dist/{token-goat-chunk-BFNTLGVA.mjs → token-goat-chunk-W3E65QOW.mjs} +31 -9
  30. package/dist/{token-goat-chunk-GMXBQS6O.mjs → token-goat-chunk-WFX2V3OF.mjs} +58 -47
  31. package/dist/{token-goat-chunk-4DC6DDIB.mjs → token-goat-chunk-YQZE5NS6.mjs} +5 -5
  32. package/dist/token-goat-hook.mjs +11 -11
  33. package/dist/token-goat.core.mjs +18 -18
  34. package/docs/cli.md +3 -2
  35. package/package.json +1 -1
@@ -4,7 +4,7 @@ import {
4
4
  canRunWrappedShell,
5
5
  clipToDeliveryCap,
6
6
  deliveredOutputBytes
7
- } from "./token-goat-chunk-UXHDBVUR.mjs";
7
+ } from "./token-goat-chunk-RHHFEXKF.mjs";
8
8
  import {
9
9
  DEFAULT_RECONCILE_BUDGET_MS,
10
10
  HOOK_EVENTS,
@@ -17,7 +17,7 @@ import {
17
17
  readStdinJson,
18
18
  reconcileProject,
19
19
  storeWebOutput
20
- } from "./token-goat-chunk-UMXD2M7O.mjs";
20
+ } from "./token-goat-chunk-V3ZWBEHM.mjs";
21
21
  import {
22
22
  BODY_FIRST_TOOL_RESPONSE_KEYS,
23
23
  GENERIC_SERVED_OUTPUT_KEY,
@@ -144,7 +144,7 @@ import {
144
144
  wasFileReadThisSession,
145
145
  wasHintShown,
146
146
  withPreservedBlocks
147
- } from "./token-goat-chunk-XOHTFRDH.mjs";
147
+ } from "./token-goat-chunk-BFEWOMA3.mjs";
148
148
  import {
149
149
  BASH_OUTPUT_SUBDIR,
150
150
  commandHash,
@@ -167,26 +167,26 @@ import {
167
167
  splitOwnTrailingNotices,
168
168
  storeBashOutput,
169
169
  summarizeOutputDelta
170
- } from "./token-goat-chunk-5USHO7B6.mjs";
170
+ } from "./token-goat-chunk-CTOYJ232.mjs";
171
171
  import {
172
172
  recordStat,
173
173
  redactSecrets,
174
174
  savedTokensFromBytes,
175
175
  storeBlob,
176
176
  stripAnsiEscapes
177
- } from "./token-goat-chunk-DMQ32ZSR.mjs";
177
+ } from "./token-goat-chunk-HPOFR6QX.mjs";
178
178
  import {
179
179
  extractMarkdownHeadings,
180
180
  formatHeadingTreeParts
181
- } from "./token-goat-chunk-BFNTLGVA.mjs";
181
+ } from "./token-goat-chunk-W3E65QOW.mjs";
182
182
  import {
183
183
  countSymbols
184
- } from "./token-goat-chunk-63QWJ6UM.mjs";
184
+ } from "./token-goat-chunk-I67CPTDD.mjs";
185
185
  import {
186
186
  detectLanguage,
187
187
  getDb,
188
188
  languageHasFlag
189
- } from "./token-goat-chunk-R3X5TCPB.mjs";
189
+ } from "./token-goat-chunk-L7VCX33I.mjs";
190
190
  import {
191
191
  detectHarness,
192
192
  envBool,
@@ -196,7 +196,7 @@ import {
196
196
  isUnderSystemTemp,
197
197
  loadConfig,
198
198
  shortFingerprint
199
- } from "./token-goat-chunk-H3DXI6VD.mjs";
199
+ } from "./token-goat-chunk-CA6V36KW.mjs";
200
200
  import {
201
201
  ENV_KEYS,
202
202
  IDENTICAL_READ_MIN_BODY_BYTES,
@@ -207,6 +207,7 @@ import {
207
207
  countNoun,
208
208
  dataDir,
209
209
  ensureDirSync,
210
+ escapeRegExp,
210
211
  escapesOntoNetworkThroughLinks,
211
212
  extractErrorMessage,
212
213
  foldPath,
@@ -215,7 +216,7 @@ import {
215
216
  isInsideRoot,
216
217
  runGit,
217
218
  toKB
218
- } from "./token-goat-chunk-3H6FQT62.mjs";
219
+ } from "./token-goat-chunk-QIDLJGXZ.mjs";
219
220
  import {
220
221
  UNTRUSTED_TOOL_TAG,
221
222
  displaySafePath,
@@ -403,7 +404,7 @@ function grepIntInput(toolInput, key) {
403
404
  function grepSignature(toolInput) {
404
405
  const pattern = toolInput["pattern"];
405
406
  if (typeof pattern !== "string" || pattern === "") return null;
406
- const path4 = typeof toolInput["path"] === "string" ? toolInput["path"] : "";
407
+ const path5 = typeof toolInput["path"] === "string" ? toolInput["path"] : "";
407
408
  const outputMode = typeof toolInput["output_mode"] === "string" ? toolInput["output_mode"] : "files_with_matches";
408
409
  const glob = typeof toolInput["glob"] === "string" ? toolInput["glob"] : "";
409
410
  const type = typeof toolInput["type"] === "string" ? toolInput["type"] : "";
@@ -418,7 +419,7 @@ function grepSignature(toolInput) {
418
419
  const offset = grepIntInput(toolInput, "offset");
419
420
  return JSON.stringify([
420
421
  pattern,
421
- path4,
422
+ path5,
422
423
  outputMode,
423
424
  glob,
424
425
  type,
@@ -562,8 +563,8 @@ function isBroadCatchAllGlob(pattern, pathArg) {
562
563
  function globSignature(toolInput) {
563
564
  const pattern = toolInput["pattern"];
564
565
  if (typeof pattern !== "string" || pattern === "") return null;
565
- const path4 = typeof toolInput["path"] === "string" ? toolInput["path"] : "";
566
- return JSON.stringify([pattern, path4]);
566
+ const path5 = typeof toolInput["path"] === "string" ? toolInput["path"] : "";
567
+ return JSON.stringify([pattern, path5]);
567
568
  }
568
569
  var { post: postGlobHandler, pre: preGlobDedupHandler } = makeDedupHintHandlers({
569
570
  toolName: "Glob",
@@ -717,6 +718,7 @@ registerHook("pre_tool_use", preWriteRewriteHandler, { toolName: "Write" });
717
718
  // src/hooks_compact.ts
718
719
  init_define_import_meta_env();
719
720
  import { spawnSync } from "node:child_process";
721
+ import * as path3 from "node:path";
720
722
  var MEM_EPOCH_TIMEOUT_MS = 800;
721
723
  var MAX_ROWS = 40;
722
724
  function appendCappedSection(lines, header, rows, cap) {
@@ -919,8 +921,14 @@ function postCompactHandler(event) {
919
921
  const summary = typeof raw === "string" ? raw : "";
920
922
  const bytes = Buffer.byteLength(summary, "utf-8");
921
923
  const sample = manifestPathSample(event.sessionId);
922
- const haystack = foldPath(summary);
923
- const survived = sample.filter((p) => haystack.includes(foldPath(p))).length;
924
+ const cwd = normalizePath(getCwd(event) ?? process.cwd());
925
+ const spell = (p) => foldPath(p.replaceAll("\\", "/"));
926
+ const haystack = spell(summary);
927
+ const survived = sample.filter((p) => {
928
+ const rel = path3.relative(cwd, p);
929
+ const forms = rel !== "" && !rel.startsWith("..") && !path3.isAbsolute(rel) ? [p, rel] : [p];
930
+ return forms.some((f) => haystack.includes(spell(f)));
931
+ }).length;
924
932
  const trigger = typeof event.raw["trigger"] === "string" ? event.raw["trigger"] : "unknown";
925
933
  const budget = loadConfig().compact_assist.summary_budget_chars;
926
934
  const over = budget > 0 && summary.length > budget ? 1 : 0;
@@ -945,7 +953,7 @@ init_define_import_meta_env();
945
953
  var TRACKED_SKILL = "token-goat";
946
954
  var MAX_COMMANDS_SHOWN = 8;
947
955
  async function currentCommandNames() {
948
- const { buildProgram } = await import("./token-goat-chunk-QMZMHQRR.mjs");
956
+ const { buildProgram } = await import("./token-goat-chunk-BWCRC23L.mjs");
949
957
  return flattenCommandNames(buildCommandManifest(buildProgram()));
950
958
  }
951
959
  async function recordSkillVersionSnapshot(sessionId, skillName) {
@@ -1234,8 +1242,10 @@ function reconcileNote(cwd, indexed) {
1234
1242
  const budgetMs = envInt(ENV_KEYS.RECONCILE_BUDGET_MS, DEFAULT_RECONCILE_BUDGET_MS, 0, 6e4);
1235
1243
  const result = reconcileProject({ cwd, budgetMs });
1236
1244
  if (isReconcileClean(result) && !result.budgetExhausted) return null;
1245
+ const upgraded = result.parserStale + result.embedStale;
1246
+ const edited = result.changed.length - upgraded;
1237
1247
  const parts = [];
1238
- if (result.changed.length > 0) parts.push(`${result.changed.length} changed`);
1248
+ if (edited > 0) parts.push(`${edited} changed`);
1239
1249
  if (result.added.length > 0) parts.push(`${result.added.length} new`);
1240
1250
  if (result.removed.length > 0) parts.push(`${result.removed.length} removed`);
1241
1251
  const breakdown = parts.length > 1 ? ` (${parts.join(", ")})` : "";
@@ -1244,7 +1254,11 @@ function reconcileNote(cwd, indexed) {
1244
1254
  if (total === 0) {
1245
1255
  return `token-goat: index drift check${truncated} found nothing in the part it had time to scan. Symbol lookups may be briefly stale.`;
1246
1256
  }
1247
- return `token-goat: reindexing ${countNoun(total, "file")} that changed outside this session${breakdown}${truncated}. Symbol lookups may be briefly stale.`;
1257
+ const drifted = total - upgraded;
1258
+ const clauses = [];
1259
+ if (drifted > 0) clauses.push(`${countNoun(drifted, "file")} that changed outside this session${breakdown}`);
1260
+ if (upgraded > 0) clauses.push(`${countNoun(upgraded, "file")} unchanged on disk but indexed by an older version of token-goat`);
1261
+ return `token-goat: reindexing ${clauses.join(" and ")}${truncated}. Symbol lookups may be briefly stale.`;
1248
1262
  } catch {
1249
1263
  return null;
1250
1264
  }
@@ -1773,11 +1787,11 @@ function toRegExp(r, i) {
1773
1787
  if (r.str.length === 0) {
1774
1788
  throw new Error(`Rule #${i} ("${r.name}") has empty "str" property, which is not allowed.`);
1775
1789
  }
1776
- return new RegExp(escapeRegExp(r.str), "y");
1790
+ return new RegExp(escapeRegExp2(r.str), "y");
1777
1791
  }
1778
- return new RegExp(escapeRegExp(r.name), "y");
1792
+ return new RegExp(escapeRegExp2(r.name), "y");
1779
1793
  }
1780
- function escapeRegExp(str) {
1794
+ function escapeRegExp2(str) {
1781
1795
  return str.replace(/[-[\]{}()*+!<=:?./\\^$|#\s,]/g, "\\$&");
1782
1796
  }
1783
1797
  function toSticky(re) {
@@ -5837,8 +5851,8 @@ function trimCharacterEnd(str, char) {
5837
5851
  function unicodeEscape(str) {
5838
5852
  return str.replace(/[\s\S]/g, (c) => "\\u" + c.charCodeAt().toString(16).padStart(4, "0"));
5839
5853
  }
5840
- function get(obj, path4) {
5841
- for (const key of path4) {
5854
+ function get(obj, path5) {
5855
+ for (const key of path5) {
5842
5856
  if (!obj) {
5843
5857
  return void 0;
5844
5858
  }
@@ -7039,8 +7053,8 @@ function withBrackets(str, brackets) {
7039
7053
  const rbr = typeof brackets[1] === "string" ? brackets[1] : "]";
7040
7054
  return lbr + str + rbr;
7041
7055
  }
7042
- function pathRewrite(path4, rewriter, baseUrl, metadata, elem) {
7043
- const modifiedPath = typeof rewriter === "function" ? rewriter(path4, metadata, elem) : path4;
7056
+ function pathRewrite(path5, rewriter, baseUrl, metadata, elem) {
7057
+ const modifiedPath = typeof rewriter === "function" ? rewriter(path5, metadata, elem) : path5;
7044
7058
  return modifiedPath[0] === "/" && baseUrl ? trimCharacterEnd(baseUrl, "/") + modifiedPath : modifiedPath;
7045
7059
  }
7046
7060
  function formatImage(elem, walk, builder, formatOptions) {
@@ -7364,9 +7378,9 @@ function handleDeprecatedOptions(options) {
7364
7378
  options.selectors.push(...tagDefinitions);
7365
7379
  options.selectors = mergeDuplicatesPreferLast(options.selectors, ((s) => s.selector));
7366
7380
  }
7367
- function set(obj, path4, value) {
7368
- const valueKey = path4.pop();
7369
- for (const key of path4) {
7381
+ function set(obj, path5, value) {
7382
+ const valueKey = path5.pop();
7383
+ for (const key of path5) {
7370
7384
  let nested = obj[key];
7371
7385
  if (!nested) {
7372
7386
  nested = {};
@@ -7705,10 +7719,7 @@ function splitShellSegments(cmd) {
7705
7719
  }
7706
7720
  function isTempPath(fp) {
7707
7721
  const norm = fp.replace(/\\/g, "/");
7708
- return /^\/tmp\//i.test(norm) || /\/var\/folders\//i.test(norm) || // Anchored to a local drive root on purpose. Unanchored, this clause said yes to
7709
- // `//host/share/AppData/Local/Temp/x.md`, and the caller's answer to yes is to stat the path --
7710
- // which on Windows opens an SMB session to whatever host a command named. A temp directory is
7711
- // somewhere on a local volume; nothing that starts with two separators is one.
7722
+ return /^\/tmp\//i.test(norm) || /\/var\/folders\//i.test(norm) || // Anchored to a local drive root on purpose. Unanchored, this clause said yes to `//host/share/AppData/Local/Temp/x.md`, and the caller's answer to yes is to stat the path -- which on Windows opens an SMB session to whatever host a command named. A temp directory is somewhere on a local volume; nothing that starts with two separators is one.
7712
7723
  /^[a-z]:\/(?:[^/]+\/)*AppData\/Local\/Temp\//i.test(norm) || norm.startsWith("/c/Users/") && norm.includes("/AppData/Local/Temp/") || isUnderSystemTemp(fp);
7713
7724
  }
7714
7725
  function isOrchestratorStateFile(filePath) {
@@ -8256,6 +8267,7 @@ function extractNodeFileRead(cmd) {
8256
8267
  const filePath = readSync2[1];
8257
8268
  if (isOrchestratorStateFile(filePath)) return null;
8258
8269
  if (isTempPath(filePath)) return null;
8270
+ if (new RegExp(`(?:writeFileSync|appendFileSync)\\(\\s*['"]${escapeRegExp(filePath)}['"]`).test(cmd)) return null;
8259
8271
  const { isDoc, isConfig, isSql } = classifyDocConfig(filePath);
8260
8272
  return { filePath, isDoc, isConfig, isSql };
8261
8273
  }
@@ -8521,10 +8533,7 @@ var CURL_VALUE_FLAGS = /* @__PURE__ */ new Set([
8521
8533
  "--range",
8522
8534
  "-r",
8523
8535
  "--interface",
8524
- // Options whose own value is a URL or a host. Leaving these out was the same defect as reading
8525
- // the first URL in the string: `curl --doh-url https://doh/ -o f https://target` answered with
8526
- // the DoH resolver rather than the file being fetched. `--url-query` belongs here too -- it
8527
- // appends query data to the request, it does not name the request target.
8536
+ // Options whose own value is a URL or a host. Leaving these out was the same defect as reading the first URL in the string: `curl --doh-url https://doh/ -o f https://target` answered with the DoH resolver rather than the file being fetched. `--url-query` belongs here too -- it appends query data to the request, it does not name the request target.
8528
8537
  "--url-query",
8529
8538
  "--doh-url",
8530
8539
  "--preproxy",
@@ -9988,7 +9997,8 @@ function recordBashFileReadsForSessionCache(cmd, cwd) {
9988
9997
  }
9989
9998
  async function maybeEmitLargeUncompressedHint(cmd, output, exitCode, cwd, isUnwrapped, event, ansiResult) {
9990
9999
  const outputBytes = Buffer.byteLength(output, "utf-8");
9991
- if (outputBytes < 4096 || process.env["TOKEN_GOAT_BASH_COMPRESS"] === "0" || exitCode !== null && exitCode !== 0 || event.raw["_tg_harness"] !== "vscode" && (isCompressibleSingleCommand(cmd) || !isUnwrapped)) {
10000
+ if (outputBytes < 4096 || process.env["TOKEN_GOAT_BASH_COMPRESS"] === "0" || exitCode !== null && exitCode !== 0 || // token-goat's own output is already the narrow form; telling the model to compress it is noise.
10001
+ /^\s*token-goat\s/.test(cmd) || event.raw["_tg_harness"] !== "vscode" && (isCompressibleSingleCommand(cmd) || !isUnwrapped)) {
9992
10002
  return null;
9993
10003
  }
9994
10004
  const key = `bash-uncompressed-4k:${event.sessionId ?? ""}:${shortFingerprint(cmd)}`;
@@ -9997,7 +10007,8 @@ async function maybeEmitLargeUncompressedHint(cmd, output, exitCode, cwd, isUnwr
9997
10007
  recordStat("session_hint", 0, 0);
9998
10008
  const id = await storeBashOutput(cmd, output, exitCode ?? 0, cwd);
9999
10009
  const kb = Math.round(outputBytes / 1024);
10000
- const msg = `[tg] Output was ${kb}KB uncompressed. For large tool outputs, run with 'token-goat compress -c "${displaySafeText(cmd)}"' or inspect via 'token-goat bash-output ${id}'.`;
10010
+ const compressable = !/["`$\r\n]/.test(cmd);
10011
+ const msg = `[tg] Output was ${kb}KB uncompressed; \`token-goat bash-output ${id}\` recalls it` + (compressable ? `, and \`token-goat compress -c "${displaySafeText(cmd)}"\` compresses the next run.` : ".");
10001
10012
  if (ansiResult !== null && ansiResult.hookType === "rewriteOutput") {
10002
10013
  return emitRewrite(msg + "\n" + fenceUntrusted(ansiResult.updatedOutput, UNTRUSTED_TOOL_TAG), "large uncompressed output hint", void 0, "counted-elsewhere");
10003
10014
  }
@@ -10322,7 +10333,7 @@ registerHook("post_tool_use", postTaskOutputHandler, { toolName: "TaskOutput" })
10322
10333
  // src/hooks_tool_failure.ts
10323
10334
  init_define_import_meta_env();
10324
10335
  import { existsSync as existsSync2, readFileSync as readFileSync3, statSync as statSync7, writeFileSync as writeFileSync2 } from "node:fs";
10325
- import { dirname as dirname2, relative } from "node:path";
10336
+ import { dirname as dirname2, relative as relative2 } from "node:path";
10326
10337
  var FAILURE_SUFFIX = ".tool-failures.json";
10327
10338
  var MAX_TRACKED_FAILURES = 64;
10328
10339
  var SIGNATURE_ERROR_CHARS = 200;
@@ -10393,7 +10404,7 @@ function diagnoseEditFailure(event, errorText) {
10393
10404
  const stat = statSync7(absPath);
10394
10405
  if (stat.size > MAX_EDIT_DIAGNOSE_BYTES) return null;
10395
10406
  const fileContent = readFileSync3(absPath, "utf8");
10396
- const relDisplay = displaySafeText(relative(process.cwd(), absPath).replace(/\\/g, "/") || absPath);
10407
+ const relDisplay = displaySafeText(relative2(process.cwd(), absPath).replace(/\\/g, "/") || absPath);
10397
10408
  if (isMultiple) {
10398
10409
  const matchLines = [];
10399
10410
  let pos = 0;
@@ -11243,7 +11254,7 @@ init_define_import_meta_env();
11243
11254
  import { createHash as createHash2 } from "node:crypto";
11244
11255
  import * as fs2 from "node:fs";
11245
11256
  import * as os2 from "node:os";
11246
- import * as path3 from "node:path";
11257
+ import * as path4 from "node:path";
11247
11258
  var BRIEFING_TARGET_TOKENS = 550;
11248
11259
  function buildSubagentBriefing() {
11249
11260
  try {
@@ -11468,8 +11479,8 @@ function parseAgentDefinition(text, fallbackName) {
11468
11479
  return { name: name2, restricted: false };
11469
11480
  }
11470
11481
  function findRestrictedAgentNames(roots) {
11471
- const projectAgentsRoot = path3.join(process.cwd(), ".claude", "agents");
11472
- const scanRoots = roots ?? [path3.join(os2.homedir(), ".claude", "agents"), projectAgentsRoot];
11482
+ const projectAgentsRoot = path4.join(process.cwd(), ".claude", "agents");
11483
+ const scanRoots = roots ?? [path4.join(os2.homedir(), ".claude", "agents"), projectAgentsRoot];
11473
11484
  const names = /* @__PURE__ */ new Set();
11474
11485
  const visited = /* @__PURE__ */ new Set();
11475
11486
  let filesSeen = 0;
@@ -11493,7 +11504,7 @@ function findRestrictedAgentNames(roots) {
11493
11504
  }
11494
11505
  for (const entry of entries) {
11495
11506
  if (filesSeen >= ROSTER_WALK_MAX_FILES) return;
11496
- const full = path3.join(dir, entry.name);
11507
+ const full = path4.join(dir, entry.name);
11497
11508
  let stat;
11498
11509
  try {
11499
11510
  stat = fs2.statSync(full);
@@ -11512,12 +11523,12 @@ function findRestrictedAgentNames(roots) {
11512
11523
  } catch {
11513
11524
  continue;
11514
11525
  }
11515
- const parsed = parseAgentDefinition(text, path3.basename(entry.name, path3.extname(entry.name)));
11526
+ const parsed = parseAgentDefinition(text, path4.basename(entry.name, path4.extname(entry.name)));
11516
11527
  if (parsed !== null && parsed.restricted) names.add(parsed.name);
11517
11528
  }
11518
11529
  };
11519
11530
  for (const root of scanRoots) {
11520
- const isProjectRoot = roots === void 0 && path3.resolve(root) === path3.resolve(projectAgentsRoot);
11531
+ const isProjectRoot = roots === void 0 && path4.resolve(root) === path4.resolve(projectAgentsRoot);
11521
11532
  if (!isProjectRoot) {
11522
11533
  walk(root, 0, null);
11523
11534
  continue;
@@ -20,17 +20,17 @@ import {
20
20
  setOcrTimeoutForTesting,
21
21
  setTesseractEntryForTesting,
22
22
  verifyOcrLangCache
23
- } from "./token-goat-chunk-EJCY6KHO.mjs";
24
- import "./token-goat-chunk-DMQ32ZSR.mjs";
25
- import "./token-goat-chunk-R3X5TCPB.mjs";
23
+ } from "./token-goat-chunk-H3FONFBO.mjs";
24
+ import "./token-goat-chunk-HPOFR6QX.mjs";
25
+ import "./token-goat-chunk-L7VCX33I.mjs";
26
26
  import {
27
27
  DEFAULT_OCR_LANG,
28
28
  isSupportedOcrLang,
29
29
  resolveOcrLangs
30
- } from "./token-goat-chunk-H3DXI6VD.mjs";
30
+ } from "./token-goat-chunk-CA6V36KW.mjs";
31
31
  import "./token-goat-chunk-EEIDFMEM.mjs";
32
32
  import "./token-goat-chunk-OSUFN2FV.mjs";
33
- import "./token-goat-chunk-3H6FQT62.mjs";
33
+ import "./token-goat-chunk-QIDLJGXZ.mjs";
34
34
  import "./token-goat-chunk-ERTXEKB6.mjs";
35
35
  import "./token-goat-chunk-GMOUBOX4.mjs";
36
36
  import "./token-goat-chunk-A37V4PBF.mjs";
@@ -2,20 +2,20 @@ 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-GMXBQS6O.mjs";
6
- import "./token-goat-chunk-UXHDBVUR.mjs";
7
- import "./token-goat-chunk-UMXD2M7O.mjs";
8
- import "./token-goat-chunk-XOHTFRDH.mjs";
9
- import "./token-goat-chunk-5USHO7B6.mjs";
10
- import "./token-goat-chunk-DMQ32ZSR.mjs";
5
+ } from "./token-goat-chunk-WFX2V3OF.mjs";
6
+ import "./token-goat-chunk-RHHFEXKF.mjs";
7
+ import "./token-goat-chunk-V3ZWBEHM.mjs";
8
+ import "./token-goat-chunk-BFEWOMA3.mjs";
9
+ import "./token-goat-chunk-CTOYJ232.mjs";
10
+ import "./token-goat-chunk-HPOFR6QX.mjs";
11
11
  import "./token-goat-chunk-3BTK54F3.mjs";
12
- import "./token-goat-chunk-BFNTLGVA.mjs";
13
- import "./token-goat-chunk-63QWJ6UM.mjs";
14
- import "./token-goat-chunk-R3X5TCPB.mjs";
15
- import "./token-goat-chunk-H3DXI6VD.mjs";
12
+ import "./token-goat-chunk-W3E65QOW.mjs";
13
+ import "./token-goat-chunk-I67CPTDD.mjs";
14
+ import "./token-goat-chunk-L7VCX33I.mjs";
15
+ import "./token-goat-chunk-CA6V36KW.mjs";
16
16
  import "./token-goat-chunk-EEIDFMEM.mjs";
17
17
  import "./token-goat-chunk-OSUFN2FV.mjs";
18
- import "./token-goat-chunk-3H6FQT62.mjs";
18
+ import "./token-goat-chunk-QIDLJGXZ.mjs";
19
19
  import "./token-goat-chunk-ERTXEKB6.mjs";
20
20
  import "./token-goat-chunk-Y4AFKTHK.mjs";
21
21
  import "./token-goat-chunk-GMOUBOX4.mjs";
@@ -2,33 +2,33 @@ 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-Z5VPAXTC.mjs";
6
- import "./token-goat-chunk-2PCFAQAS.mjs";
7
- import "./token-goat-chunk-EJCY6KHO.mjs";
5
+ } from "./token-goat-chunk-LTYWLKAG.mjs";
6
+ import "./token-goat-chunk-QML7ITHJ.mjs";
7
+ import "./token-goat-chunk-H3FONFBO.mjs";
8
8
  import "./token-goat-chunk-LMFO66YD.mjs";
9
- import "./token-goat-chunk-UMXD2M7O.mjs";
10
- import "./token-goat-chunk-XOHTFRDH.mjs";
11
- import "./token-goat-chunk-5USHO7B6.mjs";
12
- import "./token-goat-chunk-DMQ32ZSR.mjs";
9
+ import "./token-goat-chunk-V3ZWBEHM.mjs";
10
+ import "./token-goat-chunk-BFEWOMA3.mjs";
11
+ import "./token-goat-chunk-CTOYJ232.mjs";
12
+ import "./token-goat-chunk-HPOFR6QX.mjs";
13
13
  import "./token-goat-chunk-LKSXAMJB.mjs";
14
- import "./token-goat-chunk-NQZCSVN2.mjs";
15
- import "./token-goat-chunk-2IEIBVVD.mjs";
16
- import "./token-goat-chunk-NAMH252H.mjs";
17
- import "./token-goat-chunk-GIMGT34T.mjs";
14
+ import "./token-goat-chunk-UENEL6KX.mjs";
15
+ import "./token-goat-chunk-ARNRFR6M.mjs";
16
+ import "./token-goat-chunk-LA3O4JGD.mjs";
17
+ import "./token-goat-chunk-EPXNOKIV.mjs";
18
18
  import "./token-goat-chunk-3BTK54F3.mjs";
19
- import "./token-goat-chunk-MA5BZ2KI.mjs";
19
+ import "./token-goat-chunk-7VLWNQDQ.mjs";
20
20
  import "./token-goat-chunk-XTQAOTSO.mjs";
21
21
  import "./token-goat-chunk-AH6QILZM.mjs";
22
- import "./token-goat-chunk-YPS64ACE.mjs";
23
- import "./token-goat-chunk-BFNTLGVA.mjs";
24
- import "./token-goat-chunk-63QWJ6UM.mjs";
25
- import "./token-goat-chunk-R3X5TCPB.mjs";
26
- import "./token-goat-chunk-H3DXI6VD.mjs";
22
+ import "./token-goat-chunk-GRI6HWX3.mjs";
23
+ import "./token-goat-chunk-W3E65QOW.mjs";
24
+ import "./token-goat-chunk-I67CPTDD.mjs";
25
+ import "./token-goat-chunk-L7VCX33I.mjs";
26
+ import "./token-goat-chunk-CA6V36KW.mjs";
27
27
  import "./token-goat-chunk-EEIDFMEM.mjs";
28
28
  import "./token-goat-chunk-OSUFN2FV.mjs";
29
29
  import {
30
30
  installEpipeGuard
31
- } from "./token-goat-chunk-3H6FQT62.mjs";
31
+ } from "./token-goat-chunk-QIDLJGXZ.mjs";
32
32
  import "./token-goat-chunk-ERTXEKB6.mjs";
33
33
  import "./token-goat-chunk-Y4AFKTHK.mjs";
34
34
  import "./token-goat-chunk-GMOUBOX4.mjs";
package/docs/cli.md CHANGED
@@ -74,6 +74,7 @@ token-goat pdf-extract manual.pdf --pages 12-15 --layout --head 120
74
74
  | `token-goat imports "file"` | Show the import graph for a file one level deep. Accepts a comma-separated file list (`"a,b,c"`) to cover several files in one call, one clearly-headed block per file; extra space-separated file arguments are reported in a note naming that comma form instead of being silently dropped. `--grep <pattern>` only shows imports whose MODULE SPECIFIER matches this regex (literal substring if it is not valid regex), applied before `--json`'s truncation; when it matches nothing among real imports, the output names the active filter instead of reading like the file has no imports at all. |
75
75
  | `token-goat dep-docs <package>` | Extract one installed npm package's README, `package.json` metadata, and (if resolvable) a compact `.d.ts` signature outline, instead of grepping `node_modules`. |
76
76
  | `token-goat find "<query>"` | Find the FILES defining a symbol whose name matches a pattern: a case-insensitive substring scan over indexed symbol names, emitting the distinct file paths. When no name contains the pattern, falls back to an edit-distance match so a mistyped name still lands (`getUserr` → `getUser`) — the same ranking `Did you mean:` uses. The fallback runs only when the substring pass found nothing, so an exact match is never reordered or displaced, and a query near nothing still reports a clean miss instead of unrelated names. A recovered match names what it actually matched on stderr rather than silently answering for a name you didn't type; `--json` marks it with `fuzzy: true` and `matchedNames`, both absent on an exact hit. `--limit <n>` caps the file count. Matches on NAMES only — for meaning-based search over file content use `token-goat semantic`. |
77
+ | `token-goat locate <spec>` | Locate a symbol or landmark with exact line spans across the project or in a specific file. Supports `name` or `file::name`, with fuzzy edit-distance fallback when no exact match is found. `--file <path>` restricts search to a specific file; `--limit <n>` caps results; `--json` emits the shared envelope with exact line spans. |
77
78
  | `token-goat similar "file::symbol"` | Find the top-k symbols most similar to a given symbol, via full-text search over symbol names and bodies. Also accepts `read`'s `symbol@LINE` anchor to pick out an otherwise-ambiguous candidate. |
78
79
  | `token-goat test-for "file"` | Find test file(s) for an implementation file and list their test functions. `--json`'s `testFile` renders root-relative when a project root resolves, absolute when none does, matching plain-text output. `--json` emits the shared `{items, truncated, totalCount}` envelope — the same shape `symbol`/`refs`/`skeleton`/`outline --json` return, present whether or not truncation occurred, so a script never has to branch on shape. |
79
80
  | `token-goat dead` | Surface functions, methods, and classes with no recorded callers in the project index. Private names and common entry points (`main`, `app`, etc.) are excluded by default. `--include-private` lifts the underscore filter; `--kind` narrows to specific symbol types, comma-separated for a union (`--kind function,method`) — an unrecognized kind errors instead of silently reading as a clean codebase; `--top N` caps output; `--json` for structured output. `--exclude-tests` hides dead symbols DEFINED in a test file (opt-in — omitted, output is unchanged); prints a note naming how many were hidden. `--grep <pattern>` only shows dead symbols whose NAME matches this regex (literal substring if it is not valid regex), applied before `--top`'s slice; when it matches nothing among dead symbols that do exist, the output names the active filter instead of reading like a genuinely clean codebase. Results are a heuristic lead — dynamic dispatch and external callers are invisible to static indexing. `--json` emits both `file` and `filePath` with the identical value, root-relative when a project root resolves, absolute when none does, matching plain-text output; `file` is retained for this release only and will be removed in a future release, `filePath` is the spelling to migrate to (matching `symbol`/`types --json`). `--json` emits the shared `{items, truncated, totalCount}` envelope — the same shape `symbol`/`refs`/`skeleton`/`outline --json` return, present whether or not truncation occurred, so a script never has to branch on shape. |
@@ -147,7 +148,7 @@ token-goat pdf-extract manual.pdf --pages 12-15 --layout --head 120
147
148
  | `token-goat transcript <file>` | Slice a WebVTT/SRT transcript by `--speaker <name>`, `--from`/`--to <hh:mm:ss>`, and/or `--grep <pattern>` instead of a raw Read. |
148
149
  | `token-goat screenshot <url> <destPath>` | Capture a local headless-browser screenshot, shrunk the same way local image reads are (image-shrink pipeline). `--executable-path` overrides the Chrome/Chromium binary; `--width`/`--height` set the viewport (default 1280x800); `--full-page` captures the full scrollable page. Only `http:`/`https:` targets are allowed, and loopback/link-local/private/cloud-metadata addresses are refused by default (`screenshot.block_private_targets`, env `TOKEN_GOAT_SCREENSHOT_BLOCK_PRIVATE_TARGETS`) — see [Security, privacy, and uninstall](security.md) for what that check does and does not cover. |
149
150
  | `token-goat clean-cache` | Prune on-disk caches to their configured floor without waiting for the worker. |
150
- | `token-goat reclaim-index` | Shrink an oversized symbol index (`VACUUM` + WAL checkpoint). `--rebuild` also drops every derived row — files/symbols/refs/chunks — so the next `token-goat index` re-derives them under current parser rules, which is what actually reclaims space held by rows a since-fixed extractor wrote too large. Refuses to run while the worker daemon is writing to the index unless `--force`. `token-goat doctor` points here when `global.db` grows past 1 GB, and separately when it finds a stored symbol body above the parser's own size cap — a leftover from a since-fixed extractor bug, which only `--rebuild` can clear (a plain `VACUUM` reclaims freed pages but never deletes row content). |
151
+ | `token-goat reclaim-index` | Shrink an oversized symbol index (`VACUUM` + WAL checkpoint). `--rebuild` also drops every derived row — files/symbols/refs/chunks — so the next `token-goat index` re-derives them under current parser rules, which is what actually reclaims space held by rows a since-fixed extractor wrote too large. Refuses to run while the worker daemon is writing to the index unless `--force`. `token-goat doctor` points here when `global.db` grows past 1 GB and at least a tenth of it is free pages (it points to `project prune` instead for scratch files indexed under the OS temp dir), and separately when it finds a stored symbol body above the parser's own size cap — a leftover from a since-fixed extractor bug, which only `--rebuild` can clear (a plain `VACUUM` reclaims freed pages but never deletes row content). |
151
152
  | `token-goat prune-cache` | Manually trigger LRU eviction across all cache directories (images, bash, web, skills). |
152
153
  | `token-goat session-summary` | Compact one-liner about current session state — designed for orchestrators and multi-agent loops. |
153
154
  | `token-goat cache-audit` | Audit your Claude Code config for patterns that bust the prompt cache. |
@@ -173,7 +174,7 @@ token-goat pdf-extract manual.pdf --pages 12-15 --layout --head 120
173
174
  | `token-goat note set/get/unset/list/clear` | Persistent per-project notes stored as key-value pairs. Token-goat injects them at session start and after compaction so they survive conversation rollover. Use to pin decisions, constraints, or reminders that would otherwise vanish after compaction. `note list --json` for machine-readable output; `note clear` removes everything at once. |
174
175
  | `token-goat project list` | Show all project roots indexed by token-goat with their file counts. Roots on the blocklist appear tagged `[excluded]`. `--json` for structured output. |
175
176
  | `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. |
176
- | `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. |
177
+ | `token-goat project prune [--dry-run]` | Remove blocked/excluded roots that no longer exist on disk, and index rows for files under the OS temp dir. `--dry-run` previews removals without touching the config file. Useful after deleting or moving projects. |
177
178
  | `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. |
178
179
  | `token-goat upgrade` | Check for updates or upgrade token-goat to the latest version via npm. Pass `--check` to report status without installing. `--json` emits machine-readable version status. |
179
180
  | `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. |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "token-goat",
3
- "version": "2.9.16",
3
+ "version": "2.9.18",
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",