token-goat 2.6.36 → 2.8.0

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 (34) hide show
  1. package/README.md +31 -5
  2. package/SECURITY.md +46 -17
  3. package/dist/{token-goat-chunk-UFOVM7ZN.mjs → token-goat-chunk-2F6TFBZE.mjs} +1274 -367
  4. package/dist/token-goat-chunk-324QOJYZ.mjs +91 -0
  5. package/dist/{token-goat-chunk-V465YKOR.mjs → token-goat-chunk-44Y77VHR.mjs} +365 -23
  6. package/dist/{token-goat-chunk-CNDOJ3ZP.mjs → token-goat-chunk-4KZILRZN.mjs} +2 -2
  7. package/dist/token-goat-chunk-5CVKO3DA.mjs +185 -0
  8. package/dist/token-goat-chunk-A62K4XW2.mjs +23 -0
  9. package/dist/{token-goat-chunk-VYMGEVZS.mjs → token-goat-chunk-AO6MFFTW.mjs} +317 -11
  10. package/dist/{token-goat-chunk-65BKISIS.mjs → token-goat-chunk-FBGBTICM.mjs} +4 -4
  11. package/dist/{token-goat-chunk-LN6OUHTV.mjs → token-goat-chunk-J35AKWEQ.mjs} +5 -5
  12. package/dist/{token-goat-hook-chunk-BDR6C6IE.mjs → token-goat-chunk-LVCBDJVE.mjs} +318 -51
  13. package/dist/token-goat-chunk-R4SR7MQY.mjs +486 -0
  14. package/dist/{token-goat-chunk-IYTVE6KN.mjs → token-goat-chunk-SRAR6DOK.mjs} +133 -141
  15. package/dist/{token-goat-chunk-MGOUYAA2.mjs → token-goat-chunk-TUPJRK7R.mjs} +1 -1
  16. package/dist/{token-goat-chunk-KYFJC37X.mjs → token-goat-chunk-VXSYZGBA.mjs} +582 -135
  17. package/dist/{token-goat-chunk-DG53MVNJ.mjs → token-goat-chunk-WN5T5EW5.mjs} +212 -212
  18. package/dist/token-goat-hook.mjs +7 -7
  19. package/dist/token-goat.core.mjs +5 -5
  20. package/package.json +9 -7
  21. package/dist/token-goat-chunk-FRTBMRP7.mjs +0 -10048
  22. package/dist/token-goat-hook-chunk-3QYSN4QV.mjs +0 -14764
  23. package/dist/token-goat-hook-chunk-3ZDBWJDF.mjs +0 -13659
  24. package/dist/token-goat-hook-chunk-5UH54CW6.mjs +0 -912
  25. package/dist/token-goat-hook-chunk-6ODM3MP7.mjs +0 -706
  26. package/dist/token-goat-hook-chunk-A77A26A7.mjs +0 -18184
  27. package/dist/token-goat-hook-chunk-BUOCULAM.mjs +0 -29
  28. package/dist/token-goat-hook-chunk-C6GIABOX.mjs +0 -15971
  29. package/dist/token-goat-hook-chunk-E257IGSN.mjs +0 -153
  30. package/dist/token-goat-hook-chunk-MW5HPEGD.mjs +0 -10411
  31. package/dist/token-goat-hook-chunk-QSCYNJ2B.mjs +0 -23
  32. package/dist/token-goat-hook-chunk-RFRLWOQH.mjs +0 -11
  33. package/dist/token-goat-hook-chunk-XUMIVYEN.mjs +0 -109
  34. package/dist/token-goat-hook-chunk-Y2WHX2P3.mjs +0 -6463
@@ -0,0 +1,185 @@
1
+ import { createRequire as __cjsRequire } from 'node:module';
2
+ const require = __cjsRequire(import.meta.url);
3
+ import {
4
+ external_exports
5
+ } from "./token-goat-chunk-WN5T5EW5.mjs";
6
+ import "./token-goat-chunk-AEX54RUZ.mjs";
7
+
8
+ // src/mcp_jsonrpc.ts
9
+ var LATEST_PROTOCOL_VERSION = "2025-11-25";
10
+ var SUPPORTED_PROTOCOL_VERSIONS = [
11
+ LATEST_PROTOCOL_VERSION,
12
+ "2025-06-18",
13
+ "2025-03-26",
14
+ "2024-11-05",
15
+ "2024-10-07"
16
+ ];
17
+ var JSONRPC_INVALID_REQUEST = -32600;
18
+ var JSONRPC_METHOD_NOT_FOUND = -32601;
19
+ var JSONRPC_INVALID_PARAMS = -32602;
20
+ var DRAFT_07_SCHEMA_ID = "http://json-schema.org/draft-07/schema#";
21
+ var MCP_INVALID_PARAMS_PREFIX = `MCP error ${JSONRPC_INVALID_PARAMS}: `;
22
+ var EMPTY_OBJECT_JSON_SCHEMA = { type: "object", properties: {} };
23
+ function buildJsonSchema(shape) {
24
+ if (shape === void 0 || Object.keys(shape).length === 0) return { ...EMPTY_OBJECT_JSON_SCHEMA };
25
+ const schema = external_exports.toJSONSchema(external_exports.object(shape), { io: "input" });
26
+ return { ...schema, $schema: DRAFT_07_SCHEMA_ID };
27
+ }
28
+ function describeParseError(error) {
29
+ return error.issues.map((issue) => {
30
+ if (issue.path.length === 0) return issue.message;
31
+ const dotted = issue.path.reduce(
32
+ (acc, seg, i) => i === 0 ? String(seg) : typeof seg === "number" ? `${acc}[${seg}]` : `${acc}.${String(seg)}`,
33
+ ""
34
+ );
35
+ return `${issue.message} at ${dotted}`;
36
+ }).join("\n");
37
+ }
38
+ function errorText(message) {
39
+ return { content: [{ type: "text", text: message }], isError: true };
40
+ }
41
+ function isRequest(message) {
42
+ return "method" in message && "id" in message && message.id !== void 0 && message.id !== null;
43
+ }
44
+ var McpServer = class {
45
+ info;
46
+ tools = /* @__PURE__ */ new Map();
47
+ handlers = /* @__PURE__ */ new Map();
48
+ transport;
49
+ /** Set by the caller to learn when the peer hung up; `mcp-serve` waits on exactly this. */
50
+ onclose;
51
+ constructor(info) {
52
+ this.info = info;
53
+ }
54
+ /**
55
+ * Registers a tool. Schema generation happens here rather than at `tools/list` time, which also
56
+ * means a malformed shape fails loudly at startup instead of on a client's first listing.
57
+ */
58
+ registerTool(name, definition, handler) {
59
+ if (this.tools.has(name)) throw new Error(`MCP tool registered twice: ${name}`);
60
+ const shape = definition.inputSchema;
61
+ this.tools.set(name, {
62
+ name,
63
+ description: definition.description,
64
+ jsonSchema: buildJsonSchema(shape),
65
+ // An absent shape still needs a validator, so an argumentless tool called with arguments is
66
+ // not silently handed them.
67
+ validator: external_exports.object(shape ?? {})
68
+ });
69
+ this.handlers.set(name, handler);
70
+ }
71
+ /**
72
+ * Wires this server to a transport and starts it.
73
+ *
74
+ * `onmessage` is assigned before `start()` on purpose: a transport may have buffered messages
75
+ * that arrive the moment it starts (the SDK's in-memory transport queues anything sent before a
76
+ * handler exists and drains the queue inside `start()`), so a server that started first would
77
+ * drop the client's `initialize`.
78
+ */
79
+ async connect(transport) {
80
+ this.transport = transport;
81
+ transport.onmessage = (message) => {
82
+ void this.handleMessage(message);
83
+ };
84
+ transport.onclose = () => {
85
+ this.transport = void 0;
86
+ this.onclose?.();
87
+ };
88
+ await transport.start();
89
+ }
90
+ async close() {
91
+ const transport = this.transport;
92
+ this.transport = void 0;
93
+ await transport?.close();
94
+ }
95
+ async send(message) {
96
+ try {
97
+ await this.transport?.send(message);
98
+ } catch {
99
+ }
100
+ }
101
+ async handleMessage(message) {
102
+ if (!isRequest(message)) return;
103
+ const { id, method } = message;
104
+ const params = message.params ?? {};
105
+ try {
106
+ switch (method) {
107
+ case "initialize":
108
+ await this.send({ jsonrpc: "2.0", id, result: this.initializeResult(params) });
109
+ return;
110
+ case "ping":
111
+ await this.send({ jsonrpc: "2.0", id, result: {} });
112
+ return;
113
+ case "tools/list":
114
+ await this.send({ jsonrpc: "2.0", id, result: { tools: this.listTools() } });
115
+ return;
116
+ case "tools/call":
117
+ await this.send({ jsonrpc: "2.0", id, result: await this.callTool(params) });
118
+ return;
119
+ default:
120
+ await this.sendError(id, JSONRPC_METHOD_NOT_FOUND, `Method not found: ${method}`);
121
+ return;
122
+ }
123
+ } catch (err) {
124
+ await this.sendError(id, JSONRPC_INVALID_REQUEST, err instanceof Error ? err.message : String(err));
125
+ }
126
+ }
127
+ async sendError(id, code, message) {
128
+ await this.send({ jsonrpc: "2.0", id, error: { code, message } });
129
+ }
130
+ initializeResult(params) {
131
+ const requested = params["protocolVersion"];
132
+ const protocolVersion = typeof requested === "string" && SUPPORTED_PROTOCOL_VERSIONS.includes(requested) ? requested : LATEST_PROTOCOL_VERSION;
133
+ return {
134
+ protocolVersion,
135
+ // Tools only, and they never change after startup, so no `listChanged`. This is the one
136
+ // place we deliberately do not copy the SDK, which declares `listChanged: true` because its
137
+ // McpServer is *able* to send that notification. Token-goat registers every tool before it
138
+ // connects and never adds or removes one, so we would be promising a notification we can
139
+ // never have cause to send. No client behaviour turns on it either way -- a subscription
140
+ // that never fires and an absent subscription are the same thing when the list is fixed --
141
+ // so the honest declaration wins. Contrast the tool-error text below, which a model reads as
142
+ // content and is therefore matched to the SDK exactly.
143
+ capabilities: { tools: {} },
144
+ serverInfo: { name: this.info.name, version: this.info.version }
145
+ };
146
+ }
147
+ listTools() {
148
+ return [...this.tools.values()].map((tool) => ({
149
+ name: tool.name,
150
+ ...tool.description !== void 0 ? { description: tool.description } : {},
151
+ inputSchema: tool.jsonSchema,
152
+ // The SDK emits this for every tool it registers, and it is literally true of ours: we do
153
+ // not implement task augmentation, so a client that asks for it gets refused. Emitting the
154
+ // same thing keeps `tools/list` byte-identical to what token-goat sent before, which is what
155
+ // the differential test in tests/mcp_jsonrpc.test.ts pins.
156
+ execution: { taskSupport: "forbidden" }
157
+ }));
158
+ }
159
+ async callTool(params) {
160
+ const name = params["name"];
161
+ if (typeof name !== "string") return errorText('Invalid params: tools/call requires a string "name"');
162
+ const tool = this.tools.get(name);
163
+ const handler = this.handlers.get(name);
164
+ if (tool === void 0 || handler === void 0) return errorText(`${MCP_INVALID_PARAMS_PREFIX}Tool ${name} not found`);
165
+ const parsed = tool.validator.safeParse(params["arguments"] ?? {});
166
+ if (!parsed.success) {
167
+ return errorText(
168
+ `${MCP_INVALID_PARAMS_PREFIX}Input validation error: Invalid arguments for tool ${name}: ${describeParseError(parsed.error)}`
169
+ );
170
+ }
171
+ try {
172
+ return await handler(parsed.data);
173
+ } catch (err) {
174
+ return errorText(err instanceof Error ? err.message : String(err));
175
+ }
176
+ }
177
+ };
178
+ export {
179
+ JSONRPC_INVALID_PARAMS,
180
+ JSONRPC_INVALID_REQUEST,
181
+ JSONRPC_METHOD_NOT_FOUND,
182
+ LATEST_PROTOCOL_VERSION,
183
+ McpServer,
184
+ SUPPORTED_PROTOCOL_VERSIONS
185
+ };
@@ -0,0 +1,23 @@
1
+ import { createRequire as __cjsRequire } from 'node:module';
2
+ const require = __cjsRequire(import.meta.url);
3
+ import {
4
+ buildEvent,
5
+ relay,
6
+ relayInProcess
7
+ } from "./token-goat-chunk-LVCBDJVE.mjs";
8
+ import {
9
+ MAX_STDIN_BYTES,
10
+ readStdinJson
11
+ } from "./token-goat-chunk-AO6MFFTW.mjs";
12
+ import "./token-goat-chunk-2F6TFBZE.mjs";
13
+ import "./token-goat-chunk-TUPJRK7R.mjs";
14
+ import "./token-goat-chunk-44Y77VHR.mjs";
15
+ import "./token-goat-chunk-AO2QD2AG.mjs";
16
+ import "./token-goat-chunk-AEX54RUZ.mjs";
17
+ export {
18
+ MAX_STDIN_BYTES,
19
+ buildEvent,
20
+ readStdinJson,
21
+ relay,
22
+ relayInProcess
23
+ };
@@ -1,12 +1,14 @@
1
1
  import { createRequire as __cjsRequire } from 'node:module';
2
2
  const require = __cjsRequire(import.meta.url);
3
3
  import {
4
+ copilotCliMcpToolsDir,
5
+ estimateTokensFromLength,
4
6
  isBlobStale,
5
7
  isBuildCommand,
6
8
  loadBlob,
7
9
  sanitizeFtsQuery,
8
10
  storeBlob
9
- } from "./token-goat-chunk-UFOVM7ZN.mjs";
11
+ } from "./token-goat-chunk-2F6TFBZE.mjs";
10
12
  import {
11
13
  SYMBOL_BODY_CHAR_CAP,
12
14
  extractErrorMessage,
@@ -16,11 +18,94 @@ import {
16
18
  redactSecrets,
17
19
  runGit,
18
20
  shortFingerprint
19
- } from "./token-goat-chunk-V465YKOR.mjs";
21
+ } from "./token-goat-chunk-44Y77VHR.mjs";
20
22
  import {
21
23
  registerReset
22
24
  } from "./token-goat-chunk-AO2QD2AG.mjs";
23
25
 
26
+ // src/copilot_mcp_tools.ts
27
+ import fs from "node:fs";
28
+ import path from "node:path";
29
+ function wireShapeLength(tools) {
30
+ const wire = tools.map((tool) => {
31
+ const out = {};
32
+ if (typeof tool.name === "string") out["name"] = tool.name;
33
+ if (typeof tool.description === "string") out["description"] = tool.description;
34
+ if (tool.inputSchema !== void 0) out["inputSchema"] = tool.inputSchema;
35
+ return out;
36
+ });
37
+ return JSON.stringify(wire).length;
38
+ }
39
+ function readCopilotMcpTools(dir = copilotCliMcpToolsDir()) {
40
+ let entries;
41
+ try {
42
+ entries = fs.readdirSync(dir);
43
+ } catch {
44
+ return { cacheFound: false, servers: [], unreadable: 0 };
45
+ }
46
+ const newest = /* @__PURE__ */ new Map();
47
+ let unreadable = 0;
48
+ for (const entry of entries) {
49
+ if (!entry.toLowerCase().endsWith(".json")) continue;
50
+ let parsed;
51
+ try {
52
+ parsed = JSON.parse(fs.readFileSync(path.join(dir, entry), "utf8"));
53
+ } catch {
54
+ unreadable += 1;
55
+ continue;
56
+ }
57
+ if (typeof parsed !== "object" || parsed === null) {
58
+ unreadable += 1;
59
+ continue;
60
+ }
61
+ const record = parsed;
62
+ const serverName = typeof record.serverName === "string" ? record.serverName.trim() : "";
63
+ if (serverName === "") {
64
+ unreadable += 1;
65
+ continue;
66
+ }
67
+ const tools = Array.isArray(record.tools) ? record.tools : [];
68
+ const updatedAt = typeof record.updatedAt === "string" ? record.updatedAt : "";
69
+ const definitionBytes = wireShapeLength(tools);
70
+ const candidate = {
71
+ serverName,
72
+ toolCount: tools.length,
73
+ definitionBytes,
74
+ estimatedTokens: estimateTokensFromLength(definitionBytes),
75
+ updatedAt,
76
+ // `tools` is an unchecked cast off a JSON array, so every element is
77
+ // treated as untrusted here rather than trusted to match CachedTool.
78
+ toolNames: tools.map((t) => t?.name).filter((n) => typeof n === "string" && n !== "")
79
+ };
80
+ const existing = newest.get(serverName);
81
+ if (existing === void 0 || candidate.updatedAt > existing.updatedAt) newest.set(serverName, candidate);
82
+ }
83
+ const servers = [...newest.values()].sort(
84
+ (a, b) => b.definitionBytes - a.definitionBytes || a.serverName.localeCompare(b.serverName)
85
+ );
86
+ return { cacheFound: true, servers, unreadable };
87
+ }
88
+
89
+ // src/copilot_mcp_names.ts
90
+ var _cached = null;
91
+ function copilotMcpToolNameMap() {
92
+ if (_cached) return _cached;
93
+ const map = /* @__PURE__ */ new Map();
94
+ try {
95
+ for (const server of readCopilotMcpTools().servers) {
96
+ for (const toolName of server.toolNames) {
97
+ map.set(`${server.serverName}-${toolName}`, `mcp__${server.serverName}__${toolName}`);
98
+ }
99
+ }
100
+ } catch {
101
+ }
102
+ _cached = map;
103
+ return map;
104
+ }
105
+ function canonicalizeCopilotMcpToolName(toolName) {
106
+ return copilotMcpToolNameMap().get(toolName) ?? toolName;
107
+ }
108
+
24
109
  // src/hooks_cli.ts
25
110
  var _LOG = {
26
111
  warn: (msg, ...args) => console.warn(`[hooks_cli] ${msg}`, ...args),
@@ -150,6 +235,12 @@ function normalizePayload(payload, harness = "claude") {
150
235
  result2["_tg_harness"] = harness;
151
236
  return result2;
152
237
  }
238
+ if (harness === "copilot_cli") {
239
+ const result2 = { ...obj };
240
+ result2["tool_name"] = canonicalizeCopilotMcpToolName(toolName);
241
+ result2["_tg_harness"] = harness;
242
+ return result2;
243
+ }
153
244
  if (harness === "kimi") {
154
245
  const result2 = remapToolName(obj, toolName, KIMI_TOOL_NAME_MAP, KIMI_INPUT_KEY_MAP);
155
246
  result2["_tg_harness"] = harness;
@@ -373,11 +464,11 @@ function gitStateFingerprintSync(cwd) {
373
464
  }
374
465
  }
375
466
  var DIR_FINGERPRINT_LISTING_CAP_ENTRIES = 1e4;
376
- function dirStateFingerprintSync(path) {
467
+ function dirStateFingerprintSync(path2) {
377
468
  try {
378
- const stat = statSync(path);
469
+ const stat = statSync(path2);
379
470
  if (!stat.isDirectory()) return null;
380
- const entries = readdirSync(path);
471
+ const entries = readdirSync(path2);
381
472
  if (entries.length <= DIR_FINGERPRINT_LISTING_CAP_ENTRIES) {
382
473
  return shortFingerprint(entries.slice().sort().join("\0"));
383
474
  }
@@ -387,12 +478,12 @@ function dirStateFingerprintSync(path) {
387
478
  }
388
479
  }
389
480
  var FILE_FINGERPRINT_CONTENT_CAP_BYTES = 2 * 1024 * 1024;
390
- function fileStateFingerprintSync(path) {
481
+ function fileStateFingerprintSync(path2) {
391
482
  try {
392
- const stat = statSync(path);
483
+ const stat = statSync(path2);
393
484
  if (!stat.isFile()) return null;
394
485
  if (stat.size <= FILE_FINGERPRINT_CONTENT_CAP_BYTES) {
395
- return shortFingerprint(readFileSync(path));
486
+ return shortFingerprint(readFileSync(path2));
396
487
  }
397
488
  return shortFingerprint(`${stat.mtimeMs}\0${stat.size}`);
398
489
  } catch {
@@ -715,9 +806,11 @@ var HOOK_EVENTS = [
715
806
  "notification",
716
807
  "stop",
717
808
  "pre_compact",
809
+ "post_compact",
718
810
  "user_prompt_submit",
719
811
  "subagent_stop",
720
- "session_start"
812
+ "session_start",
813
+ "post_tool_use_failure"
721
814
  ];
722
815
 
723
816
  // src/cli_commands.ts
@@ -855,10 +948,10 @@ function readStdinJson(timeoutMs = DEFAULT_STDIN_TIMEOUT_MS, maxBytes = MAX_STDI
855
948
  }
856
949
 
857
950
  // src/symbol_body_probe.ts
858
- import * as fs from "fs";
951
+ import * as fs2 from "fs";
859
952
  var OVERSIZED_BODY_PROBE_SQL = `SELECT 1 FROM symbols WHERE LENGTH(body) > ${SYMBOL_BODY_CHAR_CAP} LIMIT 1`;
860
953
  function checkSymbolBodySize(dbPath) {
861
- if (!fs.existsSync(dbPath)) {
954
+ if (!fs2.existsSync(dbPath)) {
862
955
  return { name: "Symbol body size", status: "ok", message: "no database yet" };
863
956
  }
864
957
  try {
@@ -881,7 +974,210 @@ function checkSymbolBodySize(dbPath) {
881
974
  }
882
975
  }
883
976
 
977
+ // src/resident_context.ts
978
+ import * as fs3 from "node:fs";
979
+ var LARGE_TASK_LIST_BYTES = 2e4;
980
+ var LARGE_SKILL_BODY_BYTES = 2e4;
981
+ var SKILL_BODY_REPEAT_THRESHOLD = 2;
982
+ var RESIDENT_TAIL_MAX_BYTES = 1048576;
983
+ function createResidentContextStats() {
984
+ return {
985
+ attachmentsByType: /* @__PURE__ */ new Map(),
986
+ latestTaskList: null,
987
+ taskReminderCount: 0,
988
+ taskReminderBytes: 0,
989
+ skillBodies: /* @__PURE__ */ new Map(),
990
+ compactionCount: 0
991
+ };
992
+ }
993
+ function bump(map, key, bytes) {
994
+ const entry = map.get(key);
995
+ if (entry === void 0) map.set(key, { count: 1, bytes });
996
+ else {
997
+ entry.count += 1;
998
+ entry.bytes += bytes;
999
+ }
1000
+ }
1001
+ function messageText(message) {
1002
+ if (message === null || typeof message !== "object") return "";
1003
+ const content = message["content"];
1004
+ if (typeof content === "string") return content;
1005
+ if (!Array.isArray(content)) return "";
1006
+ let out = "";
1007
+ for (const block of content) {
1008
+ if (block === null || typeof block !== "object") continue;
1009
+ const text = block["text"];
1010
+ if (typeof text === "string") out += text;
1011
+ }
1012
+ return out;
1013
+ }
1014
+ function skillNameFromBody(text) {
1015
+ const dir = /^Base directory for this skill:\s*(.+?)\s*$/m.exec(text);
1016
+ if (dir?.[1] !== void 0) {
1017
+ const segments = dir[1].split(/[\\/]/).filter((s) => s.length > 0);
1018
+ const last = segments[segments.length - 1];
1019
+ if (last !== void 0 && last.length > 0) return last;
1020
+ }
1021
+ const heading = /^#\s+(.+?)\s*$/m.exec(text);
1022
+ if (heading?.[1] !== void 0 && heading[1].length > 0) return heading[1];
1023
+ return null;
1024
+ }
1025
+ function readTaskList(attachment, bytes) {
1026
+ const snapshot = {
1027
+ bytes,
1028
+ itemCount: typeof attachment["itemCount"] === "number" ? attachment["itemCount"] : 0,
1029
+ completed: 0,
1030
+ inProgress: 0,
1031
+ pending: 0,
1032
+ descriptionBytes: 0,
1033
+ completedDescriptionBytes: 0
1034
+ };
1035
+ const content = attachment["content"];
1036
+ if (!Array.isArray(content)) return snapshot;
1037
+ if (content.length > snapshot.itemCount) snapshot.itemCount = content.length;
1038
+ for (const item of content) {
1039
+ if (item === null || typeof item !== "object") continue;
1040
+ const record = item;
1041
+ const status = typeof record["status"] === "string" ? record["status"] : "";
1042
+ const description = typeof record["description"] === "string" ? record["description"] : "";
1043
+ snapshot.descriptionBytes += description.length;
1044
+ if (status === "completed") {
1045
+ snapshot.completed += 1;
1046
+ snapshot.completedDescriptionBytes += description.length;
1047
+ } else if (status === "in_progress") snapshot.inProgress += 1;
1048
+ else if (status === "pending") snapshot.pending += 1;
1049
+ }
1050
+ return snapshot;
1051
+ }
1052
+ function collectInvokedSkills(acc, record) {
1053
+ const skills = record["skills"];
1054
+ if (!Array.isArray(skills)) return;
1055
+ for (const entry of skills) {
1056
+ if (entry === null || typeof entry !== "object") continue;
1057
+ const skill = entry;
1058
+ const content = skill["content"];
1059
+ if (typeof content !== "string" || content.length < LARGE_SKILL_BODY_BYTES) continue;
1060
+ const name = skillName(skill);
1061
+ if (name !== null) bump(acc.skillBodies, name, content.length);
1062
+ }
1063
+ }
1064
+ function skillName(skill) {
1065
+ const name = skill["name"];
1066
+ if (typeof name === "string" && name.trim() !== "") return name.trim();
1067
+ const path2 = skill["path"];
1068
+ if (typeof path2 !== "string") return null;
1069
+ const segments = path2.split(/[\\/]/).filter((part) => part !== "");
1070
+ const last = segments[segments.length - 1];
1071
+ return last === void 0 || last === "" ? null : last;
1072
+ }
1073
+ function accumulateResidentLine(acc, parsed, bytes) {
1074
+ try {
1075
+ if (parsed === null || typeof parsed !== "object") return;
1076
+ const line = parsed;
1077
+ if (line["type"] === "system" && line["subtype"] === "compact_boundary") acc.compactionCount += 1;
1078
+ const attachment = line["attachment"];
1079
+ if (attachment !== null && typeof attachment === "object") {
1080
+ const record = attachment;
1081
+ const type = typeof record["type"] === "string" ? record["type"] : "unknown";
1082
+ bump(acc.attachmentsByType, type, bytes);
1083
+ if (type === "task_reminder") {
1084
+ acc.taskReminderCount += 1;
1085
+ acc.taskReminderBytes += bytes;
1086
+ acc.latestTaskList = readTaskList(record, bytes);
1087
+ }
1088
+ if (type === "invoked_skills") collectInvokedSkills(acc, record);
1089
+ }
1090
+ if (line["isMeta"] === true && line["type"] === "user") {
1091
+ const text = messageText(line["message"]);
1092
+ if (text.length >= LARGE_SKILL_BODY_BYTES) {
1093
+ const skill = skillNameFromBody(text);
1094
+ if (skill !== null) bump(acc.skillBodies, skill, text.length);
1095
+ }
1096
+ }
1097
+ } catch {
1098
+ }
1099
+ }
1100
+ function summarizeResidentContext(acc) {
1101
+ const attachmentClasses = [...acc.attachmentsByType.entries()].map(([type, v]) => ({ type, count: v.count, bytes: v.bytes })).sort((a, b) => b.bytes - a.bytes || a.type.localeCompare(b.type));
1102
+ const repeatedSkillBodies = [...acc.skillBodies.entries()].filter(([, v]) => v.count >= SKILL_BODY_REPEAT_THRESHOLD).map(([skill, v]) => ({ skill, count: v.count, bytes: v.bytes })).sort((a, b) => b.bytes - a.bytes || a.skill.localeCompare(b.skill));
1103
+ return {
1104
+ attachmentClasses,
1105
+ totalAttachmentBytes: attachmentClasses.reduce((n, c) => n + c.bytes, 0),
1106
+ latestTaskList: acc.latestTaskList,
1107
+ taskReminderCount: acc.taskReminderCount,
1108
+ taskReminderBytes: acc.taskReminderBytes,
1109
+ repeatedSkillBodies,
1110
+ compactionCount: acc.compactionCount
1111
+ };
1112
+ }
1113
+ function formatBytes(bytes) {
1114
+ if (bytes >= 1e6) return `${(bytes / 1048576).toFixed(1)} MB`;
1115
+ if (bytes >= 1e3) return `${Math.round(bytes / 1024)} KB`;
1116
+ return `${bytes} B`;
1117
+ }
1118
+ function formatTokenEstimate(tokens) {
1119
+ return tokens >= 1e3 ? `${Math.round(tokens / 1e3)}K` : String(tokens);
1120
+ }
1121
+ function taskListPruneHint(snapshot) {
1122
+ if (snapshot === null) return null;
1123
+ if (snapshot.bytes < LARGE_TASK_LIST_BYTES) return null;
1124
+ if (snapshot.completed === 0) return null;
1125
+ const tokens = formatTokenEstimate(estimateTokensFromLength(snapshot.bytes));
1126
+ const prunable = formatBytes(snapshot.completedDescriptionBytes);
1127
+ return `Your task list is ${formatBytes(snapshot.bytes)} (~${tokens} tok est) and is re-injected in full whenever it changes; ${snapshot.completed} of ${snapshot.itemCount} items are already completed and their descriptions alone are ${prunable}. Prune completed items or shorten their descriptions with TaskUpdate. Check ownership first if other agents share this list.`;
1128
+ }
1129
+ function repeatedSkillBodyHint(injections) {
1130
+ const worst = injections[0];
1131
+ if (worst === void 0) return null;
1132
+ if (worst.count < SKILL_BODY_REPEAT_THRESHOLD || worst.bytes < LARGE_SKILL_BODY_BYTES) return null;
1133
+ const tokens = formatTokenEstimate(estimateTokensFromLength(worst.bytes));
1134
+ return `The \`${worst.skill}\` skill body has been injected ${worst.count} times this session (${formatBytes(worst.bytes)} total, ~${tokens} tok est). Slash expansion and the Skill tool both send the whole body every time, and no hook can intercept either. If it is already loaded, work from it instead of re-invoking; to reread one part, use \`token-goat skill-section ${worst.skill} '<heading>'\`.`;
1135
+ }
1136
+ function readTranscriptTail(transcriptPath, maxBytes = RESIDENT_TAIL_MAX_BYTES) {
1137
+ let fd = null;
1138
+ try {
1139
+ const stat = fs3.statSync(transcriptPath);
1140
+ if (!stat.isFile() || stat.size === 0) return [];
1141
+ const start = Math.max(0, stat.size - maxBytes);
1142
+ const length = stat.size - start;
1143
+ fd = fs3.openSync(transcriptPath, "r");
1144
+ const buf = Buffer.allocUnsafe(length);
1145
+ const read = fs3.readSync(fd, buf, 0, length, start);
1146
+ const lines = buf.subarray(0, read).toString("utf8").split("\n");
1147
+ if (start > 0) lines.shift();
1148
+ return lines;
1149
+ } catch {
1150
+ return [];
1151
+ } finally {
1152
+ if (fd !== null) {
1153
+ try {
1154
+ fs3.closeSync(fd);
1155
+ } catch {
1156
+ }
1157
+ }
1158
+ }
1159
+ }
1160
+ function lineMayCarryResidentSignal(line) {
1161
+ return line.includes('"task_reminder"') || line.includes('"compact_boundary"') || line.includes('"invoked_skills"') || line.includes('"isMeta"');
1162
+ }
1163
+ function accumulateResidentLines(lines) {
1164
+ const acc = createResidentContextStats();
1165
+ for (const line of lines) {
1166
+ const trimmed = line.trim();
1167
+ if (trimmed === "") continue;
1168
+ let parsed;
1169
+ try {
1170
+ parsed = JSON.parse(trimmed);
1171
+ } catch {
1172
+ continue;
1173
+ }
1174
+ accumulateResidentLine(acc, parsed, trimmed.length);
1175
+ }
1176
+ return acc;
1177
+ }
1178
+
884
1179
  export {
1180
+ readCopilotMcpTools,
885
1181
  GEMINI_TOOL_NAME_MAP,
886
1182
  normalizePayload,
887
1183
  isRecallCacheType,
@@ -907,6 +1203,16 @@ export {
907
1203
  filterCommandManifest,
908
1204
  formatCommandManifest,
909
1205
  checkSymbolBodySize,
1206
+ createResidentContextStats,
1207
+ accumulateResidentLine,
1208
+ summarizeResidentContext,
1209
+ formatBytes,
1210
+ formatTokenEstimate,
1211
+ taskListPruneHint,
1212
+ repeatedSkillBodyHint,
1213
+ readTranscriptTail,
1214
+ lineMayCarryResidentSignal,
1215
+ accumulateResidentLines,
910
1216
  MAX_STDIN_BYTES,
911
1217
  readStdinJson
912
1218
  };
@@ -25,7 +25,7 @@ import {
25
25
  runSkeleton,
26
26
  runSymbol,
27
27
  withPinnedReads
28
- } from "./token-goat-chunk-IYTVE6KN.mjs";
28
+ } from "./token-goat-chunk-SRAR6DOK.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-UFOVM7ZN.mjs";
37
+ } from "./token-goat-chunk-2F6TFBZE.mjs";
38
38
  import {
39
39
  VERSION,
40
40
  dataDir,
@@ -45,7 +45,7 @@ import {
45
45
  normalizePath,
46
46
  recordStat,
47
47
  resolveProjectRoot
48
- } from "./token-goat-chunk-V465YKOR.mjs";
48
+ } from "./token-goat-chunk-44Y77VHR.mjs";
49
49
  import "./token-goat-chunk-AO2QD2AG.mjs";
50
50
  import "./token-goat-chunk-AEX54RUZ.mjs";
51
51
 
@@ -201,7 +201,7 @@ function withConfinedRead(pins, fn) {
201
201
  }
202
202
  }
203
203
  async function createMcpServer() {
204
- const [{ McpServer }, { z }] = await Promise.all([import("@modelcontextprotocol/sdk/server/mcp.js"), import("./token-goat-chunk-DG53MVNJ.mjs")]);
204
+ const [{ McpServer }, { z }] = await Promise.all([import("./token-goat-chunk-5CVKO3DA.mjs"), import("./token-goat-chunk-R4SR7MQY.mjs")]);
205
205
  const server = new McpServer({ name: "token-goat", version: VERSION });
206
206
  const makeProjectRootField = (verb) => z.string().optional().describe(
207
207
  `absolute path to the workspace root to scope this ${verb} to; defaults to the MCP server process's cwd, which is not always the actual workspace root for MCP clients -- pass this explicitly when it might differ`
@@ -7,11 +7,11 @@ import {
7
7
  expandGlobs,
8
8
  leftoverIntegrations,
9
9
  run
10
- } from "./token-goat-chunk-KYFJC37X.mjs";
11
- import "./token-goat-chunk-IYTVE6KN.mjs";
12
- import "./token-goat-chunk-VYMGEVZS.mjs";
13
- import "./token-goat-chunk-UFOVM7ZN.mjs";
14
- import "./token-goat-chunk-V465YKOR.mjs";
10
+ } from "./token-goat-chunk-VXSYZGBA.mjs";
11
+ import "./token-goat-chunk-SRAR6DOK.mjs";
12
+ import "./token-goat-chunk-AO6MFFTW.mjs";
13
+ import "./token-goat-chunk-2F6TFBZE.mjs";
14
+ import "./token-goat-chunk-44Y77VHR.mjs";
15
15
  import "./token-goat-chunk-AO2QD2AG.mjs";
16
16
  import "./token-goat-chunk-AEX54RUZ.mjs";
17
17
  export {