token-goat 2.9.26 → 2.9.28

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 (57) hide show
  1. package/README.md +28 -88
  2. package/SECURITY.md +14 -0
  3. package/dist/{token-goat-chunk-CQHY4SL7.mjs → token-goat-chunk-4JJBFT7V.mjs} +3 -3
  4. package/dist/token-goat-chunk-4JMWPWCS.mjs +96 -0
  5. package/dist/token-goat-chunk-4RN64ZND.mjs +954 -0
  6. package/dist/{token-goat-chunk-5M3NRJD2.mjs → token-goat-chunk-4STDIGYC.mjs} +2 -2
  7. package/dist/{token-goat-chunk-BH4ZVRB2.mjs → token-goat-chunk-5NNYIUGV.mjs} +2 -2
  8. package/dist/{token-goat-chunk-LHUP4DXZ.mjs → token-goat-chunk-66LODAUT.mjs} +1 -1
  9. package/dist/{token-goat-chunk-M6CVNHTW.mjs → token-goat-chunk-6CCQDAYY.mjs} +69 -566
  10. package/dist/{token-goat-chunk-62IHOU6L.mjs → token-goat-chunk-6DQNNFJ6.mjs} +5 -3
  11. package/dist/{token-goat-chunk-H2CPLEMP.mjs → token-goat-chunk-6SGLIPQB.mjs} +40 -20
  12. package/dist/{token-goat-chunk-MBDJR6HU.mjs → token-goat-chunk-6Z5ULNE5.mjs} +3570 -3970
  13. package/dist/token-goat-chunk-7MAFDRVR.mjs +47 -0
  14. package/dist/{token-goat-chunk-POOI26O6.mjs → token-goat-chunk-7R2XYKC5.mjs} +6 -5
  15. package/dist/{token-goat-chunk-E6B2UFDF.mjs → token-goat-chunk-BCN2YQC6.mjs} +10 -8
  16. package/dist/token-goat-chunk-BJBRGPBX.mjs +244 -0
  17. package/dist/{token-goat-chunk-TG5ZPD6B.mjs → token-goat-chunk-CBMY2FGI.mjs} +479 -544
  18. package/dist/{token-goat-chunk-LRIZ7K3F.mjs → token-goat-chunk-CC2PYJM7.mjs} +2 -1
  19. package/dist/{token-goat-chunk-7EPYB34H.mjs → token-goat-chunk-D5ICEPSB.mjs} +14 -14
  20. package/dist/token-goat-chunk-D7GHQKQD.mjs +30 -0
  21. package/dist/{token-goat-chunk-JALB3KWJ.mjs → token-goat-chunk-EL7M26WP.mjs} +7623 -8462
  22. package/dist/{token-goat-chunk-ZZT6PVB3.mjs → token-goat-chunk-F7QZRIWM.mjs} +69 -20
  23. package/dist/{token-goat-chunk-FLEYOCAS.mjs → token-goat-chunk-FSTWAOW6.mjs} +3 -3
  24. package/dist/token-goat-chunk-GGLEE35R.mjs +564 -0
  25. package/dist/{token-goat-chunk-IKZLSRII.mjs → token-goat-chunk-HUDC5GS5.mjs} +2 -2
  26. package/dist/{token-goat-chunk-43JFN26X.mjs → token-goat-chunk-HXNAC4ZF.mjs} +22 -964
  27. package/dist/token-goat-chunk-I7CB3X4K.mjs +1379 -0
  28. package/dist/{token-goat-chunk-GKZ2US5B.mjs → token-goat-chunk-IFORDBGC.mjs} +32 -27
  29. package/dist/{token-goat-chunk-CXBOFPF4.mjs → token-goat-chunk-IHKZ3QNA.mjs} +26 -15
  30. package/dist/token-goat-chunk-IZXKVT4A.mjs +711 -0
  31. package/dist/{token-goat-chunk-PAV6NNY7.mjs → token-goat-chunk-JXK2HQXS.mjs} +22 -8
  32. package/dist/{token-goat-chunk-6E2IDLHF.mjs → token-goat-chunk-L45ZQRT7.mjs} +1 -1
  33. package/dist/{token-goat-chunk-MDV5VWF4.mjs → token-goat-chunk-L7SDVGBT.mjs} +196 -14
  34. package/dist/token-goat-chunk-LEG3JNJ6.mjs +139 -0
  35. package/dist/{token-goat-chunk-7SXPJOSZ.mjs → token-goat-chunk-M56BWPBN.mjs} +1 -1
  36. package/dist/{token-goat-chunk-G6EYO5CD.mjs → token-goat-chunk-MAMQPC43.mjs} +2 -87
  37. package/dist/{token-goat-chunk-VVROQBQJ.mjs → token-goat-chunk-MMLMALWK.mjs} +5 -3
  38. package/dist/{token-goat-chunk-D2X7XN5M.mjs → token-goat-chunk-MVSWBNMD.mjs} +7 -231
  39. package/dist/{token-goat-chunk-C2PG4K5D.mjs → token-goat-chunk-NILVK7XM.mjs} +4 -4
  40. package/dist/{token-goat-chunk-BAWKGODL.mjs → token-goat-chunk-Q4S3UQGO.mjs} +1 -1
  41. package/dist/{token-goat-chunk-MWID3YOY.mjs → token-goat-chunk-QM76S6YS.mjs} +3 -3
  42. package/dist/{token-goat-chunk-4GEDH2WU.mjs → token-goat-chunk-RJH64KQH.mjs} +81 -70
  43. package/dist/{token-goat-chunk-STYRLQIW.mjs → token-goat-chunk-RSS3TLXU.mjs} +2 -2
  44. package/dist/{token-goat-chunk-5BQ7W7D7.mjs → token-goat-chunk-SJEKA7Y2.mjs} +21 -16
  45. package/dist/{token-goat-chunk-RIB6XY2V.mjs → token-goat-chunk-UBUKHVYW.mjs} +1 -1
  46. package/dist/{token-goat-chunk-7Y6GWTII.mjs → token-goat-chunk-VE27HPIF.mjs} +14 -10
  47. package/dist/{token-goat-chunk-RH27ZSPF.mjs → token-goat-chunk-VWZKZPSR.mjs} +100 -23
  48. package/dist/{token-goat-chunk-L45IWWMC.mjs → token-goat-chunk-XI2VFAV6.mjs} +6 -6
  49. package/dist/{token-goat-chunk-WNBSCSAI.mjs → token-goat-chunk-XKY4VKJ4.mjs} +1 -1
  50. package/dist/{token-goat-chunk-L43RKNHA.mjs → token-goat-chunk-YWSP6IU7.mjs} +1 -1
  51. package/dist/token-goat-hook.mjs +23 -17
  52. package/dist/token-goat.core.mjs +32 -27
  53. package/docs/cli.md +32 -21
  54. package/docs/install.md +2 -0
  55. package/package.json +1 -1
  56. package/dist/token-goat-chunk-ADMR3QVF.mjs +0 -41
  57. package/dist/token-goat-chunk-MZTO4FXH.mjs +0 -26
@@ -3,14 +3,14 @@ const require = __cjsRequire(import.meta.url);
3
3
  import {
4
4
  findLatestTranscript,
5
5
  readFileLines
6
- } from "./token-goat-chunk-62IHOU6L.mjs";
6
+ } from "./token-goat-chunk-6DQNNFJ6.mjs";
7
7
  import {
8
8
  copilotCliUserRoot,
9
9
  readCopilotMcpTools
10
- } from "./token-goat-chunk-VVROQBQJ.mjs";
10
+ } from "./token-goat-chunk-MMLMALWK.mjs";
11
11
  import {
12
12
  canonicalize
13
- } from "./token-goat-chunk-6E2IDLHF.mjs";
13
+ } from "./token-goat-chunk-L45ZQRT7.mjs";
14
14
  import {
15
15
  init_define_import_meta_env
16
16
  } from "./token-goat-chunk-A37V4PBF.mjs";
@@ -213,6 +213,11 @@ function readNumber(source, key) {
213
213
  const value = source[key];
214
214
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
215
215
  }
216
+ function unusedMcpServers(report) {
217
+ const calls = report.mcpCalls;
218
+ return calls === null ? null : report.mcpTools.servers.filter((server) => (calls[server.serverName] ?? 0) === 0);
219
+ }
220
+ var MCP_DISABLE_NOTE = "'copilot mcp enable <name>' restores one, and '--disable-mcp-server <name>' drops one for a single run instead.";
216
221
  function buildCopilotWasteReport(eventsPath) {
217
222
  const report = {
218
223
  sessionPath: eventsPath,
@@ -224,14 +229,14 @@ function buildCopilotWasteReport(eventsPath) {
224
229
  hookRecordBytes: 0,
225
230
  // Measured off the file rather than off a string of its contents: an event log large enough for this number to matter is exactly the one readFileSync cannot return, since V8 caps a string at about 512 MB.
226
231
  totalEventBytes: fs.statSync(eventsPath).size,
227
- // Read through the real resolution chain rather than passed in. Copilot's
228
- // own COPILOT_CACHE_HOME override is what tests point at a fixture, so the
229
- // shipping path is the tested path and there is no seam here that only a
230
- // test ever supplies.
231
- mcpTools: readCopilotMcpTools()
232
+ // Read through the real resolution chain rather than passed in. Copilot's own COPILOT_CACHE_HOME override is what tests point at a fixture, so the shipping path is the tested path and there is no seam here that only a test ever supplies.
233
+ mcpTools: readCopilotMcpTools(),
234
+ mcpCalls: null
232
235
  };
233
236
  const classes = /* @__PURE__ */ new Map();
234
237
  const seen = /* @__PURE__ */ new Map();
238
+ const mcpCalls = /* @__PURE__ */ new Map();
239
+ let toolExecutions = 0;
235
240
  for (const line of readFileLines(eventsPath)) {
236
241
  const trimmed = line.trim();
237
242
  if (trimmed === "") continue;
@@ -266,6 +271,12 @@ function buildCopilotWasteReport(eventsPath) {
266
271
  });
267
272
  continue;
268
273
  }
274
+ if (type === "tool.execution_start") {
275
+ toolExecutions += 1;
276
+ const server = data["mcpServerName"];
277
+ if (typeof server === "string") mcpCalls.set(server, (mcpCalls.get(server) ?? 0) + 1);
278
+ continue;
279
+ }
269
280
  if (type !== "user.message") continue;
270
281
  const transformed = typeof data["transformedContent"] === "string" ? data["transformedContent"] : "";
271
282
  if (transformed === "") continue;
@@ -293,6 +304,7 @@ function buildCopilotWasteReport(eventsPath) {
293
304
  }
294
305
  }
295
306
  report.blocks = [...classes.values()].sort((a, b) => b.bytes - a.bytes);
307
+ report.mcpCalls = toolExecutions === 0 ? null : Object.fromEntries(mcpCalls);
296
308
  return report;
297
309
  }
298
310
 
@@ -300,5 +312,7 @@ export {
300
312
  isCopilotTranscript,
301
313
  findLatestCopilotSession,
302
314
  findProjectSession,
315
+ unusedMcpServers,
316
+ MCP_DISABLE_NOTE,
303
317
  buildCopilotWasteReport
304
318
  };
@@ -21,7 +21,7 @@ init_define_import_meta_env();
21
21
  import { createRequire } from "node:module";
22
22
  function resolveVersion() {
23
23
  if (true) {
24
- return "2.9.26";
24
+ return "2.9.28";
25
25
  }
26
26
  const require2 = createRequire(import.meta.url);
27
27
  const pkg = require2("../package.json");
@@ -1,28 +1,35 @@
1
1
  import { createRequire as __cjsRequire } from 'node:module';
2
2
  const require = __cjsRequire(import.meta.url);
3
3
  import {
4
- WEB_FETCH_KEY_SEP,
5
4
  computeAdaptiveBudget,
6
5
  getContextPressure,
7
- getSessionBashOutputs,
8
- getSessionBashReruns,
9
- getSessionFiles,
10
- getSessionWebFetches,
11
6
  isNoisePath,
12
7
  listSiblingSessionStates,
13
8
  loadSessionCache
14
- } from "./token-goat-chunk-M6CVNHTW.mjs";
9
+ } from "./token-goat-chunk-6CCQDAYY.mjs";
10
+ import {
11
+ WEB_FETCH_KEY_SEP,
12
+ getSessionBashOutputs,
13
+ getSessionBashReruns,
14
+ getSessionFiles,
15
+ getSessionWebFetches
16
+ } from "./token-goat-chunk-GGLEE35R.mjs";
15
17
  import {
16
18
  getBashOutput
17
- } from "./token-goat-chunk-43JFN26X.mjs";
19
+ } from "./token-goat-chunk-HXNAC4ZF.mjs";
18
20
  import {
21
+ findProject,
19
22
  loadConfig
20
- } from "./token-goat-chunk-LRIZ7K3F.mjs";
23
+ } from "./token-goat-chunk-CC2PYJM7.mjs";
21
24
  import {
25
+ atomicWriteText,
26
+ dataDir,
27
+ ensureDirSync,
22
28
  foldPath,
23
29
  runGit,
24
- toKB
25
- } from "./token-goat-chunk-6E2IDLHF.mjs";
30
+ toKB,
31
+ withFileLock
32
+ } from "./token-goat-chunk-L45ZQRT7.mjs";
26
33
  import {
27
34
  displaySafePath,
28
35
  displaySafeText,
@@ -35,6 +42,174 @@ import {
35
42
  // src/manifest.ts
36
43
  init_define_import_meta_env();
37
44
  import { spawnSync } from "node:child_process";
45
+
46
+ // src/project_memory.ts
47
+ init_define_import_meta_env();
48
+ import * as fs from "node:fs";
49
+ import * as path from "node:path";
50
+ var MAX_ENTRIES = 30;
51
+ var MAX_VALUE_LEN = 300;
52
+ var MAX_TOTAL_CHARS = 4e3;
53
+ var KEY_RE = /^[A-Za-z0-9_-]{1,80}$/;
54
+ function ordinal(a, b) {
55
+ return a < b ? -1 : a > b ? 1 : 0;
56
+ }
57
+ function memoryPath(projectHash) {
58
+ return path.join(dataDir(), "projects", `${projectHash}_memory.toml`);
59
+ }
60
+ function validateKey(key) {
61
+ if (!KEY_RE.test(key)) {
62
+ throw new Error(
63
+ `Invalid memory key ${JSON.stringify(key)}: use only letters, digits, hyphens, underscores (max 80 chars)`
64
+ );
65
+ }
66
+ }
67
+ function parseTOML(content) {
68
+ const result = {};
69
+ for (const line of content.split("\n")) {
70
+ const trimmed = line.trim();
71
+ if (!trimmed || trimmed.startsWith("#")) {
72
+ continue;
73
+ }
74
+ const match = trimmed.match(/^([A-Za-z0-9_-]+)\s*=\s*"(.*)"\s*$/);
75
+ if (match) {
76
+ const [, key, value] = match;
77
+ if (key && value !== void 0) {
78
+ const unescaped = value.replace(/\\([\\nrt"])/g, (_, c) => {
79
+ switch (c) {
80
+ case "\\":
81
+ return "\\";
82
+ case "n":
83
+ return "\n";
84
+ case "r":
85
+ return "\r";
86
+ case '"':
87
+ return '"';
88
+ default:
89
+ return _;
90
+ }
91
+ });
92
+ result[key] = unescaped;
93
+ }
94
+ }
95
+ }
96
+ return result;
97
+ }
98
+ function loadRaw(filePath) {
99
+ try {
100
+ if (!fs.existsSync(filePath)) {
101
+ return {};
102
+ }
103
+ const content = fs.readFileSync(filePath, "utf-8");
104
+ return parseTOML(content);
105
+ } catch {
106
+ return {};
107
+ }
108
+ }
109
+ function save(filePath, entries) {
110
+ const lines = [];
111
+ const sorted = Object.entries(entries).sort(([a], [b]) => ordinal(a, b));
112
+ for (const [k, v] of sorted) {
113
+ const escaped = v.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\r/g, "\\r").replace(/\n/g, "\\n");
114
+ lines.push(`${k} = "${escaped}"`);
115
+ }
116
+ const content = lines.length > 0 ? lines.join("\n") + "\n" : "";
117
+ const dir = path.dirname(filePath);
118
+ ensureDirSync(dir);
119
+ atomicWriteText(filePath, content);
120
+ }
121
+ function loadEntries(projectHash) {
122
+ return loadRaw(memoryPath(projectHash));
123
+ }
124
+ function setEntry(projectHash, key, value) {
125
+ validateKey(key);
126
+ const p = memoryPath(projectHash);
127
+ const dir = path.dirname(p);
128
+ ensureDirSync(dir);
129
+ const doSet = () => {
130
+ const entries = loadRaw(p);
131
+ const isNewKey = !(key in entries);
132
+ if (isNewKey && Object.keys(entries).length >= MAX_ENTRIES) {
133
+ const keysToKeep = MAX_ENTRIES - 1;
134
+ const allKeys = Object.keys(entries).sort((a, b) => ordinal(a, b));
135
+ for (const k of allKeys.slice(keysToKeep)) {
136
+ delete entries[k];
137
+ }
138
+ }
139
+ entries[key] = value;
140
+ save(p, entries);
141
+ return true;
142
+ };
143
+ if (withFileLock(`${p}.lock`, doSet) === void 0) doSet();
144
+ }
145
+ function unsetEntry(projectHash, key) {
146
+ validateKey(key);
147
+ const p = memoryPath(projectHash);
148
+ const doUnset = () => {
149
+ const entries = loadRaw(p);
150
+ if (key in entries) {
151
+ delete entries[key];
152
+ save(p, entries);
153
+ }
154
+ return true;
155
+ };
156
+ if (withFileLock(`${p}.lock`, doUnset) === void 0) doUnset();
157
+ }
158
+ function clearAll(projectHash) {
159
+ const p = memoryPath(projectHash);
160
+ const doClear = () => {
161
+ if (fs.existsSync(p)) {
162
+ save(p, {});
163
+ }
164
+ return true;
165
+ };
166
+ if (withFileLock(`${p}.lock`, doClear) === void 0) doClear();
167
+ }
168
+ function buildInjection(projectHash) {
169
+ try {
170
+ const entries = loadEntries(projectHash);
171
+ if (Object.keys(entries).length === 0) {
172
+ return null;
173
+ }
174
+ const header = '### Project notes (`token-goat note set <key> "<finding>"`)';
175
+ const lines = [header];
176
+ let total = header.length;
177
+ let skipped = 0;
178
+ const entries_list = Object.entries(entries).sort(([a], [b]) => ordinal(a, b)).slice(0, MAX_ENTRIES);
179
+ for (const [key, val] of entries_list) {
180
+ const display = val.length <= MAX_VALUE_LEN ? val : val.slice(0, MAX_VALUE_LEN) + "\u2026";
181
+ const line = `- **${key}**: ${display}`;
182
+ if (total + line.length + 1 > MAX_TOTAL_CHARS) {
183
+ skipped++;
184
+ continue;
185
+ }
186
+ lines.push(line);
187
+ total += line.length + 1;
188
+ }
189
+ if (skipped > 0) {
190
+ while (lines.length > 1 && total + `- (+${skipped} more memory entries omitted -- total size limit reached)`.length + 1 > MAX_TOTAL_CHARS) {
191
+ const popped = lines.pop();
192
+ total -= popped.length + 1;
193
+ skipped++;
194
+ }
195
+ lines.push(`- (+${skipped} more memory entries omitted -- total size limit reached)`);
196
+ }
197
+ return lines.join("\n");
198
+ } catch {
199
+ return null;
200
+ }
201
+ }
202
+ function projectNotesFor(cwd) {
203
+ if (cwd === void 0) return null;
204
+ try {
205
+ const project = findProject(cwd);
206
+ return project === null ? null : buildInjection(project.hash);
207
+ } catch {
208
+ return null;
209
+ }
210
+ }
211
+
212
+ // src/manifest.ts
38
213
  var MEM_EPOCH_TIMEOUT_MS = 800;
39
214
  var MAX_ROWS = 40;
40
215
  function appendCappedSection(lines, header, rows, cap) {
@@ -115,6 +290,8 @@ function buildManifestParts(sessionId, cwd, transcriptPath) {
115
290
  for (let i = 0; i < Math.min(rows.length, MAX_ROWS); i++) fileRows.push({ path: displaySafePath(entries[i].path), row: rows[i] });
116
291
  };
117
292
  appendFileSection("### Edited files", editedFiles, (entry) => `- ${displaySafePath(entry.path)}`);
293
+ const notes = projectNotesFor(cwd);
294
+ if (notes !== null) lines.push("", notes);
118
295
  appendFileSection("### Surgically read files (symbol/section reads, never read whole)", symbolOnlyFiles, renderSymbolReadRow);
119
296
  appendFileSection("### Read files", readFiles, renderReadRow);
120
297
  appendCappedSection(lines, "### Web URLs fetched", webFetches.map(([key, cacheId]) => renderWebFetchRow(key, cacheId)), MAX_ROWS);
@@ -123,10 +300,10 @@ function buildManifestParts(sessionId, cwd, transcriptPath) {
123
300
  const text = capManifestChars(lines.join("\n"), sessionId, cwd, transcriptPath);
124
301
  const seen = /* @__PURE__ */ new Set();
125
302
  const printed = [];
126
- for (const { path, row } of fileRows) {
127
- if (seen.has(path) || !(text.includes(row + "\n") || text.endsWith(row))) continue;
128
- seen.add(path);
129
- printed.push(path);
303
+ for (const { path: path2, row } of fileRows) {
304
+ if (seen.has(path2) || !(text.includes(row + "\n") || text.endsWith(row))) continue;
305
+ seen.add(path2);
306
+ printed.push(path2);
130
307
  }
131
308
  return { text, printed };
132
309
  }
@@ -238,6 +415,11 @@ function summaryBudgetDirective(budgetChars) {
238
415
  }
239
416
 
240
417
  export {
418
+ loadEntries,
419
+ setEntry,
420
+ unsetEntry,
421
+ clearAll,
422
+ projectNotesFor,
241
423
  renderWebFetchRow,
242
424
  manifestPrintedPaths,
243
425
  buildManifest,
@@ -0,0 +1,139 @@
1
+ import { createRequire as __cjsRequire } from 'node:module';
2
+ const require = __cjsRequire(import.meta.url);
3
+ import {
4
+ CONTENT_DELIVERED_REASON,
5
+ classifyToolError,
6
+ errorPrefix
7
+ } from "./token-goat-chunk-I7CB3X4K.mjs";
8
+ import "./token-goat-chunk-IZXKVT4A.mjs";
9
+ import "./token-goat-chunk-RJH64KQH.mjs";
10
+ import "./token-goat-chunk-MMLMALWK.mjs";
11
+ import "./token-goat-chunk-GGLEE35R.mjs";
12
+ import "./token-goat-chunk-4JMWPWCS.mjs";
13
+ import "./token-goat-chunk-4RN64ZND.mjs";
14
+ import "./token-goat-chunk-MVSWBNMD.mjs";
15
+ import "./token-goat-chunk-M56BWPBN.mjs";
16
+ import "./token-goat-chunk-CC2PYJM7.mjs";
17
+ import "./token-goat-chunk-EEIDFMEM.mjs";
18
+ import "./token-goat-chunk-OSUFN2FV.mjs";
19
+ import "./token-goat-chunk-L45ZQRT7.mjs";
20
+ import {
21
+ displaySafeText
22
+ } from "./token-goat-chunk-NMTKNYGF.mjs";
23
+ import "./token-goat-chunk-GMOUBOX4.mjs";
24
+ import {
25
+ init_define_import_meta_env
26
+ } from "./token-goat-chunk-A37V4PBF.mjs";
27
+
28
+ // src/tool_error_census.ts
29
+ init_define_import_meta_env();
30
+ var UNKNOWN_PREFIX_LIMIT = 20;
31
+ var REASONS_PER_ROW = 4;
32
+ var UNKNOWN_MODEL = "(unknown)";
33
+ function tally(map, name) {
34
+ let row = map.get(name);
35
+ if (row === void 0) {
36
+ row = { name, calls: 0, errors: 0, unknown: 0, delivered: 0, expected: {} };
37
+ map.set(name, row);
38
+ }
39
+ return row;
40
+ }
41
+ function byCodeUnit(a, b) {
42
+ return a < b ? -1 : a > b ? 1 : 0;
43
+ }
44
+ function sortTallies(map) {
45
+ return [...map.values()].sort((a, b) => b.errors - a.errors || b.calls - a.calls || byCodeUnit(a.name, b.name));
46
+ }
47
+ function newToolErrorAccumulator() {
48
+ const tools = /* @__PURE__ */ new Map();
49
+ const models = /* @__PURE__ */ new Map();
50
+ const unknownPrefixes = /* @__PURE__ */ new Map();
51
+ return {
52
+ countCall(tool, model) {
53
+ tally(tools, tool).calls += 1;
54
+ tally(models, model ?? UNKNOWN_MODEL).calls += 1;
55
+ },
56
+ countError(tool, model, errorText, flags) {
57
+ const verdict = classifyToolError(tool, errorText, flags);
58
+ const rows = [tally(tools, tool), tally(models, model ?? UNKNOWN_MODEL)];
59
+ if (verdict.reason === CONTENT_DELIVERED_REASON) {
60
+ for (const row of rows) row.delivered += 1;
61
+ return;
62
+ }
63
+ for (const row of rows) {
64
+ row.errors += 1;
65
+ if (verdict.kind === "unknown") row.unknown += 1;
66
+ else row.expected[verdict.reason] = (row.expected[verdict.reason] ?? 0) + 1;
67
+ }
68
+ if (verdict.kind !== "unknown") return;
69
+ const prefix = errorPrefix(errorText);
70
+ const key = `${tool}\0${prefix}`;
71
+ const hit = unknownPrefixes.get(key);
72
+ if (hit === void 0) unknownPrefixes.set(key, { tool, prefix, count: 1 });
73
+ else hit.count += 1;
74
+ },
75
+ finish() {
76
+ const topUnknown = [...unknownPrefixes.values()].sort((a, b) => b.count - a.count || byCodeUnit(a.tool, b.tool) || byCodeUnit(a.prefix, b.prefix)).slice(0, UNKNOWN_PREFIX_LIMIT);
77
+ return { byTool: sortTallies(tools), byModel: sortTallies(models), unknownPrefixes: topUnknown };
78
+ }
79
+ };
80
+ }
81
+ function fmt(n) {
82
+ return n.toLocaleString("en-US");
83
+ }
84
+ function rate(errors, calls) {
85
+ return calls === 0 ? "-" : `${(errors / calls * 100).toFixed(1)}%`;
86
+ }
87
+ function reasonsCell(expected) {
88
+ const ranked = Object.entries(expected).sort((a, b) => b[1] - a[1] || byCodeUnit(a[0], b[0]));
89
+ const shown = ranked.slice(0, REASONS_PER_ROW).map(([reason, n]) => `${reason} ${fmt(n)}`);
90
+ const rest = ranked.slice(REASONS_PER_ROW).reduce((sum, [, n]) => sum + n, 0);
91
+ if (rest > 0) shown.push(`+${fmt(rest)} more`);
92
+ return shown.join(", ");
93
+ }
94
+ function columnWidth(names, header) {
95
+ return names.reduce((w, n) => Math.max(w, n.length), header.length);
96
+ }
97
+ function tableLines(rows) {
98
+ const failing = rows.filter((r) => r.errors > 0);
99
+ const names = failing.map((r) => displaySafeText(r.name));
100
+ const width = columnWidth(names, "name");
101
+ const out = [`${"name".padEnd(width)} ${"calls".padStart(9)} ${"errors".padStart(7)} ${"rate".padStart(6)} ${"unknown".padStart(7)} expected, by reason`];
102
+ failing.forEach((r, i) => out.push(`${names[i].padEnd(width)} ${fmt(r.calls).padStart(9)} ${fmt(r.errors).padStart(7)} ${rate(r.errors, r.calls).padStart(6)} ${fmt(r.unknown).padStart(7)} ${reasonsCell(r.expected)}`.trimEnd()));
103
+ if (failing.length < rows.length) out.push(`(${fmt(rows.length - failing.length)} more ran without an error)`);
104
+ return out;
105
+ }
106
+ function deliveredLines(rows) {
107
+ const delivering = rows.filter((r) => r.delivered > 0).sort((a, b) => b.delivered - a.delivered || byCodeUnit(a.name, b.name));
108
+ if (delivering.length === 0) return [];
109
+ const total = delivering.reduce((sum, r) => sum + r.delivered, 0);
110
+ return [`Content delivered by a token-goat deny, not counted as errors: ${fmt(total)} (${delivering.map((r) => `${displaySafeText(r.name)} ${fmt(r.delivered)}`).join(", ")}).`];
111
+ }
112
+ function formatToolErrorCensus(census, scope) {
113
+ const calls = census.byTool.reduce((sum, r) => sum + r.calls, 0);
114
+ const errors = census.byTool.reduce((sum, r) => sum + r.errors, 0);
115
+ const unknown = census.byTool.reduce((sum, r) => sum + r.unknown, 0);
116
+ const toolWidth = columnWidth(census.unknownPrefixes.map((p) => displaySafeText(p.tool)), "");
117
+ const window = scope.windowDays === 0 ? "all time" : `transcripts modified in the last ${fmt(scope.windowDays)} days`;
118
+ const lines = [
119
+ `# Tool errors (${fmt(scope.filesScanned)} transcripts, ${window})`,
120
+ `Calls ${fmt(calls)}, errors ${fmt(errors)} (${rate(errors, calls)}), unknown ${fmt(unknown)}. Expected means a named failure shape in tool_error_class.ts; unknown is everything else.`,
121
+ ...deliveredLines(census.byTool),
122
+ "",
123
+ "## By tool",
124
+ ...tableLines(census.byTool),
125
+ "",
126
+ "## By model",
127
+ ...tableLines(census.byModel),
128
+ "",
129
+ "## Top unknown error prefixes (paths, quoted text and digits masked)",
130
+ ...census.unknownPrefixes.map((p) => `${fmt(p.count).padStart(7)} ${displaySafeText(p.tool).padEnd(toolWidth)} ${displaySafeText(p.prefix)}`)
131
+ ];
132
+ if (census.unknownPrefixes.length === 0) lines.push("(none)");
133
+ return lines.join("\n");
134
+ }
135
+ export {
136
+ UNKNOWN_PREFIX_LIMIT,
137
+ formatToolErrorCensus,
138
+ newToolErrorAccumulator
139
+ };
@@ -15,7 +15,7 @@ import {
15
15
  foldCase,
16
16
  foldPath,
17
17
  sleepSync
18
- } from "./token-goat-chunk-6E2IDLHF.mjs";
18
+ } from "./token-goat-chunk-L45ZQRT7.mjs";
19
19
  import {
20
20
  safeJoin
21
21
  } from "./token-goat-chunk-NMTKNYGF.mjs";
@@ -1,13 +1,8 @@
1
1
  import { createRequire as __cjsRequire } from 'node:module';
2
2
  const require = __cjsRequire(import.meta.url);
3
3
  import {
4
- classifyContent,
5
- guardDivisor,
6
- stripAnsiEscapes
7
- } from "./token-goat-chunk-D2X7XN5M.mjs";
8
- import {
9
- safeSlice
10
- } from "./token-goat-chunk-6E2IDLHF.mjs";
4
+ estimateTokensFromLength
5
+ } from "./token-goat-chunk-4JMWPWCS.mjs";
11
6
  import {
12
7
  displaySafeText
13
8
  } from "./token-goat-chunk-NMTKNYGF.mjs";
@@ -25,82 +20,6 @@ function claudeConfigDir(homeDir = os.homedir()) {
25
20
  return path.join(homeDir, ".claude");
26
21
  }
27
22
 
28
- // src/overflow_guard.ts
29
- init_define_import_meta_env();
30
- function estimateTokensFromLength(length, cls = "text") {
31
- return Math.max(1, Math.floor(Math.max(0, length) / guardDivisor(cls)) + 1);
32
- }
33
- function estimateTokens(text) {
34
- const stripped = stripAnsiEscapes(text);
35
- return estimateTokensFromLength(stripped.length, classifyContent(stripped));
36
- }
37
- function trimToBudget(text, budgetTokens, command) {
38
- const markerMarginTokens = 64;
39
- const strippedAll = stripAnsiEscapes(text);
40
- const contentClass = classifyContent(strippedAll);
41
- const totalTokens = estimateTokensFromLength(strippedAll.length, contentClass);
42
- if (totalTokens <= budgetTokens) {
43
- return text;
44
- }
45
- const lines = text.split("\n");
46
- if (lines.length > 1 && lines[lines.length - 1] === "") lines.pop();
47
- const totalLines = lines.length;
48
- const bodyBudget = Math.max(1, budgetTokens - markerMarginTokens);
49
- const charBudget = bodyBudget * guardDivisor(contentClass);
50
- const kept = [];
51
- let used = 0;
52
- for (const ln of lines) {
53
- const stripped = stripAnsiEscapes(ln);
54
- const cost = ln.length + 1;
55
- if (kept.length === 0 && cost > charBudget) {
56
- const truncated = safeSlice(stripped, charBudget);
57
- kept.push(truncated);
58
- break;
59
- }
60
- if (kept.length > 0 && used + cost > charBudget) {
61
- break;
62
- }
63
- kept.push(ln);
64
- used += cost;
65
- }
66
- const shown = kept.length;
67
- const hint = getHintFor(command);
68
- const marker = `[token-goat: output capped at ~${budgetTokens} tokens to protect context \u2014 showing ${shown} of ${totalLines} lines. ${hint}]`;
69
- return kept.join("\n") + "\n" + marker;
70
- }
71
- function capJsonRows(items, budgetTokens) {
72
- const totalCount = items.length;
73
- const charBudget = Math.max(1, budgetTokens * guardDivisor());
74
- const kept = [];
75
- let used = 0;
76
- for (const item of items) {
77
- const cost = JSON.stringify(item).length + 2;
78
- if (kept.length > 0 && used + cost > charBudget) break;
79
- kept.push(item);
80
- used += cost;
81
- }
82
- return { items: kept, truncated: kept.length < totalCount, totalCount };
83
- }
84
- function getHintFor(command) {
85
- const cmd = (command || "").toLowerCase().trim();
86
- if (cmd === "symbol") {
87
- return "Request a specific method (file.py::Class.method) or use --json for structured access.";
88
- }
89
- if (cmd === "heading" || cmd === "section") {
90
- return "Request a narrower sub-heading, e.g. 'doc.md::Section#2'.";
91
- }
92
- if (cmd === "lines") {
93
- return "Request a smaller line range, e.g. 'file.py@100-150'.";
94
- }
95
- if (cmd === "bash-output" || cmd === "web-output") {
96
- return "Use --grep PATTERN, --section HEADING, or --tail N to narrow the cached output.";
97
- }
98
- if (cmd === "semantic") {
99
- return "Narrow your query text or pass --limit to reduce the number of matches returned.";
100
- }
101
- return "Narrow your query or raise overflow_guard max_tokens in config.";
102
- }
103
-
104
23
  // src/resident_context.ts
105
24
  init_define_import_meta_env();
106
25
  import * as fs from "node:fs";
@@ -307,10 +226,6 @@ function accumulateResidentLines(lines) {
307
226
 
308
227
  export {
309
228
  claudeConfigDir,
310
- estimateTokensFromLength,
311
- estimateTokens,
312
- trimToBudget,
313
- capJsonRows,
314
229
  createResidentContextStats,
315
230
  accumulateResidentLine,
316
231
  summarizeResidentContext,
@@ -2,10 +2,10 @@ import { createRequire as __cjsRequire } from 'node:module';
2
2
  const require = __cjsRequire(import.meta.url);
3
3
  import {
4
4
  estimateTokensFromLength
5
- } from "./token-goat-chunk-G6EYO5CD.mjs";
5
+ } from "./token-goat-chunk-4JMWPWCS.mjs";
6
6
  import {
7
7
  loadConfig
8
- } from "./token-goat-chunk-LRIZ7K3F.mjs";
8
+ } from "./token-goat-chunk-CC2PYJM7.mjs";
9
9
  import {
10
10
  atomicWriteText,
11
11
  backupFile,
@@ -21,7 +21,7 @@ import {
21
21
  upsertDelimitedBlock,
22
22
  withInstallScope,
23
23
  writeIfDifferent
24
- } from "./token-goat-chunk-6E2IDLHF.mjs";
24
+ } from "./token-goat-chunk-L45ZQRT7.mjs";
25
25
  import {
26
26
  init_define_import_meta_env
27
27
  } from "./token-goat-chunk-A37V4PBF.mjs";
@@ -51,6 +51,7 @@ function buildGuidanceBody(fallbackToolClause, opts = {}) {
51
51
  '- not knowing which command answers a question you can already state in words \u2192 `answer "<question>"`, which resolves the subject in the index, runs the right command, prints `via: <command>`, and refuses rather than guess',
52
52
  '- a shell text search with context flags to find a function body \u2192 `read "file::symbol"`',
53
53
  '- paging one function with view/view_range \u2192 `read "file::symbol"`',
54
+ "- a read refused by a `[tg]` deny, retried with a shifted range or a different read tool \u2192 the command the deny names",
54
55
  '- reading a symbol plus chasing its callers and containing doc section as separate reads \u2192 `brief "file::symbol"`',
55
56
  '- reading one heading of a large doc \u2192 `section "file::Heading"`',
56
57
  "- searching for a symbol's callers \u2192 `refs file::symbol --callers`",
@@ -1339,6 +1340,7 @@ export {
1339
1340
  foldToolName,
1340
1341
  COPILOT_CLI_TOOL_NAME_MAP,
1341
1342
  MATERIALIZE_SHRUNK_IMAGE_JS,
1343
+ COPILOT_CLI_HOOK_SCRIPT,
1342
1344
  buildGuidanceBody,
1343
1345
  skillDescriptionLine,
1344
1346
  buildGuidanceBlock,