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
@@ -1,912 +0,0 @@
1
- import { createRequire as __cjsRequire } from 'node:module';
2
- const require = __cjsRequire(import.meta.url);
3
- import {
4
- isBlobStale,
5
- isBuildCommand,
6
- loadBlob,
7
- sanitizeFtsQuery,
8
- storeBlob
9
- } from "./token-goat-hook-chunk-A77A26A7.mjs";
10
- import {
11
- SYMBOL_BODY_CHAR_CAP,
12
- extractErrorMessage,
13
- getDb,
14
- globalDbPath,
15
- normalizePath,
16
- redactSecrets,
17
- runGit,
18
- shortFingerprint
19
- } from "./token-goat-hook-chunk-Y2WHX2P3.mjs";
20
- import {
21
- registerReset
22
- } from "./token-goat-hook-chunk-BUOCULAM.mjs";
23
-
24
- // src/hooks_cli.ts
25
- var _LOG = {
26
- warn: (msg, ...args) => console.warn(`[hooks_cli] ${msg}`, ...args),
27
- debug: (msg, ...args) => console.error(`[hooks_cli] ${msg}`, ...args),
28
- error: (msg, err, ...args) => console.error(`[hooks_cli] ${msg}`, ...err ? [err] : [], ...args)
29
- };
30
- var CODEX_TOOL_NAME_MAP = {
31
- bash: "Bash",
32
- edit_file: "Edit",
33
- edit: "Edit",
34
- apply_patch: "Edit",
35
- write_file: "Write",
36
- search_files: "Grep",
37
- grep: "Grep",
38
- list_files: "Glob",
39
- glob: "Glob",
40
- web_search: "WebFetch"
41
- };
42
- var GEMINI_TOOL_NAME_MAP = {
43
- run_shell_command: "Bash",
44
- read_file: "Read",
45
- read_many_files: "Read",
46
- list_directory: "Read",
47
- write_file: "Write",
48
- replace: "Edit",
49
- glob: "Glob",
50
- grep_search: "Grep",
51
- // 'search_file_content' was GREP_TOOL_NAME's value before gemini-cli renamed it to 'grep_search' (confirmed against gemini-cli's current base-declarations.ts, which now defines GREP_TOOL_NAME = 'grep_search'; some of gemini-cli's own docs pages still lag the rename). Kept as a backward-compat entry for older installed gemini-cli versions that still emit the pre-rename name -- not dead/hallucinated, just legacy.
52
- search_file_content: "Grep",
53
- // Gemini's real web-search tool is registered as 'google_web_search' (WEB_SEARCH_TOOL_NAME in gemini-cli's tool-names.ts) -- 'web_search' is not a tool name Gemini CLI ever emits, so the old entry here silently never matched a real invocation.
54
- google_web_search: "WebFetch",
55
- web_fetch: "WebFetch"
56
- };
57
- var GEMINI_INPUT_KEY_MAP = {
58
- // Gemini's real write_file/replace tool schemas already use 'file_path' (and replace's 'old_string'/'new_string') verbatim -- identical to token-goat's own canonical keys (see getFilePath() in hooks_common.ts, which reads event.toolInput['file_path']) -- confirmed against gemini-cli's own EditToolParams/WriteFileToolParams interfaces. No remap is needed for Write/Edit; the previous entries here actively renamed a key that was already correct (file_path -> path), which would have silently corrupted the path argument on every real Gemini Write/Edit call.
59
- // 'Read' covers THREE distinct raw Gemini tools (see GEMINI_TOOL_NAME_MAP above), and they do NOT share one schema: read_file already uses 'file_path' verbatim, no remap needed; list_directory uses 'dir_path' (confirmed against gemini-cli's LSToolParams) -- remapped to 'file_path' below, exactly mirroring Grep's existing dir_path->path fix; read_many_files uses 'include' (confirmed against gemini-cli's ReadManyFilesParams), an ARRAY of glob patterns, not a single file path -- there is no single string to remap it to, so it is deliberately left unmapped. getFilePath() (hooks_common.ts) will return undefined for this call, and preReadHandler/postReadHandler already fall back to passOutput() on an undefined path (see preReadHandler's `if (filePath === undefined) return passOutput()`), so a real read_many_files call still succeeds normally; it just doesn't get session-dedup tracking or read-count hints. Accepted limitation, not a bug to force-fit.
60
- Read: { dir_path: "file_path" },
61
- // Grep's real tool (grep_search) calls its target-directory argument 'dir_path', which preReadHandler's Grep fallback (event.toolInput['path']) doesn't recognize, so that one remains remapped.
62
- Grep: { dir_path: "path" }
63
- };
64
- var GROK_TOOL_NAME_MAP = {
65
- read_file: "Read",
66
- write: "Write",
67
- search_replace: "Edit",
68
- run_terminal_command: "Bash",
69
- grep: "Grep",
70
- list_dir: "Glob"
71
- };
72
- var GROK_INPUT_KEY_MAP = {
73
- Read: { target_file: "file_path" }
74
- };
75
- var KIMI_TOOL_NAME_MAP = {
76
- FetchURL: "WebFetch",
77
- ReadMediaFile: "Read"
78
- };
79
- var KIMI_INPUT_KEY_MAP = {
80
- Read: { path: "file_path" },
81
- Write: { path: "file_path" },
82
- Edit: { path: "file_path" }
83
- };
84
- function remapInputKeys(input, keyMap) {
85
- const newInput = {};
86
- for (const [k, v] of Object.entries(input)) {
87
- newInput[keyMap[k] || k] = v;
88
- }
89
- return newInput;
90
- }
91
- function remapToolName(obj, toolName, nameMap, inputKeyMap) {
92
- const mapped = nameMap[toolName] ?? toolName;
93
- const result = { ...obj };
94
- result["tool_name"] = mapped;
95
- const rawInput = obj["tool_input"];
96
- const keyMap = inputKeyMap[mapped];
97
- if (keyMap && typeof rawInput === "object" && rawInput !== null && !Array.isArray(rawInput)) {
98
- result["tool_input"] = remapInputKeys(rawInput, keyMap);
99
- }
100
- return result;
101
- }
102
- function grokToCanonicalWire(obj) {
103
- const wire = { ...obj };
104
- if (typeof wire["toolName"] === "string" && wire["tool_name"] === void 0) {
105
- wire["tool_name"] = wire["toolName"];
106
- }
107
- if (typeof wire["toolInput"] === "object" && wire["toolInput"] !== null && !Array.isArray(wire["toolInput"]) && wire["tool_input"] === void 0) {
108
- wire["tool_input"] = wire["toolInput"];
109
- }
110
- if (typeof wire["sessionId"] === "string" && wire["session_id"] === void 0) {
111
- wire["session_id"] = wire["sessionId"];
112
- }
113
- const toolResult = wire["toolResult"];
114
- if (toolResult !== null && typeof toolResult === "object" && !Array.isArray(toolResult)) {
115
- const tr = toolResult;
116
- if (typeof tr["output_for_prompt"] === "string") {
117
- wire["tool_response"] = { content: tr["output_for_prompt"], exit_code: tr["exit_code"] };
118
- } else {
119
- const firstString = Object.entries(tr).find(([k, v]) => k !== "type" && typeof v === "string");
120
- if (firstString) {
121
- wire["tool_response"] = { content: firstString[1] };
122
- }
123
- }
124
- }
125
- return wire;
126
- }
127
- function normalizePayload(payload, harness = "claude") {
128
- if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
129
- _LOG.warn("normalizePayload: payload is not a dict; received %s", typeof payload);
130
- return {};
131
- }
132
- const rawObj = payload;
133
- const obj = harness === "grok" ? grokToCanonicalWire(rawObj) : rawObj;
134
- const toolName = obj["tool_name"];
135
- if (typeof toolName !== "string" || !toolName.trim()) {
136
- _LOG.debug("normalizePayload: tool_name missing or invalid; received %s", toolName);
137
- return {};
138
- }
139
- if (harness === "grok") {
140
- const result2 = remapToolName(obj, toolName, GROK_TOOL_NAME_MAP, GROK_INPUT_KEY_MAP);
141
- result2["_tg_harness"] = harness;
142
- return result2;
143
- }
144
- if (harness === "codex") {
145
- const mapped = CODEX_TOOL_NAME_MAP[toolName];
146
- const result2 = { ...obj };
147
- if (mapped) {
148
- result2["tool_name"] = mapped;
149
- }
150
- result2["_tg_harness"] = harness;
151
- return result2;
152
- }
153
- if (harness === "kimi") {
154
- const result2 = remapToolName(obj, toolName, KIMI_TOOL_NAME_MAP, KIMI_INPUT_KEY_MAP);
155
- result2["_tg_harness"] = harness;
156
- return result2;
157
- }
158
- if (harness === "gemini") {
159
- const result2 = remapToolName(obj, toolName, GEMINI_TOOL_NAME_MAP, GEMINI_INPUT_KEY_MAP);
160
- if ("functionCallId" in result2 && !("toolUseId" in result2)) {
161
- result2["toolUseId"] = result2["functionCallId"];
162
- delete result2["functionCallId"];
163
- }
164
- result2["_tg_harness"] = harness;
165
- return result2;
166
- }
167
- const result = { ...obj };
168
- result["_tg_harness"] = harness;
169
- return result;
170
- }
171
-
172
- // src/types.ts
173
- var HOOK_EVENTS = [
174
- "pre_tool_use",
175
- "post_tool_use",
176
- "notification",
177
- "stop",
178
- "pre_compact",
179
- "user_prompt_submit",
180
- "subagent_stop",
181
- "session_start"
182
- ];
183
-
184
- // src/stdin_json.ts
185
- var DEFAULT_STDIN_TIMEOUT_MS = 5e3;
186
- var MAX_STDIN_WALL_MS = 6e4;
187
- var MAX_STDIN_BYTES = 64 * 1024 * 1024;
188
- function readStdinJson(timeoutMs = DEFAULT_STDIN_TIMEOUT_MS, maxBytes = MAX_STDIN_BYTES) {
189
- return new Promise((resolve2, reject) => {
190
- const chunks = [];
191
- let totalBytes = 0;
192
- let settled = false;
193
- const finish = (fn) => {
194
- if (settled) return;
195
- settled = true;
196
- clearTimeout(idleTimer);
197
- clearTimeout(wallTimer);
198
- process.stdin.removeListener("data", onData);
199
- process.stdin.removeListener("end", onEnd);
200
- process.stdin.removeListener("error", onError);
201
- fn();
202
- };
203
- let idleTimer = setTimeout(() => {
204
- finish(() => reject(new Error("readStdinJson: timed out waiting for stdin")));
205
- }, timeoutMs);
206
- const wallTimer = setTimeout(() => {
207
- finish(() => {
208
- process.stdin.destroy();
209
- reject(new Error(`readStdinJson: stdin took longer than ${MAX_STDIN_WALL_MS} ms`));
210
- });
211
- }, MAX_STDIN_WALL_MS);
212
- const onData = (chunk) => {
213
- clearTimeout(idleTimer);
214
- idleTimer = setTimeout(() => {
215
- finish(() => reject(new Error("readStdinJson: timed out waiting for stdin")));
216
- }, timeoutMs);
217
- totalBytes += chunk.length;
218
- if (totalBytes > maxBytes) {
219
- finish(() => {
220
- process.stdin.destroy();
221
- reject(new Error(`readStdinJson: stdin exceeded ${maxBytes} bytes`));
222
- });
223
- return;
224
- }
225
- chunks.push(chunk);
226
- };
227
- const onEnd = () => {
228
- finish(() => {
229
- const text = Buffer.concat(chunks).toString("utf8").trim();
230
- if (text === "") {
231
- reject(new Error("readStdinJson: empty stdin"));
232
- return;
233
- }
234
- try {
235
- resolve2(JSON.parse(text));
236
- } catch (err) {
237
- reject(err instanceof Error ? err : new Error(String(err)));
238
- }
239
- });
240
- };
241
- const onError = (err) => {
242
- finish(() => reject(err instanceof Error ? err : new Error(String(err))));
243
- };
244
- process.stdin.on("data", onData);
245
- process.stdin.on("end", onEnd);
246
- process.stdin.on("error", onError);
247
- });
248
- }
249
-
250
- // src/recall_index.ts
251
- var VALID_TYPES = ["bash", "web", "mcp"];
252
- function isRecallCacheType(value) {
253
- return VALID_TYPES.includes(value);
254
- }
255
- function indexRecallEntry(cacheType, id, label, content, storedAt) {
256
- try {
257
- const db = getDb(globalDbPath());
258
- db.prepare(
259
- `INSERT INTO cache_recall (cache_type, entry_id, label, content, stored_at)
260
- VALUES (@cacheType, @id, @label, @content, @storedAt)
261
- ON CONFLICT(cache_type, entry_id) DO UPDATE SET
262
- label = excluded.label,
263
- content = excluded.content,
264
- stored_at = excluded.stored_at`
265
- ).run({ cacheType, id, label, content, storedAt });
266
- } catch {
267
- }
268
- }
269
- var _ftsAvailable = null;
270
- function hasFtsTable() {
271
- if (_ftsAvailable !== null) return _ftsAvailable;
272
- try {
273
- const db = getDb(globalDbPath());
274
- const row = db.prepare(`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'cache_recall_fts'`).get();
275
- _ftsAvailable = row !== void 0;
276
- } catch {
277
- _ftsAvailable = false;
278
- }
279
- return _ftsAvailable;
280
- }
281
- function buildSnippet(content, query, maxLen = 160) {
282
- const flat = content.replace(/\s+/g, " ").trim();
283
- const tokens = query.split(/\s+/).filter(Boolean);
284
- let idx = -1;
285
- const lowerFlat = flat.toLowerCase();
286
- for (const token of tokens) {
287
- const at = lowerFlat.indexOf(token.toLowerCase());
288
- if (at !== -1) {
289
- idx = at;
290
- break;
291
- }
292
- }
293
- if (idx === -1) {
294
- return flat.length > maxLen ? flat.slice(0, maxLen) + "..." : flat;
295
- }
296
- const start = Math.max(0, idx - Math.floor(maxLen / 3));
297
- const end = Math.min(flat.length, start + maxLen);
298
- const prefix = start > 0 ? "..." : "";
299
- const suffix = end < flat.length ? "..." : "";
300
- return prefix + flat.slice(start, end) + suffix;
301
- }
302
- function toFtsMatchExpr(query) {
303
- if (query.trim().length === 0) return null;
304
- return sanitizeFtsQuery(query);
305
- }
306
- function mapRowsToHits(rows, snippetQuery) {
307
- return rows.filter((r) => isRecallCacheType(r.cacheType)).map((r) => ({
308
- id: r.id,
309
- cacheType: r.cacheType,
310
- label: r.label ?? "",
311
- snippet: buildSnippet(r.content ?? "", snippetQuery),
312
- storedAt: r.storedAt ?? 0
313
- }));
314
- }
315
- function ftsSearch(query, type, limit) {
316
- const matchExpr = toFtsMatchExpr(query);
317
- if (matchExpr === null) return [];
318
- const db = getDb(globalDbPath());
319
- const rows = type !== void 0 ? db.prepare(
320
- `SELECT c.entry_id AS id, c.cache_type AS cacheType, c.label AS label, c.content AS content, c.stored_at AS storedAt
321
- FROM cache_recall_fts
322
- JOIN cache_recall c ON c.row_id = cache_recall_fts.rowid
323
- WHERE cache_recall_fts MATCH ? AND c.cache_type = ?
324
- ORDER BY bm25(cache_recall_fts)
325
- LIMIT ?`
326
- ).all(matchExpr, type, limit) : db.prepare(
327
- `SELECT c.entry_id AS id, c.cache_type AS cacheType, c.label AS label, c.content AS content, c.stored_at AS storedAt
328
- FROM cache_recall_fts
329
- JOIN cache_recall c ON c.row_id = cache_recall_fts.rowid
330
- WHERE cache_recall_fts MATCH ?
331
- ORDER BY bm25(cache_recall_fts)
332
- LIMIT ?`
333
- ).all(matchExpr, limit);
334
- return mapRowsToHits(rows, query);
335
- }
336
- function likeSearch(query, type, limit) {
337
- const trimmed = query.trim();
338
- if (!trimmed) return [];
339
- const db = getDb(globalDbPath());
340
- const needle = `%${trimmed.replace(/\\/g, "\\\\").replace(/[%_]/g, (c) => `\\${c}`)}%`;
341
- const rows = type !== void 0 ? db.prepare(
342
- `SELECT entry_id AS id, cache_type AS cacheType, label, content, stored_at AS storedAt
343
- FROM cache_recall
344
- WHERE cache_type = ? AND (label LIKE ? ESCAPE '\\' OR content LIKE ? ESCAPE '\\')
345
- ORDER BY stored_at DESC
346
- LIMIT ?`
347
- ).all(type, needle, needle, limit) : db.prepare(
348
- `SELECT entry_id AS id, cache_type AS cacheType, label, content, stored_at AS storedAt
349
- FROM cache_recall
350
- WHERE label LIKE ? ESCAPE '\\' OR content LIKE ? ESCAPE '\\'
351
- ORDER BY stored_at DESC
352
- LIMIT ?`
353
- ).all(needle, needle, limit);
354
- return mapRowsToHits(rows, trimmed);
355
- }
356
- function listRecentRecall(opts = {}) {
357
- const limit = opts.limit ?? 10;
358
- const columns = `SELECT entry_id AS id, cache_type AS cacheType, label, content, stored_at AS storedAt FROM cache_recall`;
359
- try {
360
- const db = getDb(globalDbPath());
361
- const rows = opts.type !== void 0 ? db.prepare(`${columns} WHERE cache_type = ? ORDER BY stored_at DESC LIMIT ?`).all(opts.type, limit) : db.prepare(`${columns} ORDER BY stored_at DESC LIMIT ?`).all(limit);
362
- return mapRowsToHits(rows, "");
363
- } catch {
364
- return [];
365
- }
366
- }
367
- function searchRecall(query, opts = {}) {
368
- const limit = opts.limit ?? 10;
369
- if (query.trim() === "") return [];
370
- try {
371
- if (hasFtsTable()) {
372
- return ftsSearch(query, opts.type, limit);
373
- }
374
- return likeSearch(query, opts.type, limit);
375
- } catch {
376
- try {
377
- return likeSearch(query, opts.type, limit);
378
- } catch {
379
- return [];
380
- }
381
- }
382
- }
383
-
384
- // src/bash_output_cache.ts
385
- import { readdirSync, readFileSync, statSync } from "fs";
386
- import { resolve } from "path";
387
- var BASH_OUTPUT_SUBDIR = "bash_outputs";
388
- var _byId = /* @__PURE__ */ new Map();
389
- var COMMAND_PATTERNS = {
390
- gitMutable: /^\s*git\s+(diff|status)\b/i,
391
- gitImmutable: /^\s*git\s+show\s+[0-9a-f]{40}\b/i,
392
- gitDiffScoped: /\s--\s+\S/,
393
- dirListing: /^\s*(?:ls|eza|exa|dir|Get-ChildItem|gci)\b/i,
394
- depList: /^\s*(?:npm\s+(?:-\S+\s+)*(?:ls|list)\b|pip\s+(?:-\S+\s+)*(?:list|freeze)\b|uv\s+pip\s+(?:-\S+\s+)*(?:list|freeze)\b|pnpm\s+(?:-\S+\s+)*(?:list|ls)\b|yarn\s+(?:-\S+\s+)*(?:list)\b|cargo\s+(?:-\S+\s+)*tree\b|bundle\s+(?:-\S+\s+)*(?:list|show)\b|composer\s+(?:-\S+\s+)*show\b)/i,
395
- npmInstall: /^\s*npm\s+(?:-\S+\s+)*(?:install|ci)\b/i,
396
- npmAudit: /^\s*npm\s+(?:-\S+\s+)*audit\b(?!.*(?:--fix|fix)\b)/i,
397
- npmOutdated: /^\s*npm\s+(?:-\S+\s+)*outdated\b/i,
398
- envProbe: /^\s*(?:node\s+(?:-v|--version)|npm\s+(?:-v|--version)|python3?\s+(?:(?:-V)\b|--?version)|git\s+--version|uv\s+--version|go\s+version|rustc\s+--version|cargo\s+--version|java\s+--version|ruby\s+--version|gem\s+--version|php\s+--version|which\b|where\b)/i,
399
- npx: /^\s*npx\s+(?:--?yes\s+)?(?!.*\b(?:install|add|remove|uninstall|i|rm|update|upgrade|set|get|publish|link|ci|audit|shrinkwrap|dedupe|prune|rebuild)\b)/i,
400
- gitPush: /^\s*git\s+push\b/i,
401
- testRunner: /^\s*(?:npx\s+)?(?:pytest|vitest|jest|go\s+test)\b/i,
402
- lintCommand: /^\s*(?:(?:npx\s+)?eslint|(?:uv\s+run\s+)?ruff)\b/i,
403
- npmRunScript: /^\s*npm\s+run(?:-script)?\b/i,
404
- catCommand: /^\s*cat\b/i
405
- };
406
- var DEP_LOCKFILES = {
407
- npm: ["package-lock.json", "yarn.lock"],
408
- pip: ["requirements.txt"],
409
- uv: ["uv.lock", "requirements.txt"],
410
- pnpm: ["pnpm-lock.yaml"],
411
- yarn: ["yarn.lock"],
412
- cargo: ["Cargo.lock"],
413
- bundle: ["Gemfile.lock"],
414
- composer: ["composer.lock"]
415
- };
416
- function isCommandOfType(cmd, type) {
417
- const pattern = COMMAND_PATTERNS[type];
418
- return pattern?.test(cmd) ?? false;
419
- }
420
- var isGitMutableCommand = (cmd) => isCommandOfType(cmd, "gitMutable");
421
- var isDirListingCommand = (cmd) => isCommandOfType(cmd, "dirListing");
422
- var isDepListCommand = (cmd) => isCommandOfType(cmd, "depList");
423
- var isNpmInstallCommand = (cmd) => isCommandOfType(cmd, "npmInstall");
424
- var isNpmAuditCommand = (cmd) => isCommandOfType(cmd, "npmAudit");
425
- var isNpmOutdatedCommand = (cmd) => isCommandOfType(cmd, "npmOutdated");
426
- var isGitPushCommand = (cmd) => isCommandOfType(cmd, "gitPush");
427
- var isTestRunnerCommand = (cmd) => isCommandOfType(cmd, "testRunner");
428
- var isLintCommand = (cmd) => isCommandOfType(cmd, "lintCommand");
429
- var isNpmRunScriptCommand = (cmd) => isCommandOfType(cmd, "npmRunScript");
430
- var isCatCommand = (cmd) => isCommandOfType(cmd, "catCommand");
431
- function isScopedGitStatusOrDiffStatCommand(cmd) {
432
- if (!isCommandOfType(cmd, "gitMutable")) return false;
433
- if (!isCommandOfType(cmd, "gitDiffScoped")) return false;
434
- if (/^\s*git\s+diff\b/i.test(cmd) && !/--stat\b/.test(cmd)) return false;
435
- return true;
436
- }
437
- function gitStateFingerprintSync(cwd) {
438
- try {
439
- const headResult = runGit(["rev-parse", "HEAD"], { cwd });
440
- if (headResult.exitCode !== 0) return null;
441
- const headSha = headResult.stdout.trim();
442
- let statusHash = "";
443
- const statusResult = runGit(["status", "--porcelain"], { cwd });
444
- if (statusResult.exitCode === 0) {
445
- statusHash = shortFingerprint(statusResult.stdout);
446
- }
447
- const key = `${headSha}\0${statusHash}`;
448
- return shortFingerprint(key);
449
- } catch {
450
- return null;
451
- }
452
- }
453
- var DIR_FINGERPRINT_LISTING_CAP_ENTRIES = 1e4;
454
- function dirStateFingerprintSync(path) {
455
- try {
456
- const stat = statSync(path);
457
- if (!stat.isDirectory()) return null;
458
- const entries = readdirSync(path);
459
- if (entries.length <= DIR_FINGERPRINT_LISTING_CAP_ENTRIES) {
460
- return shortFingerprint(entries.slice().sort().join("\0"));
461
- }
462
- return shortFingerprint(stat.mtimeMs.toString());
463
- } catch {
464
- return null;
465
- }
466
- }
467
- var FILE_FINGERPRINT_CONTENT_CAP_BYTES = 2 * 1024 * 1024;
468
- function fileStateFingerprintSync(path) {
469
- try {
470
- const stat = statSync(path);
471
- if (!stat.isFile()) return null;
472
- if (stat.size <= FILE_FINGERPRINT_CONTENT_CAP_BYTES) {
473
- return shortFingerprint(readFileSync(path));
474
- }
475
- return shortFingerprint(`${stat.mtimeMs}\0${stat.size}`);
476
- } catch {
477
- return null;
478
- }
479
- }
480
- function depLockfileFingerprintSync(cmd, cwd) {
481
- if (!cwd) return null;
482
- const stripped = cmd.trim();
483
- const firstToken = stripped.split(/\s+/)[0]?.toLowerCase() || "";
484
- if (!firstToken) return null;
485
- const candidates = firstToken === "uv" ? DEP_LOCKFILES["uv"] : DEP_LOCKFILES[firstToken];
486
- if (!candidates) return null;
487
- for (const lockfile of candidates) {
488
- try {
489
- const content = readFileSync(resolve(cwd, lockfile));
490
- return shortFingerprint(content);
491
- } catch {
492
- continue;
493
- }
494
- }
495
- return null;
496
- }
497
- function normalizeCommandForCacheKey(cmd) {
498
- const trimmed = cmd.trim();
499
- const tokens = [];
500
- let current = "";
501
- let quoteChar = null;
502
- const flush = () => {
503
- if (current.length > 0) {
504
- tokens.push(current);
505
- current = "";
506
- }
507
- };
508
- for (let i = 0; i < trimmed.length; i++) {
509
- const ch = trimmed[i];
510
- if (quoteChar !== null) {
511
- current += ch;
512
- if (ch === quoteChar) quoteChar = null;
513
- continue;
514
- }
515
- if (ch === '"' || ch === "'") {
516
- quoteChar = ch;
517
- current += ch;
518
- continue;
519
- }
520
- if (/\s/.test(ch)) {
521
- flush();
522
- continue;
523
- }
524
- current += ch === "\\" ? "/" : ch;
525
- }
526
- flush();
527
- const normalized_tokens = tokens.map((token) => {
528
- if (token.startsWith("-") || ["&&", "||", "|", ">", ">>", ";", "&"].includes(token)) {
529
- return token;
530
- }
531
- if (token.startsWith("./") && !token.startsWith("../")) {
532
- token = token.slice(2);
533
- }
534
- if (token.endsWith("/") && token !== "/") {
535
- token = token.slice(0, -1);
536
- }
537
- return token || ".";
538
- });
539
- return normalized_tokens.join(" ");
540
- }
541
- async function commandHash(command, cwd = null) {
542
- const normalized = normalizeCommandForCacheKey(command);
543
- let key = cwd ? `${normalizePath(cwd)}\0${normalized}` : normalized;
544
- if (cwd && isGitMutableCommand(command)) {
545
- const fp = gitStateFingerprintSync(cwd);
546
- if (fp) key = `${key}\0git:${fp}`;
547
- }
548
- if (cwd && isDirListingCommand(command)) {
549
- const target = extractFirstPathArg(command, cwd, cwd);
550
- if (target) {
551
- const fp = dirStateFingerprintSync(target);
552
- if (fp) key = `${key}\0dir:${fp}`;
553
- }
554
- }
555
- if (isDepListCommand(command)) {
556
- const fp = depLockfileFingerprintSync(command, cwd);
557
- if (fp) key = `${key}\0lockfile:${fp}`;
558
- }
559
- if (cwd && (isNpmInstallCommand(command) || isNpmAuditCommand(command) || isNpmOutdatedCommand(command))) {
560
- const fp = depLockfileFingerprintSync(command, cwd);
561
- if (fp) key = `${key}\0npm-install:${fp}`;
562
- }
563
- return shortFingerprint(key);
564
- }
565
- function computeBashFingerprints(command, cwd) {
566
- const fingerprints = {};
567
- if (cwd && (isGitMutableCommand(command) || isGitPushCommand(command) || isTestRunnerCommand(command) || isLintCommand(command) || isBuildCommand(command) || isNpmRunScriptCommand(command))) {
568
- const fp = gitStateFingerprintSync(cwd);
569
- if (fp) fingerprints.git = fp;
570
- }
571
- if (cwd && isDirListingCommand(command)) {
572
- const target = extractFirstPathArg(command, cwd, cwd);
573
- if (target) {
574
- const fp = dirStateFingerprintSync(target);
575
- if (fp) fingerprints.dir = fp;
576
- }
577
- }
578
- if (isDepListCommand(command) || cwd && (isNpmInstallCommand(command) || isNpmAuditCommand(command) || isNpmOutdatedCommand(command))) {
579
- const fp = depLockfileFingerprintSync(command, cwd);
580
- if (fp) fingerprints.lockfile = fp;
581
- }
582
- if (cwd && isCatCommand(command)) {
583
- const target = extractFirstPathArg(command, cwd, null);
584
- if (target) {
585
- const fp = fileStateFingerprintSync(target);
586
- if (fp) fingerprints.file = fp;
587
- }
588
- }
589
- return Object.keys(fingerprints).length > 0 ? fingerprints : void 0;
590
- }
591
- function isBashEntryStale(entry, command, cwd) {
592
- const stored = entry.fingerprints;
593
- if (!stored) return false;
594
- const current = computeBashFingerprints(command, cwd);
595
- if (stored.git !== void 0 && stored.git !== current?.git) return true;
596
- if (stored.dir !== void 0 && stored.dir !== current?.dir) return true;
597
- if (stored.lockfile !== void 0 && stored.lockfile !== current?.lockfile) return true;
598
- if (stored.file !== void 0 && stored.file !== current?.file) return true;
599
- return false;
600
- }
601
- function tokenizeShellArgs(cmd) {
602
- const trimmed = cmd.trim();
603
- const tokens = [];
604
- let current = "";
605
- let quoteChar = null;
606
- let hasToken = false;
607
- for (let i = 0; i < trimmed.length; i++) {
608
- const ch = trimmed[i];
609
- if (quoteChar !== null) {
610
- if (ch === quoteChar) {
611
- quoteChar = null;
612
- } else {
613
- current += ch;
614
- }
615
- continue;
616
- }
617
- if (ch === '"' || ch === "'") {
618
- quoteChar = ch;
619
- hasToken = true;
620
- continue;
621
- }
622
- if (/\s/.test(ch)) {
623
- if (hasToken) {
624
- tokens.push(current);
625
- current = "";
626
- hasToken = false;
627
- }
628
- continue;
629
- }
630
- current += ch;
631
- hasToken = true;
632
- }
633
- if (hasToken) tokens.push(current);
634
- return tokens;
635
- }
636
- function extractFirstPathArg(cmd, cwd, fallback) {
637
- const tokens = tokenizeShellArgs(cmd);
638
- for (let i = 1; i < tokens.length; i++) {
639
- const token = tokens[i];
640
- if (!token.startsWith("-")) {
641
- if (!token.startsWith("/")) {
642
- return resolve(cwd, token);
643
- }
644
- return token;
645
- }
646
- }
647
- return fallback;
648
- }
649
- var ISSUE_LINE_PATTERN = /\b(?:error|fail(?:ed|ure)?|warning)\b/i;
650
- function summarizeOutputDelta(oldOutput, newOutput) {
651
- if (oldOutput === newOutput) return null;
652
- const oldLines = oldOutput.split("\n");
653
- const newLines = newOutput.split("\n");
654
- const oldIssueLines = oldLines.filter((l) => ISSUE_LINE_PATTERN.test(l));
655
- if (oldIssueLines.length > 0) {
656
- const newIssueLines = newLines.filter((l) => ISSUE_LINE_PATTERN.test(l));
657
- const priorTotal = oldIssueLines.length;
658
- const availableCounts = /* @__PURE__ */ new Map();
659
- for (const l of newIssueLines) availableCounts.set(l, (availableCounts.get(l) ?? 0) + 1);
660
- let resolved = 0;
661
- for (const l of oldIssueLines) {
662
- const avail = availableCounts.get(l) ?? 0;
663
- if (avail > 0) {
664
- availableCounts.set(l, avail - 1);
665
- } else {
666
- resolved++;
667
- }
668
- }
669
- const remaining = newIssueLines.length;
670
- return `[token-goat: delta] ${resolved} of ${priorTotal} prior issues resolved; remaining: ${remaining}`;
671
- }
672
- return `[token-goat: delta] output changed: ${oldLines.length} -> ${newLines.length} lines`;
673
- }
674
- async function storeBashOutput(command, output, exitCode, cwd = null) {
675
- const id = await commandHash(command, cwd);
676
- const fingerprints = computeBashFingerprints(command, cwd);
677
- const redactedOutput = redactSecrets(output).text;
678
- const redactedCommand = redactSecrets(command).text;
679
- const entry = {
680
- id,
681
- command: redactedCommand,
682
- output: redactedOutput,
683
- exitCode,
684
- storedAt: Date.now(),
685
- sizeBytes: Buffer.byteLength(redactedOutput, "utf-8"),
686
- ...fingerprints ? { fingerprints } : {}
687
- };
688
- _byId.set(id, entry);
689
- storeBlob(BASH_OUTPUT_SUBDIR, id, entry);
690
- indexRecallEntry("bash", id, redactedCommand, `${redactedCommand}
691
- ${redactedOutput}`, entry.storedAt);
692
- return id;
693
- }
694
- function coerceBashEntry(raw) {
695
- if (raw === null || typeof raw !== "object") return null;
696
- const o = raw;
697
- if (typeof o["id"] !== "string" || typeof o["command"] !== "string" || typeof o["output"] !== "string" || typeof o["exitCode"] !== "number" || typeof o["storedAt"] !== "number" || typeof o["sizeBytes"] !== "number") {
698
- return null;
699
- }
700
- const entry = {
701
- id: o["id"],
702
- command: o["command"],
703
- output: o["output"],
704
- exitCode: o["exitCode"],
705
- storedAt: o["storedAt"],
706
- sizeBytes: o["sizeBytes"]
707
- };
708
- const rawFingerprints = o["fingerprints"];
709
- if (rawFingerprints !== null && typeof rawFingerprints === "object") {
710
- const f = rawFingerprints;
711
- const fingerprints = {};
712
- if (typeof f["git"] === "string") fingerprints.git = f["git"];
713
- if (typeof f["dir"] === "string") fingerprints.dir = f["dir"];
714
- if (typeof f["lockfile"] === "string") fingerprints.lockfile = f["lockfile"];
715
- if (typeof f["file"] === "string") fingerprints.file = f["file"];
716
- if (Object.keys(fingerprints).length > 0) return { ...entry, fingerprints };
717
- }
718
- return entry;
719
- }
720
- function getBashOutput(id) {
721
- const hit = _byId.get(id);
722
- if (hit !== void 0) return hit;
723
- if (isBlobStale(BASH_OUTPUT_SUBDIR, id)) return null;
724
- const entry = coerceBashEntry(loadBlob(BASH_OUTPUT_SUBDIR, id));
725
- if (entry === null) return null;
726
- _byId.set(id, entry);
727
- return entry;
728
- }
729
- registerReset(() => {
730
- _byId = /* @__PURE__ */ new Map();
731
- });
732
-
733
- // src/cli_commands.ts
734
- function toEntry(cmd) {
735
- return {
736
- name: cmd.name(),
737
- description: cmd.description(),
738
- aliases: cmd.aliases(),
739
- options: cmd.options.map((o) => ({ flags: o.flags, description: o.description })),
740
- arguments: cmd.registeredArguments.map((a) => ({ name: a.name(), description: a.description, required: a.required })),
741
- subcommands: cmd.commands.filter((sub) => sub.name() !== "help").map(toEntry)
742
- };
743
- }
744
- function buildCommandManifest(program) {
745
- return program.commands.filter((cmd) => cmd.name() !== "help").map(toEntry);
746
- }
747
- function flattenCommandNames(manifest) {
748
- const names = [];
749
- for (const entry of manifest) {
750
- names.push(entry.name);
751
- for (const sub of entry.subcommands) {
752
- names.push(`${entry.name} ${sub.name}`);
753
- }
754
- }
755
- return names;
756
- }
757
- function entryMatches(entry, re, pattern) {
758
- const haystacks = [entry.name, entry.description, ...entry.aliases];
759
- return haystacks.some((h) => re !== null ? re.test(h) : h.includes(pattern));
760
- }
761
- function filterCommandManifest(manifest, pattern) {
762
- let re;
763
- try {
764
- re = new RegExp(pattern);
765
- } catch {
766
- re = null;
767
- }
768
- const result = [];
769
- for (const entry of manifest) {
770
- if (entryMatches(entry, re, pattern)) {
771
- result.push(entry);
772
- continue;
773
- }
774
- const matchingSubs = entry.subcommands.filter((sub) => entryMatches(sub, re, pattern));
775
- if (matchingSubs.length > 0) {
776
- result.push({ ...entry, subcommands: matchingSubs });
777
- }
778
- }
779
- return result;
780
- }
781
- function formatCommandManifest(manifest) {
782
- const lines = [];
783
- lines.push("# token-goat commands");
784
- lines.push("");
785
- for (const entry of manifest) {
786
- lines.push(`## ${entry.name}${entry.aliases.length ? " (alias: " + entry.aliases.join(", ") + ")" : ""}${entry.description ? " -- " + entry.description : ""}`);
787
- for (const arg of entry.arguments) {
788
- lines.push(` arg: ${arg.name}${arg.required ? "" : " (optional)"}${arg.description ? " -- " + arg.description : ""}`);
789
- }
790
- for (const opt of entry.options) {
791
- lines.push(` ${opt.flags}${opt.description ? " -- " + opt.description : ""}`);
792
- }
793
- for (const sub of entry.subcommands) {
794
- lines.push(` ${entry.name} ${sub.name}${sub.description ? " -- " + sub.description : ""}`);
795
- }
796
- lines.push("");
797
- }
798
- return lines.join("\n").trimEnd();
799
- }
800
-
801
- // src/web_cache.ts
802
- var WEB_OUTPUT_SUBDIR = "web_outputs";
803
- var _byId2 = /* @__PURE__ */ new Map();
804
- var _rawById = /* @__PURE__ */ new Map();
805
- var _urlIndex = /* @__PURE__ */ new Map();
806
- function cacheIdForUrl(url) {
807
- return shortFingerprint(url);
808
- }
809
- function storeWebOutput(url, content, dedupKey = url, rawContent) {
810
- const cacheId = cacheIdForUrl(dedupKey);
811
- const redactedContent = redactSecrets(content).text;
812
- _byId2.set(cacheId, redactedContent);
813
- _urlIndex.set(url, cacheId);
814
- const redactedRaw = rawContent !== void 0 && rawContent !== content ? redactSecrets(rawContent).text : void 0;
815
- if (redactedRaw !== void 0) _rawById.set(cacheId, redactedRaw);
816
- else _rawById.delete(cacheId);
817
- storeBlob(WEB_OUTPUT_SUBDIR, cacheId, redactedRaw !== void 0 ? { url, content: redactedContent, raw: redactedRaw } : { url, content: redactedContent });
818
- const redactedUrl = redactSecrets(url).text;
819
- indexRecallEntry("web", cacheId, redactedUrl, `${redactedUrl}
820
- ${redactedContent}`, Date.now());
821
- return cacheId;
822
- }
823
- function coerceWebBlob(raw) {
824
- if (raw === null || typeof raw !== "object") return null;
825
- const o = raw;
826
- if (typeof o["content"] !== "string") return null;
827
- return { url: typeof o["url"] === "string" ? o["url"] : null, content: o["content"], raw: typeof o["raw"] === "string" ? o["raw"] : null };
828
- }
829
- function getWebOutput(cacheId) {
830
- const hit = _byId2.get(cacheId);
831
- if (hit !== void 0) return hit;
832
- if (isBlobStale(WEB_OUTPUT_SUBDIR, cacheId)) return null;
833
- const blob = coerceWebBlob(loadBlob(WEB_OUTPUT_SUBDIR, cacheId));
834
- if (blob === null) return null;
835
- _byId2.set(cacheId, blob.content);
836
- if (blob.raw !== null) _rawById.set(cacheId, blob.raw);
837
- if (blob.url !== null) _urlIndex.set(blob.url, cacheId);
838
- return blob.content;
839
- }
840
- function getWebOutputRaw(cacheId) {
841
- const cleaned = getWebOutput(cacheId);
842
- if (cleaned === null) return null;
843
- return _rawById.get(cacheId) ?? cleaned;
844
- }
845
- function getWebOutputByUrlFromDisk(url, dedupKey = url) {
846
- const cacheId = cacheIdForUrl(dedupKey);
847
- const content = getWebOutput(cacheId);
848
- if (content === null) return null;
849
- return { cacheId, content };
850
- }
851
- registerReset(() => {
852
- _byId2 = /* @__PURE__ */ new Map();
853
- _rawById = /* @__PURE__ */ new Map();
854
- _urlIndex = /* @__PURE__ */ new Map();
855
- });
856
-
857
- // src/symbol_body_probe.ts
858
- import * as fs from "fs";
859
- var OVERSIZED_BODY_PROBE_SQL = `SELECT 1 FROM symbols WHERE LENGTH(body) > ${SYMBOL_BODY_CHAR_CAP} LIMIT 1`;
860
- function checkSymbolBodySize(dbPath) {
861
- if (!fs.existsSync(dbPath)) {
862
- return { name: "Symbol body size", status: "ok", message: "no database yet" };
863
- }
864
- try {
865
- const db = getDb(dbPath);
866
- const row = db.prepare(OVERSIZED_BODY_PROBE_SQL).get();
867
- if (row !== void 0) {
868
- return {
869
- name: "Symbol body size",
870
- status: "warn",
871
- message: `one or more stored symbol bodies exceed the ${SYMBOL_BODY_CHAR_CAP}-char cap enforced by boundSymbolBody -- likely a pre-fix leftover from a minified/generated file. A plain 'token-goat reclaim-index' (VACUUM only) CANNOT remove these rows -- it only reclaims freed pages, it never deletes row content. Only 'token-goat reclaim-index --rebuild' drops and re-derives them under the cap (stop the worker first with 'token-goat worker stop', since reclaim-index refuses to run while it's live); --rebuild reparses and re-embeds every indexed file across every project and can take a long time on a large multi-project index`
872
- };
873
- }
874
- return { name: "Symbol body size", status: "ok", message: "no stored symbol body exceeds the cap" };
875
- } catch (err) {
876
- return {
877
- name: "Symbol body size",
878
- status: "warn",
879
- message: `could not query symbol body size: ${extractErrorMessage(err)}`
880
- };
881
- }
882
- }
883
-
884
- export {
885
- GEMINI_TOOL_NAME_MAP,
886
- normalizePayload,
887
- HOOK_EVENTS,
888
- MAX_STDIN_BYTES,
889
- readStdinJson,
890
- isRecallCacheType,
891
- indexRecallEntry,
892
- listRecentRecall,
893
- searchRecall,
894
- BASH_OUTPUT_SUBDIR,
895
- isScopedGitStatusOrDiffStatCommand,
896
- normalizeCommandForCacheKey,
897
- commandHash,
898
- isBashEntryStale,
899
- summarizeOutputDelta,
900
- storeBashOutput,
901
- getBashOutput,
902
- buildCommandManifest,
903
- flattenCommandNames,
904
- filterCommandManifest,
905
- formatCommandManifest,
906
- WEB_OUTPUT_SUBDIR,
907
- storeWebOutput,
908
- getWebOutput,
909
- getWebOutputRaw,
910
- getWebOutputByUrlFromDisk,
911
- checkSymbolBodySize
912
- };