token-goat 2.9.19 → 2.9.21

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 (50) hide show
  1. package/README.md +3 -1
  2. package/dist/{token-goat-chunk-JNIFSVWM.mjs → token-goat-chunk-2ZHD3XCD.mjs} +5 -1
  3. package/dist/{token-goat-chunk-P46RTOAQ.mjs → token-goat-chunk-4IIU3GNS.mjs} +10 -7
  4. package/dist/{token-goat-chunk-2GTB5RBZ.mjs → token-goat-chunk-4V4OZHHV.mjs} +11 -271
  5. package/dist/token-goat-chunk-64OEF3UN.mjs +128 -0
  6. package/dist/{token-goat-chunk-AVRD7SBO.mjs → token-goat-chunk-6AQLZMCO.mjs} +270 -29
  7. package/dist/{token-goat-chunk-LACH3WKS.mjs → token-goat-chunk-BIEDTF6V.mjs} +3 -3
  8. package/dist/{token-goat-chunk-PIAESKVU.mjs → token-goat-chunk-BKHJRFDE.mjs} +568 -429
  9. package/dist/token-goat-chunk-CAHN4QU7.mjs +26 -0
  10. package/dist/{token-goat-chunk-MU6I4IDI.mjs → token-goat-chunk-CMAJGP3E.mjs} +11 -7
  11. package/dist/token-goat-chunk-CMHBTXMY.mjs +41 -0
  12. package/dist/{token-goat-chunk-HPMMLV37.mjs → token-goat-chunk-CZ5ZFJSN.mjs} +256 -341
  13. package/dist/{token-goat-chunk-7DGTVBWP.mjs → token-goat-chunk-DDP64XR7.mjs} +159 -1863
  14. package/dist/{token-goat-chunk-FX5DHKGF.mjs → token-goat-chunk-DISVHOW6.mjs} +4 -4
  15. package/dist/{token-goat-chunk-L2UOJBV4.mjs → token-goat-chunk-FZN77SZD.mjs} +27 -19
  16. package/dist/{token-goat-chunk-DA6TIDLI.mjs → token-goat-chunk-GO7OWONJ.mjs} +18 -13
  17. package/dist/{token-goat-chunk-EP7TCWHM.mjs → token-goat-chunk-IAMIQLCV.mjs} +83 -66
  18. package/dist/{token-goat-chunk-NJKNAC37.mjs → token-goat-chunk-IJUHQX2M.mjs} +1 -1
  19. package/dist/token-goat-chunk-KQI24O4B.mjs +292 -0
  20. package/dist/{token-goat-chunk-GYC34EGE.mjs → token-goat-chunk-KRPJIXCT.mjs} +2 -2
  21. package/dist/token-goat-chunk-KVAXTA2W.mjs +198 -0
  22. package/dist/{token-goat-chunk-FJZ5MZK7.mjs → token-goat-chunk-LK3YOKDF.mjs} +2 -2
  23. package/dist/token-goat-chunk-M43UJVYQ.mjs +1816 -0
  24. package/dist/{token-goat-chunk-SOKAPOOC.mjs → token-goat-chunk-M6CDVASA.mjs} +46 -47
  25. package/dist/token-goat-chunk-MECDAPXB.mjs +1989 -0
  26. package/dist/token-goat-chunk-MEKVJZUN.mjs +324 -0
  27. package/dist/token-goat-chunk-MQACN7VG.mjs +1379 -0
  28. package/dist/{token-goat-chunk-3W5P673V.mjs → token-goat-chunk-MZQORUX5.mjs} +1 -1
  29. package/dist/{token-goat-chunk-DHIXE6GV.mjs → token-goat-chunk-ORRDWKLA.mjs} +1 -1
  30. package/dist/{token-goat-chunk-F24NE3FO.mjs → token-goat-chunk-PCR43MCM.mjs} +2 -2
  31. package/dist/token-goat-chunk-PDYQA3AP.mjs +248 -0
  32. package/dist/{token-goat-chunk-S7RFIX4Y.mjs → token-goat-chunk-PHDNWISV.mjs} +45 -14
  33. package/dist/token-goat-chunk-PPYZS7TI.mjs +304 -0
  34. package/dist/{token-goat-chunk-BTKDZW7F.mjs → token-goat-chunk-QTIRVK3M.mjs} +1 -1
  35. package/dist/{token-goat-chunk-62VJ47WN.mjs → token-goat-chunk-QUM3UNWS.mjs} +5 -5
  36. package/dist/{token-goat-chunk-KWM6KBPA.mjs → token-goat-chunk-SHV6QQAS.mjs} +1 -1
  37. package/dist/{token-goat-chunk-FQYMZASI.mjs → token-goat-chunk-SMUYPBF5.mjs} +796 -4464
  38. package/dist/{token-goat-chunk-ZGQXRYKE.mjs → token-goat-chunk-TGBSMHPM.mjs} +1 -1
  39. package/dist/{token-goat-chunk-EXLLLIC3.mjs → token-goat-chunk-VXKT3W4C.mjs} +3 -3
  40. package/dist/{token-goat-chunk-WKO5TVNA.mjs → token-goat-chunk-XJUPKJF5.mjs} +544 -576
  41. package/dist/{token-goat-chunk-IBTFGZBX.mjs → token-goat-chunk-XQSSPX3X.mjs} +2 -2
  42. package/dist/{token-goat-chunk-VJQBYSXG.mjs → token-goat-chunk-ZJWPJL7O.mjs} +3 -3
  43. package/dist/token-goat-hook.mjs +17 -11
  44. package/dist/token-goat.core.mjs +27 -19
  45. package/docs/C4_RUNTIME_ARCHITECTURE.md +1 -1
  46. package/docs/cli.md +13 -12
  47. package/docs/install.md +1 -1
  48. package/docs/security.md +2 -2
  49. package/package.json +3 -2
  50. package/dist/token-goat-chunk-NYFYIRG3.mjs +0 -35
@@ -0,0 +1,1816 @@
1
+ import { createRequire as __cjsRequire } from 'node:module';
2
+ const require = __cjsRequire(import.meta.url);
3
+ import {
4
+ DEFAULT_MAX_AGE_MS,
5
+ blobPath,
6
+ isBlobStale,
7
+ loadBlob,
8
+ redactSecrets,
9
+ storeBlob,
10
+ stripAnsiEscapes
11
+ } from "./token-goat-chunk-PHDNWISV.mjs";
12
+ import {
13
+ sanitizeFtsQuery
14
+ } from "./token-goat-chunk-XQSSPX3X.mjs";
15
+ import {
16
+ getDb
17
+ } from "./token-goat-chunk-2ZHD3XCD.mjs";
18
+ import {
19
+ shortFingerprint
20
+ } from "./token-goat-chunk-M6CDVASA.mjs";
21
+ import {
22
+ registerReset
23
+ } from "./token-goat-chunk-EEIDFMEM.mjs";
24
+ import {
25
+ globalDbPath,
26
+ runGit
27
+ } from "./token-goat-chunk-TGBSMHPM.mjs";
28
+ import {
29
+ normalizePath
30
+ } from "./token-goat-chunk-NMTKNYGF.mjs";
31
+ import {
32
+ init_define_import_meta_env
33
+ } from "./token-goat-chunk-A37V4PBF.mjs";
34
+
35
+ // src/tool_filters/helpers.ts
36
+ init_define_import_meta_env();
37
+ import * as fs from "node:fs";
38
+ import * as path from "node:path";
39
+ var DEFAULT_MAX_LINES = 1e3;
40
+ var DEFAULT_MAX_BYTES = 64 * 1024;
41
+ var MAX_INSPECT_BYTES = 2 * 1024 * 1024;
42
+ var DEFAULT_MAX_INPUT_BYTES = 500 * 1024;
43
+ var FALLBACK_MAX_LINE_CHARS = 400;
44
+ var LONG_LINE_MAX_CHARS = 1e3;
45
+ var ELIDED_MARKER_RE = /… \[\d+ chars elided\]/;
46
+ var CLIPPED_MARKER_RE = / \.\.\. \[(\d+) chars clipped\] \.\.\. /;
47
+ function getMaxInputBytes() {
48
+ const raw = process.env["TOKEN_GOAT_FILTER_MAX_BYTES"];
49
+ const v = raw ? Number.parseInt(raw, 10) : 0;
50
+ return Number.isFinite(v) && v > 0 ? v : DEFAULT_MAX_INPUT_BYTES;
51
+ }
52
+ function utf8SafeEnd(buf, n) {
53
+ if (n >= buf.length) return buf.length;
54
+ let end = n;
55
+ while (end > 0 && (buf[end] & 192) === 128) end--;
56
+ return end;
57
+ }
58
+ var INPUT_MAX_LINE_CHARS = 4e3;
59
+ function clipWideLines(text, maxChars = INPUT_MAX_LINE_CHARS) {
60
+ if (text.length <= maxChars) return text;
61
+ let clipped = 0;
62
+ const out = text.split("\n").map((line) => {
63
+ if (line.length <= maxChars) return line;
64
+ clipped += 1;
65
+ const keep = Math.floor(maxChars / 2);
66
+ return line.slice(0, keep) + ` ... [${line.length - maxChars} chars clipped] ... ` + line.slice(line.length - (maxChars - keep));
67
+ });
68
+ return clipped === 0 ? text : out.join("\n");
69
+ }
70
+ function clampKeepingEnds(text, maxBytes) {
71
+ const buf = Buffer.from(text, "utf8");
72
+ if (buf.length <= maxBytes) return null;
73
+ const lines = text.split("\n");
74
+ const budget = maxBytes - Buffer.byteLength(`... [${lines.length} more lines elided by token-goat]
75
+ `, "utf8");
76
+ const half = Math.floor(budget / 2);
77
+ let headEnd = 0;
78
+ for (let used = 0; headEnd < lines.length; headEnd++) {
79
+ const n = Buffer.byteLength(lines[headEnd], "utf8") + 1;
80
+ if (used + n > half) break;
81
+ used += n;
82
+ }
83
+ let tailStart = lines.length;
84
+ for (let used = 0; tailStart > headEnd; tailStart--) {
85
+ const n = Buffer.byteLength(lines[tailStart - 1], "utf8") + 1;
86
+ if (used + n > half) break;
87
+ used += n;
88
+ }
89
+ if (headEnd === 0 && tailStart === lines.length) return buf.subarray(0, utf8SafeEnd(buf, maxBytes)).toString("utf8");
90
+ const elided = tailStart - headEnd;
91
+ return [
92
+ ...lines.slice(0, headEnd),
93
+ `... [${elided} more line${elided === 1 ? "" : "s"} elided by token-goat]`,
94
+ ...lines.slice(tailStart)
95
+ ].join("\n");
96
+ }
97
+ function compressionMarker(filter, pct) {
98
+ return `
99
+ [token-goat: ${filter} filter -${Math.round(pct)}%; disable via TOKEN_GOAT_BASH_COMPRESS]`;
100
+ }
101
+ function combineStreams(stdout, stderr) {
102
+ if (stderr.trim() && stdout.trim()) return `${stdout.replace(/\s+$/, "")}
103
+ ---
104
+ ${stderr.replace(/\s+$/, "")}`;
105
+ return stdout.trim() ? stdout.replace(/\s+$/, "") : stderr.replace(/\s+$/, "");
106
+ }
107
+ var ERROR_SIGNAL_RE = /error:|Error:|ERROR|FAILED|failed|fatal:|Traceback|exception:|Exception:|AssertionError|assert |panic:/i;
108
+ var TABLE_ROW_ANOMALY_RE = /\b(?:CrashLoopBackOff|ImagePullBackOff|ErrImagePull|CreateContainerError|CreateContainerConfigError|InvalidImageName|RunContainerError|OOMKilled|Evicted|Terminating|ContainerCreating|PodInitializing|NotReady|SchedulingDisabled|Unschedulable|Pending|Failed|Error|Unknown|Unhealthy|DEGRADED|UNAVAILABLE|STOPPED|STOPPING|TERMINATED|TERMINATING|FAILED|ROLLBACK_COMPLETE|ROLLBACK_FAILED|CREATE_FAILED|UPDATE_FAILED|DELETE_FAILED|pending-install|pending-upgrade|pending-rollback|uninstalling)\b|(?<![\w-])(?:failed|unknown)(?![\w-])/;
109
+ var TIMESTAMP_PREFIX_RE = /^\[?\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(?:\.\d+)?Z?\]?\s*|^\d{2}:\d{2}:\d{2}(?:\.\d+)?\s+/;
110
+ var REDIRECT_TOKEN_RE = /^(\d*)(>>?|<<?).*$|^&>$|^>&.*$/;
111
+ function maskQuotedSpans(cmd) {
112
+ let out = "";
113
+ let i = 0;
114
+ const n = cmd.length;
115
+ while (i < n) {
116
+ const ch = cmd[i];
117
+ if (ch === "\\") {
118
+ out += i + 1 < n ? "xx" : "x";
119
+ i += 2;
120
+ continue;
121
+ }
122
+ if (ch === "'") {
123
+ i += 1;
124
+ const end = cmd.indexOf("'", i);
125
+ const stop = end === -1 ? n : end;
126
+ out += "x".repeat(stop - i);
127
+ i = end === -1 ? n : end + 1;
128
+ continue;
129
+ }
130
+ if (ch === '"') {
131
+ i += 1;
132
+ const start = i;
133
+ while (i < n && cmd[i] !== '"') {
134
+ i += cmd[i] === "\\" && i + 1 < n ? 2 : 1;
135
+ }
136
+ out += "x".repeat(i - start);
137
+ if (i < n) i += 1;
138
+ continue;
139
+ }
140
+ out += ch;
141
+ i += 1;
142
+ }
143
+ return out;
144
+ }
145
+ function hasBareBackgroundOrNewline(cmd) {
146
+ if (cmd.includes("\n") || cmd.includes("\r")) return true;
147
+ const masked = maskQuotedSpans(cmd);
148
+ for (let i = 0; i < masked.length; i++) {
149
+ if (masked[i] !== "&") continue;
150
+ if (masked[i + 1] === "&") {
151
+ i++;
152
+ continue;
153
+ }
154
+ if (masked[i - 1] === "&") continue;
155
+ if (isRedirectAmpersand(masked, i)) continue;
156
+ return true;
157
+ }
158
+ return false;
159
+ }
160
+ function isRedirectAmpersand(cmd, index) {
161
+ const before = cmd[index - 1];
162
+ return before === "<" || before === ">" || cmd[index + 1] === ">";
163
+ }
164
+ function hasUnquotedOperator(cmd, ops) {
165
+ const masked = maskQuotedSpans(cmd);
166
+ return ops.some((op) => masked.includes(op));
167
+ }
168
+ var BYTES_ELIDED_MARKER_RE = /\n\.\.\. \[\d+ bytes elided by token-goat\]$/;
169
+ var OWN_TRAILING_NOTICE_RE = /(?:\n(?:\[token-goat: output capped at ~\d+ tokens\]|\.\.\. \[\d+ bytes elided by token-goat\]))+$/;
170
+ function splitOwnTrailingNotices(text) {
171
+ const m = OWN_TRAILING_NOTICE_RE.exec(text);
172
+ return m ? { body: text.slice(0, m.index), notices: m[0] } : { body: text, notices: "" };
173
+ }
174
+ var DIGITS_RE = /\d+/g;
175
+ var CONTROL_CHAR_RE = /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x9f]/g;
176
+ function utf8Len(s) {
177
+ return Buffer.byteLength(s, "utf8");
178
+ }
179
+ function safeDecode(text) {
180
+ return text.indexOf("\0") === -1 ? text : text.split("\0").join("");
181
+ }
182
+ function sanitizeControlChars(text) {
183
+ return text.replace(CONTROL_CHAR_RE, "");
184
+ }
185
+ function stripProgress(text) {
186
+ if (!text.includes("\r")) return text;
187
+ return text.split("\n").map((line) => {
188
+ const trimmed = line.endsWith("\r") ? line.slice(0, -1) : line;
189
+ return trimmed.includes("\r") ? trimmed.slice(trimmed.lastIndexOf("\r") + 1) : trimmed;
190
+ }).join("\n");
191
+ }
192
+ function normalise(text, opts = {}) {
193
+ if (!text) return "";
194
+ let t = text.replace(/\r\n/g, "\n");
195
+ if (!opts.skipProgress) t = stripProgress(t);
196
+ t = stripAnsiEscapes(t);
197
+ return sanitizeControlChars(t);
198
+ }
199
+ var UUID_RE = /\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/i;
200
+ var LONG_HEX_RE = /\b[0-9a-f]{32,}\b/i;
201
+ var JWT_RE = /\b[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/;
202
+ function hasHighEntropyToken(line) {
203
+ if (UUID_RE.test(line) || LONG_HEX_RE.test(line) || JWT_RE.test(line)) return true;
204
+ for (const tok of line.split(/[\s"'`,;:()[\]{}<>]+/)) {
205
+ if (tok.length < 24) continue;
206
+ if (/[a-z]/.test(tok) && /[A-Z]/.test(tok) && /[0-9]/.test(tok) && /^[A-Za-z0-9+/=_-]+$/.test(tok)) {
207
+ return true;
208
+ }
209
+ }
210
+ return false;
211
+ }
212
+ function dedupeConsecutive(lines, opts = {}) {
213
+ const minRun = opts.minRun ?? 2;
214
+ const fmt = opts.fmt ?? ((line, count2) => `${line} (\xD7${count2})`);
215
+ const entropyBypass = opts.entropyBypass ?? false;
216
+ const out = [];
217
+ let prev = null;
218
+ let count = 0;
219
+ const flush = () => {
220
+ if (prev === null) return;
221
+ if (count >= minRun) out.push(fmt(prev, count));
222
+ else for (let i = 0; i < count; i++) out.push(prev);
223
+ };
224
+ for (const line of lines) {
225
+ if (entropyBypass && hasHighEntropyToken(line)) {
226
+ flush();
227
+ prev = null;
228
+ count = 0;
229
+ out.push(line);
230
+ continue;
231
+ }
232
+ if (line === prev) {
233
+ count += 1;
234
+ continue;
235
+ }
236
+ flush();
237
+ prev = line;
238
+ count = 1;
239
+ }
240
+ flush();
241
+ return out;
242
+ }
243
+ function dedupeNumericRuns(lines, opts = {}) {
244
+ const minRun = opts.minRun ?? 2;
245
+ const arr = Array.from(lines);
246
+ const out = [];
247
+ let i = 0;
248
+ while (i < arr.length) {
249
+ const key = arr[i].replace(DIGITS_RE, "#");
250
+ let j = i + 1;
251
+ while (j < arr.length && arr[j].replace(DIGITS_RE, "#") === key) j += 1;
252
+ const run = j - i;
253
+ if (run >= minRun) {
254
+ out.push(arr[i]);
255
+ out.push(`... [${run - 1} similar lines collapsed by token-goat]`);
256
+ } else {
257
+ for (let k = i; k < j; k++) out.push(arr[k]);
258
+ }
259
+ i = j;
260
+ }
261
+ return out;
262
+ }
263
+ function truncateMiddle(lines, maxLines, opts = {}) {
264
+ if (lines.length <= maxLines) return lines;
265
+ const headRatio = opts.headRatio ?? 0.4;
266
+ const markerFmt = opts.markerFmt ?? ((n) => `... [${n} lines elided by token-goat]`);
267
+ const headKeep = Math.max(1, Math.floor(maxLines * headRatio));
268
+ const tailKeep = Math.max(1, maxLines - headKeep);
269
+ const elided = lines.length - headKeep - tailKeep;
270
+ return [...lines.slice(0, headKeep), markerFmt(elided), ...lines.slice(lines.length - tailKeep)];
271
+ }
272
+ function truncateMiddleSmart(lines, maxLines, opts = {}) {
273
+ if (lines.length <= maxLines) return lines;
274
+ const headKeep = opts.headKeep ?? 10;
275
+ const tailKeep = opts.tailKeep ?? 10;
276
+ const errorContext = opts.errorContext ?? 2;
277
+ const maxErrorLines = opts.maxErrorLines ?? 10;
278
+ const markerFmt = opts.markerFmt ?? ((n) => `--- ${n} lines omitted ---`);
279
+ const errorIndices = [];
280
+ for (let i = 0; i < lines.length; i++) if (ERROR_SIGNAL_RE.test(lines[i])) errorIndices.push(i);
281
+ if (errorIndices.length === 0) return truncateMiddle(lines, maxLines, { markerFmt });
282
+ const total = lines.length;
283
+ const effHead = Math.min(headKeep, Math.floor(total / 4));
284
+ const effTail = Math.min(tailKeep, Math.floor(total / 4));
285
+ const chosenErrors = errorIndices.length <= maxErrorLines ? errorIndices : [
286
+ ...errorIndices.slice(0, Math.ceil(maxErrorLines / 2)),
287
+ ...errorIndices.slice(errorIndices.length - Math.floor(maxErrorLines / 2))
288
+ ];
289
+ const budgetForMiddle = Math.max(0, maxLines - effHead - effTail);
290
+ const half = Math.ceil(chosenErrors.length / 2);
291
+ const front = chosenErrors.slice(0, half);
292
+ const back = chosenErrors.slice(half).reverse();
293
+ const visitOrder = [];
294
+ for (let k = 0; k < Math.max(front.length, back.length); k++) {
295
+ if (k < front.length) visitOrder.push(front[k]);
296
+ if (k < back.length) visitOrder.push(back[k]);
297
+ }
298
+ const middle = /* @__PURE__ */ new Set();
299
+ outer: for (const ei of visitOrder) {
300
+ for (let ci = Math.max(0, ei - errorContext); ci < Math.min(total, ei + errorContext + 1); ci++) {
301
+ if (ci < effHead || ci >= total - effTail) continue;
302
+ if (middle.size >= budgetForMiddle) break outer;
303
+ middle.add(ci);
304
+ }
305
+ }
306
+ const sortedMiddle = Array.from(middle).sort((a, b) => a - b);
307
+ const result = [];
308
+ const appendSection = (indices) => {
309
+ for (let pos = 0; pos < indices.length; pos++) {
310
+ const idx = indices[pos];
311
+ if (pos > 0) {
312
+ const prevIdx = indices[pos - 1];
313
+ if (idx !== prevIdx + 1) result.push(markerFmt(idx - prevIdx - 1));
314
+ }
315
+ result.push(lines[idx]);
316
+ }
317
+ };
318
+ const headList = Array.from({ length: effHead }, (_, i) => i);
319
+ const tailList = Array.from({ length: effTail }, (_, i) => total - effTail + i);
320
+ appendSection(headList);
321
+ if (sortedMiddle.length) {
322
+ const gapAfterHead = sortedMiddle[0] - (headList.length ? headList[headList.length - 1] : -1) - 1;
323
+ if (gapAfterHead > 0) result.push(markerFmt(gapAfterHead));
324
+ appendSection(sortedMiddle);
325
+ }
326
+ if (tailList.length) {
327
+ const lastKept = sortedMiddle.length ? sortedMiddle[sortedMiddle.length - 1] : headList.length ? headList[headList.length - 1] : -1;
328
+ const gapBeforeTail = tailList[0] - lastKept - 1;
329
+ if (gapBeforeTail > 0) result.push(markerFmt(gapBeforeTail));
330
+ appendSection(tailList);
331
+ }
332
+ return result;
333
+ }
334
+ function capBytes(text, maxBytes) {
335
+ const encoded = Buffer.from(text, "utf8");
336
+ if (encoded.length <= maxBytes) return text;
337
+ const widestMarker = `
338
+ ... [${encoded.length} bytes elided by token-goat]`;
339
+ const budget = maxBytes - Buffer.byteLength(widestMarker, "utf8");
340
+ if (budget <= 0) return widestMarker.trim();
341
+ const kept = clampKeepingEnds(text, budget) ?? text;
342
+ return `${kept}
343
+ ... [${encoded.length - Buffer.byteLength(kept, "utf8")} bytes elided by token-goat]`;
344
+ }
345
+ function capTokens(text, maxTokens) {
346
+ const clean = stripAnsiEscapes(text);
347
+ if (clean.length / 3.5 <= maxTokens) return text;
348
+ const maxBytes = Math.floor(maxTokens * 3.5);
349
+ let truncated = clampKeepingEnds(clean, maxBytes) ?? clean;
350
+ if (!truncated.includes("[token-goat: output capped at")) {
351
+ truncated = truncated.replace(BYTES_ELIDED_MARKER_RE, "");
352
+ truncated += `
353
+ [token-goat: output capped at ~${maxTokens} tokens]`;
354
+ }
355
+ return truncated;
356
+ }
357
+ function headTailCompress(lines, head, tail, label = "items") {
358
+ const total = lines.length;
359
+ if (total <= head + tail) return lines.join("\n");
360
+ const elided = total - head - tail;
361
+ return [...lines.slice(0, head), `... [${elided} more ${label} elided by token-goat]`, ...lines.slice(total - tail)].join(
362
+ "\n"
363
+ );
364
+ }
365
+ function truncateTableRows(text, maxRows, hint) {
366
+ const lines = text.split("\n");
367
+ const nonEmpty = lines.filter((l) => l.trim());
368
+ if (nonEmpty.length <= maxRows + 1) return text;
369
+ const header = nonEmpty[0];
370
+ const rows = nonEmpty.slice(1);
371
+ const wanted = /* @__PURE__ */ new Set();
372
+ for (let i = 0; i < rows.length && wanted.size < maxRows; i++) {
373
+ if (TABLE_ROW_ANOMALY_RE.test(rows[i])) wanted.add(i);
374
+ }
375
+ const anomalies = wanted.size;
376
+ for (let i = 0; i < rows.length && wanted.size < maxRows; i++) wanted.add(i);
377
+ const kept = [...wanted].sort((a, b) => a - b);
378
+ const elided = rows.length - kept.length;
379
+ const note = anomalies ? `[token-goat: ${elided} more rows; ${anomalies} row(s) kept for a not-ready status, the rest from the top; ${hint}]` : `[token-goat: ${elided} more rows; ${hint}]`;
380
+ return `${[header, ...kept.map((i) => rows[i])].join("\n")}
381
+ ${note}`;
382
+ }
383
+ function trimRepeatedPrefix(lines, pattern, keep) {
384
+ const out = [];
385
+ let matched = 0;
386
+ let dropped = 0;
387
+ for (const line of lines) {
388
+ if (pattern.test(line)) {
389
+ matched += 1;
390
+ if (matched <= keep) out.push(line);
391
+ else dropped += 1;
392
+ } else {
393
+ out.push(line);
394
+ }
395
+ }
396
+ if (dropped) out.push(`[token-goat: +${dropped} more lines matching ${pattern.source}]`);
397
+ return out;
398
+ }
399
+ function stripTimestamps(lines) {
400
+ return lines.map((ln) => ln.replace(TIMESTAMP_PREFIX_RE, ""));
401
+ }
402
+ function splitBlocks(text, blockRe) {
403
+ const blocks = [];
404
+ let current = [];
405
+ for (const line of text.split("\n")) {
406
+ if (blockRe.test(line)) {
407
+ if (current.length) blocks.push(current.join("\n"));
408
+ current = [line];
409
+ } else {
410
+ current.push(line);
411
+ }
412
+ }
413
+ if (current.length) blocks.push(current.join("\n"));
414
+ return blocks;
415
+ }
416
+ function maybeNote(notes, value, msg) {
417
+ if (value) notes.push(msg);
418
+ }
419
+ function squeezeBlankLines(text) {
420
+ return text.replace(/\n\s*\n\s*\n+/g, "\n\n");
421
+ }
422
+ function preserveStderrOnError(stdout, stderr, exitCode) {
423
+ if (exitCode !== 0 && stderr.trim()) {
424
+ return stdout.trim() ? `${stdout.replace(/\s+$/, "")}
425
+ ---
426
+ ${stderr.replace(/\s+$/, "")}` : stderr;
427
+ }
428
+ return null;
429
+ }
430
+ var PASSTHROUGH_PREFIXES = /* @__PURE__ */ new Set([
431
+ "sudo",
432
+ "doas",
433
+ "time",
434
+ "nice",
435
+ "ionice",
436
+ "nohup",
437
+ "exec",
438
+ "env",
439
+ "stdbuf",
440
+ "unbuffer",
441
+ "script"
442
+ ]);
443
+ var TWO_TOKEN_PREFIXES = {
444
+ python: /* @__PURE__ */ new Set(["-m"]),
445
+ python3: /* @__PURE__ */ new Set(["-m"]),
446
+ py: /* @__PURE__ */ new Set(["-m"]),
447
+ // `tool` is deliberately absent: `uv tool install/upgrade/uninstall/update <bin>`
448
+ // produces uv's own package-management output (handled by UvFilter.matches()'s
449
+ // own `tool` branch on the unstripped argv) -- it must not be consumed here.
450
+ // `uv tool run <bin>` is the one `tool` subcommand that really does execute
451
+ // `<bin>` and stream its output; it gets a dedicated 3-token strip below.
452
+ uv: /* @__PURE__ */ new Set(["run"]),
453
+ uvx: /* @__PURE__ */ new Set(),
454
+ poetry: /* @__PURE__ */ new Set(["run"]),
455
+ rye: /* @__PURE__ */ new Set(["run"]),
456
+ pdm: /* @__PURE__ */ new Set(["run"]),
457
+ pipenv: /* @__PURE__ */ new Set(["run"]),
458
+ npx: /* @__PURE__ */ new Set(),
459
+ pnpm: /* @__PURE__ */ new Set(["exec", "dlx"]),
460
+ yarn: /* @__PURE__ */ new Set(["exec", "dlx"]),
461
+ bundle: /* @__PURE__ */ new Set(["exec"]),
462
+ hatch: /* @__PURE__ */ new Set(["run"])
463
+ // `tox` is deliberately absent: `tox -e py312`'s `-e py312` is an environment
464
+ // selector, not a launcher token, so argv[2] (`py312`) is never a real binary.
465
+ // ToxFilter matches on the unstripped `tox` stem directly (see misc.ts).
466
+ };
467
+ function pathName(p) {
468
+ const norm = p.replace(/\\/g, "/");
469
+ const idx = norm.lastIndexOf("/");
470
+ return idx >= 0 ? norm.slice(idx + 1) : norm;
471
+ }
472
+ function pathStem(p) {
473
+ const name = pathName(p);
474
+ const dot = name.lastIndexOf(".");
475
+ return dot > 0 ? name.slice(0, dot) : name;
476
+ }
477
+ function positionalArgs(args, valueFlags) {
478
+ const out = [];
479
+ for (let i = 0; i < args.length; i++) {
480
+ const a = args[i];
481
+ if (a.startsWith("-")) {
482
+ if (valueFlags?.has(a) === true && i + 1 < args.length) i++;
483
+ continue;
484
+ }
485
+ out.push(a);
486
+ }
487
+ return out;
488
+ }
489
+ var SHORT_FLAGS_WITH_VALUE = /* @__PURE__ */ new Set(["-n", "-c", "-u", "-e"]);
490
+ function stripPrefixes(argv) {
491
+ if (argv.length === 0) return [];
492
+ let out = [...argv];
493
+ for (; ; ) {
494
+ const before = out.length;
495
+ while (out.length && out[0].includes("=") && !out[0].startsWith("-")) {
496
+ const head = out[0].split("=", 1)[0];
497
+ if (head && (/[A-Za-z]/.test(head[0]) || head[0] === "_") && /^[A-Za-z0-9_]+$/.test(head)) out.shift();
498
+ else break;
499
+ }
500
+ while (out.length) {
501
+ if (!PASSTHROUGH_PREFIXES.has(pathStem(out[0]).toLowerCase())) break;
502
+ out.shift();
503
+ while (out.length && out[0].startsWith("-")) {
504
+ const flag = out.shift();
505
+ if (SHORT_FLAGS_WITH_VALUE.has(flag) && out.length) out.shift();
506
+ }
507
+ }
508
+ if (out.length === before) break;
509
+ }
510
+ if (out.length === 0) return out;
511
+ if (pathStem(out[0]).toLowerCase() === "uv" && out[1] === "tool" && out[2] === "run" && out.length > 3) {
512
+ return skipLauncherOptions(out.slice(3));
513
+ }
514
+ const stem = pathStem(out[0]).toLowerCase();
515
+ const triggers = TWO_TOKEN_PREFIXES[stem];
516
+ if (triggers !== void 0 && out.length >= 2) {
517
+ const nextTok = out[1];
518
+ if (triggers.size === 0 || triggers.has(nextTok)) {
519
+ const consume = triggers.size === 0 ? 1 : 2;
520
+ if (out.length > consume) out = skipLauncherOptions(out.slice(consume));
521
+ }
522
+ }
523
+ return out;
524
+ }
525
+ var LAUNCHER_VALUE_FLAGS = /* @__PURE__ */ new Set([
526
+ "-p",
527
+ "--package",
528
+ "-c",
529
+ "--call",
530
+ "--userconfig",
531
+ "--cache",
532
+ "--shell",
533
+ "--node-arg",
534
+ "--npm",
535
+ "--with",
536
+ "--with-requirements",
537
+ "--with-editable",
538
+ "--python",
539
+ "--directory",
540
+ "--project",
541
+ "--extra",
542
+ "--group",
543
+ "--index-url",
544
+ "--index",
545
+ "--find-links",
546
+ "--constraint",
547
+ "--override",
548
+ "--filter",
549
+ "--dir",
550
+ "--workspace-root",
551
+ "--shell-mode"
552
+ ]);
553
+ function skipLauncherOptions(args) {
554
+ let i = 0;
555
+ while (i < args.length && args[i].startsWith("-") && args[i] !== "--") {
556
+ const flag = args[i];
557
+ i++;
558
+ if (LAUNCHER_VALUE_FLAGS.has(flag) && i < args.length) i++;
559
+ }
560
+ if (i < args.length && args[i] === "--") i++;
561
+ return i < args.length ? args.slice(i) : args;
562
+ }
563
+ var PACKAGE_MANAGER_SCRIPT_STEMS = /* @__PURE__ */ new Set(["npm", "pnpm", "yarn", "bun"]);
564
+ var MAX_SCRIPT_RESOLUTION_DEPTH = 5;
565
+ var COMPOUND_SCRIPT_RE = /&&|\|\||[|;]|`|\$\(/;
566
+ var packageJsonScriptsCache = /* @__PURE__ */ new Map();
567
+ function loadPackageScripts(candidate, stat) {
568
+ const cached = packageJsonScriptsCache.get(candidate);
569
+ if (cached && cached.mtimeMs === stat.mtimeMs) return cached.scripts;
570
+ let scripts = {};
571
+ try {
572
+ const parsed = JSON.parse(fs.readFileSync(candidate, "utf8"));
573
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
574
+ const raw = parsed["scripts"];
575
+ if (raw && typeof raw === "object" && !Array.isArray(raw)) scripts = raw;
576
+ }
577
+ } catch {
578
+ }
579
+ packageJsonScriptsCache.set(candidate, { mtimeMs: stat.mtimeMs, scripts });
580
+ return scripts;
581
+ }
582
+ function findNearestPackageScripts(startDir) {
583
+ let dir = startDir;
584
+ for (; ; ) {
585
+ const candidate = path.join(dir, "package.json");
586
+ try {
587
+ const stat = fs.statSync(candidate);
588
+ if (stat.isFile()) return loadPackageScripts(candidate, stat);
589
+ } catch {
590
+ }
591
+ const parent = path.dirname(dir);
592
+ if (parent === dir) return null;
593
+ dir = parent;
594
+ }
595
+ }
596
+ var YARN_BUILTIN_SUBCOMMANDS = /* @__PURE__ */ new Set([
597
+ "access",
598
+ "add",
599
+ "audit",
600
+ "autoclean",
601
+ "bin",
602
+ "cache",
603
+ "check",
604
+ "config",
605
+ "create",
606
+ "dedupe",
607
+ "dlx",
608
+ "exec",
609
+ "explain",
610
+ "generate-lock-entry",
611
+ "global",
612
+ "help",
613
+ "import",
614
+ "info",
615
+ "init",
616
+ "install",
617
+ "licenses",
618
+ "link",
619
+ "list",
620
+ "login",
621
+ "logout",
622
+ "node",
623
+ "npm",
624
+ "outdated",
625
+ "owner",
626
+ "pack",
627
+ "patch",
628
+ "plugin",
629
+ "policies",
630
+ "rebuild",
631
+ "remove",
632
+ "set",
633
+ "stage",
634
+ "tag",
635
+ "team",
636
+ "unlink",
637
+ "unplug",
638
+ "up",
639
+ "upgrade",
640
+ "upgrade-interactive",
641
+ "version",
642
+ "versions",
643
+ "why",
644
+ "workspace",
645
+ "workspaces"
646
+ ]);
647
+ function scriptNameFromArgs(stem, args) {
648
+ const [a0, a1] = args;
649
+ if (a0 === void 0) return null;
650
+ switch (stem) {
651
+ case "npm":
652
+ case "pnpm":
653
+ if (a0 === "test" || a0 === "t") return "test";
654
+ if ((a0 === "run" || a0 === "run-script") && a1 !== void 0) return a1;
655
+ return null;
656
+ case "bun":
657
+ if (a0 === "run" && a1 !== void 0) return a1;
658
+ return null;
659
+ case "yarn":
660
+ if (a0 === "run" && a1 !== void 0) return a1;
661
+ if (!a0.startsWith("-") && !YARN_BUILTIN_SUBCOMMANDS.has(a0)) return a0;
662
+ return null;
663
+ default:
664
+ return null;
665
+ }
666
+ }
667
+ function resolvePackageManagerScript(argv, cwd) {
668
+ try {
669
+ let current = argv;
670
+ let resolvedAny = false;
671
+ for (let depth = 0; depth < MAX_SCRIPT_RESOLUTION_DEPTH; depth++) {
672
+ if (current.length === 0) break;
673
+ const stem = pathStem(current[0]).toLowerCase();
674
+ if (!PACKAGE_MANAGER_SCRIPT_STEMS.has(stem)) break;
675
+ const scriptName = scriptNameFromArgs(stem, current.slice(1));
676
+ if (scriptName === null) break;
677
+ const scripts = findNearestPackageScripts(cwd);
678
+ if (scripts === null) break;
679
+ const scriptCmd = scripts[scriptName];
680
+ if (typeof scriptCmd !== "string" || !scriptCmd.trim()) break;
681
+ if (COMPOUND_SCRIPT_RE.test(scriptCmd)) break;
682
+ let nextArgv;
683
+ try {
684
+ nextArgv = shlexSplit(scriptCmd);
685
+ } catch {
686
+ break;
687
+ }
688
+ if (nextArgv.length === 0) break;
689
+ current = nextArgv;
690
+ resolvedAny = true;
691
+ }
692
+ return resolvedAny ? current : null;
693
+ } catch {
694
+ return null;
695
+ }
696
+ }
697
+ function shlexSplit(cmd) {
698
+ const tokens = [];
699
+ let cur = "";
700
+ let has = false;
701
+ let i = 0;
702
+ const n = cmd.length;
703
+ while (i < n) {
704
+ const ch = cmd[i];
705
+ if (ch === " " || ch === " " || ch === "\n" || ch === "\r") {
706
+ if (has) {
707
+ tokens.push(cur);
708
+ cur = "";
709
+ has = false;
710
+ }
711
+ i += 1;
712
+ continue;
713
+ }
714
+ has = true;
715
+ if (ch === "\\") {
716
+ if (i + 1 < n) {
717
+ cur += cmd[i + 1];
718
+ i += 2;
719
+ } else {
720
+ i += 1;
721
+ }
722
+ continue;
723
+ }
724
+ if (ch === "'") {
725
+ i += 1;
726
+ const end = cmd.indexOf("'", i);
727
+ if (end === -1) throw new Error("No closing quotation");
728
+ cur += cmd.slice(i, end);
729
+ i = end + 1;
730
+ continue;
731
+ }
732
+ if (ch === '"') {
733
+ i += 1;
734
+ let buf = "";
735
+ while (i < n && cmd[i] !== '"') {
736
+ if (cmd[i] === "\\" && i + 1 < n && (cmd[i + 1] === '"' || cmd[i + 1] === "\\")) {
737
+ buf += cmd[i + 1];
738
+ i += 2;
739
+ } else {
740
+ buf += cmd[i];
741
+ i += 1;
742
+ }
743
+ }
744
+ if (i >= n) throw new Error("No closing quotation");
745
+ cur += buf;
746
+ i += 1;
747
+ continue;
748
+ }
749
+ cur += ch;
750
+ i += 1;
751
+ }
752
+ if (has) tokens.push(cur);
753
+ return tokens;
754
+ }
755
+ function capLongLines(lines, maxChars = FALLBACK_MAX_LINE_CHARS) {
756
+ return lines.map((line) => {
757
+ if (line.length <= maxChars) return line;
758
+ if (ELIDED_MARKER_RE.test(line)) return line;
759
+ const priorClip = CLIPPED_MARKER_RE.exec(line);
760
+ let cut = maxChars;
761
+ const high = line.charCodeAt(cut - 1);
762
+ const low = line.charCodeAt(cut);
763
+ if (high >= 55296 && high <= 56319 && low >= 56320 && low <= 57343) cut -= 1;
764
+ const carried = priorClip !== null && priorClip.index >= cut ? Number(priorClip[1]) - priorClip[0].length : 0;
765
+ return `${line.slice(0, cut)} \u2026 [${line.length - cut + carried} chars elided]`;
766
+ });
767
+ }
768
+ var GREP_MAX_LINE_CHARS = 1e3;
769
+ var _GREP_PREFIX_BUDGET = 160;
770
+ var _GREP_LINE_PREFIX_RE = /^(?:(?:[A-Za-z]:)?[^:\n]{0,200}?[:-]\d+[:-]|\d+[:-])/;
771
+ var _GREP_BAIL_SHORT = /* @__PURE__ */ new Set(["e", "f", "i", "v", "y", "z", "S", "U"]);
772
+ var _GREP_BAIL_LONG = /* @__PURE__ */ new Set([
773
+ "--regexp",
774
+ "--file",
775
+ "--ignore-case",
776
+ "--invert-match",
777
+ "--null-data",
778
+ "--smart-case",
779
+ "--multiline"
780
+ ]);
781
+ var _GREP_VALUE_SHORT = /* @__PURE__ */ new Set(["A", "B", "C", "m", "d", "D"]);
782
+ var _GREP_RG_VALUE_SHORT = /* @__PURE__ */ new Set(["g", "t", "T", "M", "j", "r"]);
783
+ var _GREP_VALUE_LONG = /* @__PURE__ */ new Set([
784
+ "--max-count",
785
+ "--context",
786
+ "--after-context",
787
+ "--before-context",
788
+ "--include",
789
+ "--exclude",
790
+ "--exclude-dir",
791
+ "--glob",
792
+ "--iglob",
793
+ "--type",
794
+ "--type-not",
795
+ "--binary-files",
796
+ "--devices",
797
+ "--directories",
798
+ "--color",
799
+ "--colour",
800
+ "--replace",
801
+ "--max-columns",
802
+ "--threads",
803
+ "--sort",
804
+ "--sortr",
805
+ "--pre",
806
+ "--engine"
807
+ ]);
808
+ var _GREP_REGEX_META_RE = /[\\^$.|?*+()[\]{}]/;
809
+ function grepLiteralPattern(argv) {
810
+ if (!argv.length) return null;
811
+ const stem = pathStem(argv[0] ?? "").toLowerCase();
812
+ let rest = argv.slice(1);
813
+ let isRg = stem === "rg";
814
+ if (stem === "git") {
815
+ const gi = rest.indexOf("grep");
816
+ if (gi < 0) return null;
817
+ rest = rest.slice(gi + 1);
818
+ isRg = false;
819
+ }
820
+ let fixed = false;
821
+ let endOfFlags = false;
822
+ let pattern = null;
823
+ for (let i = 0; i < rest.length; i++) {
824
+ const tok = rest[i];
825
+ if (!endOfFlags && tok === "--") {
826
+ endOfFlags = true;
827
+ continue;
828
+ }
829
+ if (!endOfFlags && tok.startsWith("--")) {
830
+ const eq = tok.indexOf("=");
831
+ const name = eq >= 0 ? tok.slice(0, eq) : tok;
832
+ if (_GREP_BAIL_LONG.has(name)) return null;
833
+ if (name === "--fixed-strings") fixed = true;
834
+ else if (eq < 0 && _GREP_VALUE_LONG.has(name)) i++;
835
+ continue;
836
+ }
837
+ if (!endOfFlags && tok.length > 1 && tok.startsWith("-")) {
838
+ const letters = tok.slice(1);
839
+ for (let k = 0; k < letters.length; k++) {
840
+ const ch = letters[k];
841
+ if (_GREP_BAIL_SHORT.has(ch)) return null;
842
+ if (ch === "F") fixed = true;
843
+ if (_GREP_VALUE_SHORT.has(ch) || isRg && _GREP_RG_VALUE_SHORT.has(ch)) {
844
+ if (k === letters.length - 1) i++;
845
+ break;
846
+ }
847
+ }
848
+ continue;
849
+ }
850
+ pattern = tok;
851
+ break;
852
+ }
853
+ if (pattern === null || pattern.length < 3 || pattern.includes("\n")) return null;
854
+ if (!fixed && _GREP_REGEX_META_RE.test(pattern)) return null;
855
+ return pattern;
856
+ }
857
+ function _safeCut(line, idx) {
858
+ if (idx <= 0 || idx >= line.length) return idx;
859
+ const high = line.charCodeAt(idx - 1);
860
+ const low = line.charCodeAt(idx);
861
+ return high >= 55296 && high <= 56319 && low >= 56320 && low <= 57343 ? idx - 1 : idx;
862
+ }
863
+ function clipLongMatchLine(line, pattern, maxChars = GREP_MAX_LINE_CHARS) {
864
+ if (line.length <= maxChars) return line;
865
+ const pm = _GREP_LINE_PREFIX_RE.exec(line);
866
+ const prefixEnd = pm && pm[0].length <= _GREP_PREFIX_BUDGET ? pm[0].length : 0;
867
+ let idx = line.indexOf(pattern, prefixEnd);
868
+ if (idx < 0) idx = line.indexOf(pattern);
869
+ if (idx < 0) return line;
870
+ const matchEnd = idx + pattern.length;
871
+ let clipped;
872
+ if (matchEnd <= maxChars) {
873
+ const cut = _safeCut(line, maxChars);
874
+ clipped = `${line.slice(0, cut)} \u2026 [${line.length - cut} chars elided]`;
875
+ } else {
876
+ const budget = maxChars - prefixEnd;
877
+ if (budget < pattern.length + 40) return line;
878
+ let start = Math.max(prefixEnd, idx - Math.floor((budget - pattern.length) / 2));
879
+ let stop = start + budget;
880
+ if (stop > line.length) {
881
+ stop = line.length;
882
+ start = Math.max(prefixEnd, stop - budget);
883
+ }
884
+ start = _safeCut(line, start);
885
+ stop = _safeCut(line, stop);
886
+ if (start > idx || stop < matchEnd) return line;
887
+ const before = start - prefixEnd;
888
+ const after = line.length - stop;
889
+ clipped = line.slice(0, prefixEnd) + (before > 0 ? `\u2026 [${before} chars elided] \u2026` : "") + line.slice(start, stop) + (after > 0 ? ` \u2026 [${after} chars elided]` : "");
890
+ }
891
+ return clipped.length < line.length ? clipped : line;
892
+ }
893
+ function clipGrepLines(text, argv, maxChars = GREP_MAX_LINE_CHARS) {
894
+ if (text.length <= maxChars) return text;
895
+ const pattern = grepLiteralPattern(argv);
896
+ if (pattern === null) return text;
897
+ let changed = false;
898
+ const out = text.split("\n").map((line) => {
899
+ const clipped = clipLongMatchLine(line, pattern, maxChars);
900
+ if (clipped !== line) changed = true;
901
+ return clipped;
902
+ });
903
+ return changed ? out.join("\n") : text;
904
+ }
905
+ function fallbackTruncate(stdout, stderr, maxLines) {
906
+ const half = Math.floor(maxLines / 2);
907
+ const outLines = truncateMiddle(capLongLines(dedupeConsecutive(stdout.split("\n"))), half);
908
+ const errLines = truncateMiddle(capLongLines(dedupeConsecutive(stderr.split("\n"))), half);
909
+ if (stderr) return `${outLines.join("\n")}
910
+ ---
911
+ ${errLines.join("\n")}`;
912
+ return outLines.join("\n");
913
+ }
914
+ function compressBashOutput(stdout, stderr) {
915
+ let body = dedupeConsecutive(stdout.split("\n"), { entropyBypass: true }).join("\n");
916
+ if (stderr.trim()) {
917
+ const err = dedupeConsecutive(stderr.split("\n"), { entropyBypass: true }).join("\n");
918
+ body = `${body.replace(/\s+$/, "")}
919
+ ---
920
+ ${err.replace(/\s+$/, "")}`;
921
+ }
922
+ return body;
923
+ }
924
+ function dedupeCombinedOutput(text) {
925
+ return squeezeBlankLines(dedupeConsecutive(text.split("\n")).join("\n"));
926
+ }
927
+ function compressTestOutput(lines, maxLines = 300) {
928
+ const deduped = dedupeNumericRuns(lines, { minRun: 5 });
929
+ return truncateMiddle(deduped, maxLines).join("\n");
930
+ }
931
+ function byteLength(s) {
932
+ return utf8Len(s);
933
+ }
934
+
935
+ // src/bash_output_cache.ts
936
+ init_define_import_meta_env();
937
+ import { readdirSync, readFileSync as readFileSync2, statSync as statSync2 } from "fs";
938
+ import { resolve } from "path";
939
+
940
+ // src/hints/lang_patterns.ts
941
+ init_define_import_meta_env();
942
+ var LOCK_FILE_NAMES = /* @__PURE__ */ new Set([
943
+ "package-lock.json",
944
+ "yarn.lock",
945
+ "pnpm-lock.yaml",
946
+ "cargo.lock",
947
+ "poetry.lock",
948
+ "pipfile.lock",
949
+ "uv.lock",
950
+ "gemfile.lock",
951
+ "go.sum",
952
+ "composer.lock",
953
+ "mix.lock",
954
+ "pubspec.lock",
955
+ "package.resolved"
956
+ ]);
957
+ var MANIFEST_FILE_NAMES = /* @__PURE__ */ new Set([
958
+ "package.json",
959
+ "pyproject.toml",
960
+ "cargo.toml",
961
+ "go.mod",
962
+ "go.work",
963
+ "pom.xml",
964
+ "build.gradle",
965
+ "build.gradle.kts",
966
+ "settings.gradle",
967
+ "composer.json",
968
+ "gemfile",
969
+ "mix.exs",
970
+ "pubspec.yaml",
971
+ "cmakelists.txt",
972
+ "makefile",
973
+ "project.clj",
974
+ // TypeScript / JavaScript project configs
975
+ "tsconfig.json",
976
+ "jsconfig.json",
977
+ // Bundler / framework configs
978
+ "vite.config.ts",
979
+ "vite.config.js",
980
+ "webpack.config.js",
981
+ "webpack.config.ts",
982
+ "rollup.config.js",
983
+ "rollup.config.ts",
984
+ "esbuild.config.js",
985
+ "next.config.js",
986
+ "next.config.ts",
987
+ "nuxt.config.ts"
988
+ ]);
989
+ var MANIFEST_EXTENSIONS = /* @__PURE__ */ new Set([
990
+ ".cabal"
991
+ ]);
992
+ var MANIFEST_BASENAME_PATTERNS = [
993
+ /^tsconfig(\..+)?\.json$/i
994
+ ];
995
+ var BUILD_DIR_NAMES = /* @__PURE__ */ new Set([
996
+ "dist",
997
+ "target",
998
+ "build",
999
+ "out",
1000
+ "__pycache__",
1001
+ ".next",
1002
+ ".nuxt",
1003
+ ".output",
1004
+ ".gradle",
1005
+ "_build",
1006
+ ".build",
1007
+ "pkg",
1008
+ "obj"
1009
+ ]);
1010
+ var ALWAYS_GENERATED_EXTS = /* @__PURE__ */ new Set([
1011
+ ".pyc",
1012
+ ".pyo",
1013
+ ".pyd",
1014
+ ".class",
1015
+ ".o",
1016
+ ".a",
1017
+ ".so",
1018
+ ".dylib",
1019
+ ".dll",
1020
+ ".tsbuildinfo"
1021
+ ]);
1022
+ var CONDITIONALLY_GENERATED_EXTS = /* @__PURE__ */ new Set([
1023
+ ".map",
1024
+ ".d.ts"
1025
+ ]);
1026
+ var BUILD_COMMAND_PATTERNS = [
1027
+ // Rust / Cargo
1028
+ /^\s*cargo\s+(build|test|run|check|clippy)\b/i,
1029
+ // Go
1030
+ /^\s*go\s+(build|test|run|vet)\b/i,
1031
+ // Maven
1032
+ /^\s*mvn\b/i,
1033
+ // Gradle (direct or wrapper)
1034
+ /^\s*(?:gradle|\.\/gradlew|gradlew)\b/i,
1035
+ // Python / pip
1036
+ /^\s*pip\s+(install|freeze)\b/i,
1037
+ // npm audit / npm outdated — read-only reporting commands with no side effects, safe to cache/recall. Each resolves a `lockfile`-scoped fingerprint via bash_output_cache.ts's isNpmAuditCommand/isNpmOutdatedCommand, but that fingerprinting never actually ran outside their own unit tests until this entry, since neither subcommand was recognized by isBuildCommand (the generic cache-reachability gate in hooks_bash.ts). `npm install`/`npm ci`/`npm list`/`npm ls` are deliberately left out — see the negative-case test in lang_patterns.test.ts.
1038
+ /^\s*npm\s+(audit|outdated)\b/i,
1039
+ // Poetry
1040
+ /^\s*poetry\s+(install|update)\b/i,
1041
+ // uv
1042
+ /^\s*uv\s+(sync|pip\s+install)\b/i,
1043
+ // Bundler (Ruby)
1044
+ /^\s*bundle\s+(install|update)\b/i,
1045
+ // Mix (Elixir)
1046
+ /^\s*mix\s+(deps\.get|compile|test)\b/i,
1047
+ // dotnet
1048
+ /^\s*dotnet\s+(build|test|restore)\b/i,
1049
+ // Make
1050
+ /^\s*make\b/i,
1051
+ // CMake build
1052
+ /^\s*cmake\s+--build\b/i,
1053
+ // Rake (Ruby)
1054
+ /^\s*rake\b/i,
1055
+ // TypeScript compiler (direct and via npx)
1056
+ /^\s*tsc(?:\s|$)/i,
1057
+ /^\s*npx\s+tsc(?:\s|$)/i,
1058
+ // Vite
1059
+ /^\s*vite\s+(build|dev|preview)\b/i,
1060
+ // Next.js
1061
+ /^\s*next\s+(build|dev|start)\b/i,
1062
+ // Nuxt
1063
+ /^\s*nuxt\s+(build|dev)\b/i,
1064
+ // Webpack
1065
+ /^\s*webpack\b/i,
1066
+ // esbuild
1067
+ /^\s*esbuild\b/i,
1068
+ // Rollup
1069
+ /^\s*rollup\b/i,
1070
+ // Turbo
1071
+ /^\s*turbo\s+(build|dev)\b/i
1072
+ ];
1073
+ var LOCK_FILE_COUNT = LOCK_FILE_NAMES.size;
1074
+ var MANIFEST_FILE_COUNT = MANIFEST_FILE_NAMES.size + MANIFEST_EXTENSIONS.size + MANIFEST_BASENAME_PATTERNS.length;
1075
+ var BUILD_DIR_COUNT = BUILD_DIR_NAMES.size;
1076
+ var GENERATED_EXT_COUNT = ALWAYS_GENERATED_EXTS.size + CONDITIONALLY_GENERATED_EXTS.size;
1077
+ function isLockFile(basename) {
1078
+ return LOCK_FILE_NAMES.has(basename.toLowerCase());
1079
+ }
1080
+ function isManifestFile(basename) {
1081
+ const lower = basename.toLowerCase();
1082
+ if (MANIFEST_FILE_NAMES.has(lower)) return true;
1083
+ const dot = lower.lastIndexOf(".");
1084
+ if (dot !== -1 && MANIFEST_EXTENSIONS.has(lower.slice(dot))) return true;
1085
+ if (MANIFEST_BASENAME_PATTERNS.some((re) => re.test(basename))) return true;
1086
+ return false;
1087
+ }
1088
+ function pathSegments(filePath) {
1089
+ return filePath.split(/[/\\]/).filter((s) => s.length > 0);
1090
+ }
1091
+ function isInBuildDir(filePath) {
1092
+ const segments = pathSegments(filePath);
1093
+ for (let i = 0; i < segments.length - 1; i++) {
1094
+ const seg = segments[i];
1095
+ if (seg !== void 0 && BUILD_DIR_NAMES.has(seg.toLowerCase())) return true;
1096
+ }
1097
+ return false;
1098
+ }
1099
+ function isGeneratedFile(filePath) {
1100
+ const lower = filePath.toLowerCase();
1101
+ for (const ext of ALWAYS_GENERATED_EXTS) {
1102
+ if (lower.endsWith(ext)) return true;
1103
+ }
1104
+ for (const ext of CONDITIONALLY_GENERATED_EXTS) {
1105
+ if (lower.endsWith(ext) && isInBuildDir(filePath)) return true;
1106
+ }
1107
+ return false;
1108
+ }
1109
+ function isBuildCommand(cmd) {
1110
+ return BUILD_COMMAND_PATTERNS.some((re) => re.test(cmd));
1111
+ }
1112
+ var TEST_RUNNER_COMMAND_PATTERNS = [
1113
+ // pytest (Python)
1114
+ /^pytest(?:\s|$)/i,
1115
+ // Jest / Vitest (direct or via npx)
1116
+ /^(?:npx\s+)?jest(?:\s|$)/i,
1117
+ /^(?:npx\s+)?vitest(?:\s+run|\s+watch)?(?:\s|$)/i,
1118
+ // Go
1119
+ /^go test(?:\s|$)/i,
1120
+ // Rust / Cargo
1121
+ /^cargo test(?:\s|$)/i,
1122
+ // npm test / npm run test
1123
+ /^npm (?:test|run test)(?:\s|$)/i,
1124
+ // yarn test / yarn run test / pnpm test / pnpm run test
1125
+ /^(?:yarn|pnpm)(?:\s+run)? test(?:\s|$)/i
1126
+ ];
1127
+ function isTestRunnerCommand(cmd) {
1128
+ return TEST_RUNNER_COMMAND_PATTERNS.some((re) => re.test(cmd));
1129
+ }
1130
+ var MONITORING_COMMAND_PATTERNS = [
1131
+ // GitHub CI
1132
+ { pattern: /^gh run (?:watch|view|list)/, recallHint: '--grep "fail|error|pass|\u2713|\u2717|conclusion"' },
1133
+ { pattern: /^gh run view.*--log/, recallHint: '--tail 100 --grep "Error|FAIL|error"' },
1134
+ { pattern: /^gh pr checks/, recallHint: '--grep "fail|error|pass|pending"' },
1135
+ { pattern: /^gh pr view/, recallHint: '--grep "state|title|mergeable|review|OPEN|CLOSED|MERGED"' },
1136
+ { pattern: /^gh pr list/, recallHint: '--grep "OPEN|DRAFT|MERGED|#"' },
1137
+ { pattern: /^gh issue view/, recallHint: '--grep "state|title|label|OPEN|CLOSED"' },
1138
+ { pattern: /^gh workflow (?:run|list|view)/, recallHint: '--grep "completed|failed|in_progress"' },
1139
+ // Dev servers (Next, Vite, Nuxt, Remix, Astro)
1140
+ { pattern: /^(?:npx\s+)?next dev/, recallHint: '--tail 30 --grep "error|warn|ready|compiled"' },
1141
+ { pattern: /^(?:npx\s+)?next build/, recallHint: '--grep "error|warn|Failed|\u2713"' },
1142
+ { pattern: /^(?:npx\s+)?vite\b(?:\s+dev|\s+build|\s+preview)?/, recallHint: '--tail 20 --grep "error|warn|ready"' },
1143
+ { pattern: /^(?:npx\s+)?nuxt dev/, recallHint: '--tail 30 --grep "error|warn|ready"' },
1144
+ { pattern: /^(?:npx\s+)?remix dev/, recallHint: '--tail 20 --grep "error|warn|ready"' },
1145
+ { pattern: /^(?:npx\s+)?astro dev/, recallHint: '--tail 20 --grep "error|warn|ready"' },
1146
+ // Test watchers
1147
+ { pattern: /^(?:npx\s+)?vitest(?:\s+run|\s+watch)?/, recallHint: '--grep "FAIL|PASS|Error|\u2713|\u2717"' },
1148
+ { pattern: /^(?:npx\s+)?jest(?:\s+--watch)?/, recallHint: '--grep "FAIL|PASS|Error|Tests:"' },
1149
+ { pattern: /^pytest(?:\s|$)/, recallHint: '--grep "FAILED|PASSED|ERROR|passed|failed"' },
1150
+ { pattern: /^(?:cargo\s+test|cargo\s+watch)/, recallHint: '--grep "FAILED|ok|error\\["' },
1151
+ { pattern: /^go test/, recallHint: '--grep "FAIL|ok|---"' },
1152
+ // Docker / compose
1153
+ { pattern: /^docker(?:\s+compose)?\s+logs/, recallHint: '--tail 50 --grep "error|warn|Error|WARN"' },
1154
+ { pattern: /^docker-compose\s+logs/, recallHint: '--tail 50 --grep "error|warn|Error|WARN"' },
1155
+ // File watchers / hot-reload
1156
+ { pattern: /^nodemon/, recallHint: '--tail 20 --grep "error|crash|restart"' },
1157
+ { pattern: /^air(?:\s|$)/, recallHint: '--tail 20 --grep "error|build failed"' },
1158
+ { pattern: /^cargo watch/, recallHint: '--tail 20 --grep "error\\[|warning\\["' },
1159
+ { pattern: /^watchexec/, recallHint: '--tail 20 --grep "error|warn"' },
1160
+ // Linters / formatters run repeatedly
1161
+ { pattern: /^(?:npx\s+)?eslint(?:\s|$)/, recallHint: '--grep "error|warning|\u2716|problems"' },
1162
+ { pattern: /^(?:npx\s+)?prettier(?:\s|$)/, recallHint: '--grep "unchanged|reformatted|error"' },
1163
+ { pattern: /^npx\s+tsc(?:\s|$)/, recallHint: '--grep "error TS|Cannot find|Type "' },
1164
+ { pattern: /^ruff(?:\s|$)/, recallHint: '--grep "error|warning|Found"' },
1165
+ { pattern: /^(?:cargo\s+)?clippy/, recallHint: '--grep "error\\[|warning\\["' },
1166
+ // git diff (full diff output — can be very large; excludes --stat which is small)
1167
+ { pattern: /^git diff(?!\s+--stat)(?:\s+HEAD)?(?:\s|$)/, recallHint: '--grep "@@|\\+\\+\\+|---|diff --git"' },
1168
+ { pattern: /^git diff\s+--cached(?!\s+--stat)/, recallHint: '--grep "@@|\\+\\+\\+|---|diff --git"' },
1169
+ // git push — output is dominated by the pre-push hook (full test suite, often 10K+); cache it so failures/rejections are recallable without re-pushing
1170
+ { pattern: /^git push(?:\s|$)/, recallHint: '--tail 100 --grep "error|fail|FAIL|rejected|To "' },
1171
+ // npm run * wrappers (npm test is excluded — too generic; npm run test is explicit)
1172
+ { pattern: /^npm run (?:test|spec)(?:\s|$)/, recallHint: '--grep "FAIL|PASS|Error|Tests:|\u2713|\u2717"' },
1173
+ { pattern: /^npm run build(?:\s|$)/, recallHint: '--grep "error|Built|Failed|\u2713|\u2717"' },
1174
+ { pattern: /^npm run (?:lint|typecheck|check|type-check)(?:\s|$)/, recallHint: '--grep "error|warning|\u2716|problems"' },
1175
+ // node scripts (migration runners, seed generators, etc. run repeatedly)
1176
+ { pattern: /^node\s+(?:scripts|src\/scripts)\/\S+\.m?js\b/, recallHint: '--tail 50 --grep "error|Error|done|complete|inserted|migrated"' },
1177
+ // External AI peer-review CLI tools (produce large outputs, run repeatedly per session)
1178
+ { pattern: /^codex(?:\s|$)/, recallHint: '--tail 100 --grep "error|suggestion|verdict|conclusion"' },
1179
+ { pattern: /^(?:~\/\.claude\/bin\/|\.claude\/bin\/)?glm\.sh(?:\s|$)/, recallHint: '--tail 100 --grep "error|verdict|conclusion|suggestion"' },
1180
+ // cat of a single source file — output is the full file; pre-bash emits a token-goat read suggestion
1181
+ { pattern: /^cat\s+\S+\.(java|py|ts|tsx|js|jsx|go|rb|rs|cpp|cc|cxx|c|h|hpp|kt|swift|cs|php|scala|clj)\s*$/, recallHint: "--tail 50" },
1182
+ // PowerShell read-only system-state queries (stable over 60-120s)
1183
+ {
1184
+ pattern: /^(?:powershell(?:\.exe)?|pwsh(?:\.exe)?)\s+(?:-\w+\s+)*-Command\s+["']?Get-(?:CimInstance|Process|Counter|Service|PSDrive|WmiObject)\b/i,
1185
+ recallHint: "--tail 50"
1186
+ },
1187
+ // token-goat section/outline/symbol repeat calls — output is stable until the file changes
1188
+ { pattern: /^(?:token-goat|tg)\s+section\s+["'][^"']+["']/, recallHint: "" },
1189
+ { pattern: /^(?:token-goat|tg)\s+outline\s+\S+/, recallHint: "" },
1190
+ { pattern: /^(?:token-goat|tg)\s+symbol\s+\S+/, recallHint: "" }
1191
+ ];
1192
+ function isPsMultilineSystemQuery(cmd) {
1193
+ if (!/^(?:powershell(?:\.exe)?|pwsh(?:\.exe)?)\s+/i.test(cmd)) return false;
1194
+ const cmdIdx = cmd.search(/-Command\b/i);
1195
+ if (cmdIdx === -1) return false;
1196
+ const afterCmd = cmd.slice(cmdIdx + "-Command".length).trimStart();
1197
+ if (!/^["']/.test(afterCmd)) return false;
1198
+ const body = afterCmd.slice(1);
1199
+ if (!body.includes("\n")) return false;
1200
+ if (!/\bGet-(?:CimInstance|Process|Counter|Service|PSDrive|WmiObject)\b/i.test(body)) return false;
1201
+ if (/\b(?:Remove|Set|New|Restart|Install|Uninstall|Enable|Disable|Grant|Revoke|Register|Unregister)-/i.test(body)) return false;
1202
+ if (/\b(?:Stop-(?:Process|Service|Computer)|Invoke-(?:Expression|Command)|Clear-(?:Content|EventLog|Item))\b/i.test(body)) return false;
1203
+ return true;
1204
+ }
1205
+ function getMonitoringRecallHint(cmd) {
1206
+ const trimmed = cmd.trim();
1207
+ if (hasUnquotedOperator(trimmed, ["&&", "||", ";"])) return null;
1208
+ for (const { pattern, recallHint } of MONITORING_COMMAND_PATTERNS) {
1209
+ if (pattern.test(trimmed)) return recallHint;
1210
+ }
1211
+ if (isPsMultilineSystemQuery(trimmed)) return "--tail 50";
1212
+ return null;
1213
+ }
1214
+
1215
+ // src/recall_index.ts
1216
+ init_define_import_meta_env();
1217
+ import * as fs2 from "node:fs";
1218
+ function subdirForCacheType(cacheType) {
1219
+ return cacheType === "web" ? "web_outputs" : "bash_outputs";
1220
+ }
1221
+ function blobStillExists(cacheType, id) {
1222
+ try {
1223
+ const p = blobPath(subdirForCacheType(cacheType), id);
1224
+ return p !== null && fs2.existsSync(p);
1225
+ } catch {
1226
+ return false;
1227
+ }
1228
+ }
1229
+ var VALID_TYPES = ["bash", "web", "mcp"];
1230
+ var RECALL_DEFAULT_LIMIT = 10;
1231
+ function isRecallCacheType(value) {
1232
+ return VALID_TYPES.includes(value);
1233
+ }
1234
+ function indexRecallEntry(cacheType, id, label, content, storedAt) {
1235
+ try {
1236
+ const db = getDb(globalDbPath());
1237
+ db.prepare(
1238
+ `INSERT INTO cache_recall (cache_type, entry_id, label, content, stored_at)
1239
+ VALUES (@cacheType, @id, @label, @content, @storedAt)
1240
+ ON CONFLICT(cache_type, entry_id) DO UPDATE SET
1241
+ label = excluded.label,
1242
+ content = excluded.content,
1243
+ stored_at = excluded.stored_at`
1244
+ ).run({ cacheType, id, label, content, storedAt });
1245
+ } catch {
1246
+ }
1247
+ pruneCacheRecallRows();
1248
+ }
1249
+ function pruneCacheRecallRows(maxAgeMs = DEFAULT_MAX_AGE_MS) {
1250
+ try {
1251
+ const db = getDb(globalDbPath());
1252
+ const cutoff = Date.now() - maxAgeMs;
1253
+ db.prepare(`DELETE FROM cache_recall WHERE stored_at < ?`).run(cutoff);
1254
+ } catch {
1255
+ }
1256
+ }
1257
+ var _ftsAvailable = null;
1258
+ function hasFtsTable() {
1259
+ if (_ftsAvailable !== null) return _ftsAvailable;
1260
+ try {
1261
+ const db = getDb(globalDbPath());
1262
+ const row = db.prepare(`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'cache_recall_fts'`).get();
1263
+ _ftsAvailable = row !== void 0;
1264
+ } catch {
1265
+ _ftsAvailable = false;
1266
+ }
1267
+ return _ftsAvailable;
1268
+ }
1269
+ function buildSnippet(content, query, maxLen = 160) {
1270
+ const flat = content.replace(/\s+/g, " ").trim();
1271
+ const tokens = query.split(/\s+/).filter(Boolean);
1272
+ let idx = -1;
1273
+ const lowerFlat = flat.toLowerCase();
1274
+ for (const token of tokens) {
1275
+ const at = lowerFlat.indexOf(token.toLowerCase());
1276
+ if (at !== -1) {
1277
+ idx = at;
1278
+ break;
1279
+ }
1280
+ }
1281
+ if (idx === -1) {
1282
+ return flat.length > maxLen ? flat.slice(0, maxLen) + "..." : flat;
1283
+ }
1284
+ const start = Math.max(0, idx - Math.floor(maxLen / 3));
1285
+ const end = Math.min(flat.length, start + maxLen);
1286
+ const prefix = start > 0 ? "..." : "";
1287
+ const suffix = end < flat.length ? "..." : "";
1288
+ return prefix + flat.slice(start, end) + suffix;
1289
+ }
1290
+ function toFtsMatchExpr(query) {
1291
+ if (query.trim().length === 0) return null;
1292
+ return sanitizeFtsQuery(query);
1293
+ }
1294
+ function mapRowsToHits(rows, snippetQuery) {
1295
+ return rows.filter((r) => isRecallCacheType(r.cacheType)).map((r) => ({
1296
+ id: r.id,
1297
+ cacheType: r.cacheType,
1298
+ label: r.label ?? "",
1299
+ snippet: buildSnippet(r.content ?? "", snippetQuery),
1300
+ storedAt: r.storedAt ?? 0,
1301
+ blobPresent: blobStillExists(r.cacheType, r.id)
1302
+ }));
1303
+ }
1304
+ function ftsSearch(query, type, limit) {
1305
+ const matchExpr = toFtsMatchExpr(query);
1306
+ if (matchExpr === null) return [];
1307
+ const db = getDb(globalDbPath());
1308
+ const rows = type !== void 0 ? db.prepare(
1309
+ `SELECT c.entry_id AS id, c.cache_type AS cacheType, c.label AS label, c.content AS content, c.stored_at AS storedAt
1310
+ FROM cache_recall_fts
1311
+ JOIN cache_recall c ON c.row_id = cache_recall_fts.rowid
1312
+ WHERE cache_recall_fts MATCH ? AND c.cache_type = ?
1313
+ ORDER BY bm25(cache_recall_fts)
1314
+ LIMIT ?`
1315
+ ).all(matchExpr, type, limit) : db.prepare(
1316
+ `SELECT c.entry_id AS id, c.cache_type AS cacheType, c.label AS label, c.content AS content, c.stored_at AS storedAt
1317
+ FROM cache_recall_fts
1318
+ JOIN cache_recall c ON c.row_id = cache_recall_fts.rowid
1319
+ WHERE cache_recall_fts MATCH ?
1320
+ ORDER BY bm25(cache_recall_fts)
1321
+ LIMIT ?`
1322
+ ).all(matchExpr, limit);
1323
+ return mapRowsToHits(rows, query);
1324
+ }
1325
+ function likeSearch(query, type, limit) {
1326
+ const trimmed = query.trim();
1327
+ if (!trimmed) return [];
1328
+ const db = getDb(globalDbPath());
1329
+ const needle = `%${trimmed.replace(/\\/g, "\\\\").replace(/[%_]/g, (c) => `\\${c}`)}%`;
1330
+ const rows = type !== void 0 ? db.prepare(
1331
+ `SELECT entry_id AS id, cache_type AS cacheType, label, content, stored_at AS storedAt
1332
+ FROM cache_recall
1333
+ WHERE cache_type = ? AND (label LIKE ? ESCAPE '\\' OR content LIKE ? ESCAPE '\\')
1334
+ ORDER BY stored_at DESC
1335
+ LIMIT ?`
1336
+ ).all(type, needle, needle, limit) : db.prepare(
1337
+ `SELECT entry_id AS id, cache_type AS cacheType, label, content, stored_at AS storedAt
1338
+ FROM cache_recall
1339
+ WHERE label LIKE ? ESCAPE '\\' OR content LIKE ? ESCAPE '\\'
1340
+ ORDER BY stored_at DESC
1341
+ LIMIT ?`
1342
+ ).all(needle, needle, limit);
1343
+ return mapRowsToHits(rows, trimmed);
1344
+ }
1345
+ function listRecentRecall(opts = {}) {
1346
+ const limit = opts.limit ?? RECALL_DEFAULT_LIMIT;
1347
+ const columns = `SELECT entry_id AS id, cache_type AS cacheType, label, content, stored_at AS storedAt FROM cache_recall`;
1348
+ try {
1349
+ const db = getDb(globalDbPath());
1350
+ 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);
1351
+ return mapRowsToHits(rows, "");
1352
+ } catch {
1353
+ return [];
1354
+ }
1355
+ }
1356
+ function searchRecall(query, opts = {}) {
1357
+ const limit = opts.limit ?? RECALL_DEFAULT_LIMIT;
1358
+ if (query.trim() === "") return [];
1359
+ try {
1360
+ if (hasFtsTable()) {
1361
+ return ftsSearch(query, opts.type, limit);
1362
+ }
1363
+ return likeSearch(query, opts.type, limit);
1364
+ } catch {
1365
+ try {
1366
+ return likeSearch(query, opts.type, limit);
1367
+ } catch {
1368
+ return [];
1369
+ }
1370
+ }
1371
+ }
1372
+
1373
+ // src/bash_output_cache.ts
1374
+ var BASH_OUTPUT_SUBDIR = "bash_outputs";
1375
+ var _byId = /* @__PURE__ */ new Map();
1376
+ var COMMAND_PATTERNS = {
1377
+ gitMutable: /^\s*git\s+(diff|status)\b/i,
1378
+ gitImmutable: /^\s*git\s+show\s+[0-9a-f]{40}\b/i,
1379
+ gitDiffScoped: /\s--\s+\S/,
1380
+ dirListing: /^\s*(?:ls|eza|exa|dir|Get-ChildItem|gci)\b/i,
1381
+ depList: /^\s*(?:npm\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=))*(?:ls|list)\b|pip\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=))*(?:list|freeze)\b|uv\s+pip\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=))*(?:list|freeze)\b|pnpm\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=))*(?:list|ls)\b|yarn\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=))*(?:list)\b|cargo\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=))*tree\b|bundle\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=))*(?:list|show)\b|composer\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=))*show\b)/i,
1382
+ npmInstall: /^\s*npm\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=))*(?:install|ci)\b/i,
1383
+ npmAudit: /^\s*npm\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=))*audit\b(?!.*(?:--fix|fix)\b)/i,
1384
+ npmOutdated: /^\s*npm\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=))*outdated\b/i,
1385
+ 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,
1386
+ npx: /^(?!.*\b(?:install|add|remove|uninstall|i|rm|update|upgrade|set|get|publish|link|ci|audit|shrinkwrap|dedupe|prune|rebuild)\b)\s*npx\s+(?:--?yes\s+)?/i,
1387
+ gitPush: /^\s*git\s+push\b/i,
1388
+ testRunner: /^\s*(?:npx\s+)?(?:pytest|vitest|jest|go\s+test)\b/i,
1389
+ lintCommand: /^\s*(?:(?:npx\s+)?eslint|(?:uv\s+run\s+)?ruff)\b/i,
1390
+ npmRunScript: /^\s*npm\s+run(?:-script)?\b/i,
1391
+ catCommand: /^\s*cat\b/i
1392
+ };
1393
+ var DEP_LOCKFILES = {
1394
+ npm: ["package-lock.json", "yarn.lock"],
1395
+ pip: ["requirements.txt"],
1396
+ uv: ["uv.lock", "requirements.txt"],
1397
+ pnpm: ["pnpm-lock.yaml"],
1398
+ yarn: ["yarn.lock"],
1399
+ cargo: ["Cargo.lock"],
1400
+ bundle: ["Gemfile.lock"],
1401
+ composer: ["composer.lock"]
1402
+ };
1403
+ function isCommandOfType(cmd, type) {
1404
+ const pattern = COMMAND_PATTERNS[type];
1405
+ return pattern?.test(cmd) ?? false;
1406
+ }
1407
+ var isGitMutableCommand = (cmd) => isCommandOfType(cmd, "gitMutable");
1408
+ var isDirListingCommand = (cmd) => isCommandOfType(cmd, "dirListing");
1409
+ var isDepListCommand = (cmd) => isCommandOfType(cmd, "depList");
1410
+ var isNpmInstallCommand = (cmd) => isCommandOfType(cmd, "npmInstall");
1411
+ var isNpmAuditCommand = (cmd) => isCommandOfType(cmd, "npmAudit");
1412
+ var isNpmOutdatedCommand = (cmd) => isCommandOfType(cmd, "npmOutdated");
1413
+ var isGitPushCommand = (cmd) => isCommandOfType(cmd, "gitPush");
1414
+ var isTestRunnerCommand2 = (cmd) => isCommandOfType(cmd, "testRunner");
1415
+ var isLintCommand = (cmd) => isCommandOfType(cmd, "lintCommand");
1416
+ var isNpmRunScriptCommand = (cmd) => isCommandOfType(cmd, "npmRunScript");
1417
+ var isCatCommand = (cmd) => isCommandOfType(cmd, "catCommand");
1418
+ function isScopedGitStatusOrDiffStatCommand(cmd) {
1419
+ if (!isCommandOfType(cmd, "gitMutable")) return false;
1420
+ if (!isCommandOfType(cmd, "gitDiffScoped")) return false;
1421
+ if (/^\s*git\s+diff\b/i.test(cmd) && !/--stat\b/.test(cmd)) return false;
1422
+ return true;
1423
+ }
1424
+ function gitStateFingerprintSync(cwd) {
1425
+ try {
1426
+ const headResult = runGit(["rev-parse", "HEAD"], { cwd });
1427
+ if (headResult.exitCode !== 0) return null;
1428
+ const headSha = headResult.stdout.trim();
1429
+ let statusHash = "";
1430
+ const statusResult = runGit(["status", "--porcelain"], { cwd });
1431
+ if (statusResult.exitCode === 0) {
1432
+ statusHash = shortFingerprint(statusResult.stdout);
1433
+ }
1434
+ const key = `${headSha}\0${statusHash}`;
1435
+ return shortFingerprint(key);
1436
+ } catch {
1437
+ return null;
1438
+ }
1439
+ }
1440
+ var DIR_FINGERPRINT_LISTING_CAP_ENTRIES = 1e4;
1441
+ function dirStateFingerprintSync(path2) {
1442
+ try {
1443
+ const stat = statSync2(path2);
1444
+ if (!stat.isDirectory()) return null;
1445
+ const entries = readdirSync(path2);
1446
+ if (entries.length <= DIR_FINGERPRINT_LISTING_CAP_ENTRIES) {
1447
+ return shortFingerprint(entries.slice().sort().join("\0"));
1448
+ }
1449
+ return shortFingerprint(stat.mtimeMs.toString());
1450
+ } catch {
1451
+ return null;
1452
+ }
1453
+ }
1454
+ var FILE_FINGERPRINT_CONTENT_CAP_BYTES = 2 * 1024 * 1024;
1455
+ function fileStateFingerprintSync(path2) {
1456
+ try {
1457
+ const stat = statSync2(path2);
1458
+ if (!stat.isFile()) return null;
1459
+ if (stat.size <= FILE_FINGERPRINT_CONTENT_CAP_BYTES) {
1460
+ return shortFingerprint(readFileSync2(path2));
1461
+ }
1462
+ return shortFingerprint(`${stat.mtimeMs}\0${stat.size}`);
1463
+ } catch {
1464
+ return null;
1465
+ }
1466
+ }
1467
+ function depLockfileFingerprintSync(cmd, cwd) {
1468
+ if (!cwd) return null;
1469
+ const stripped = cmd.trim();
1470
+ const firstToken = stripped.split(/\s+/)[0]?.toLowerCase() || "";
1471
+ if (!firstToken) return null;
1472
+ const candidates = firstToken === "uv" ? DEP_LOCKFILES["uv"] : DEP_LOCKFILES[firstToken];
1473
+ if (!candidates) return null;
1474
+ for (const lockfile of candidates) {
1475
+ try {
1476
+ const content = readFileSync2(resolve(cwd, lockfile));
1477
+ return shortFingerprint(content);
1478
+ } catch {
1479
+ continue;
1480
+ }
1481
+ }
1482
+ return null;
1483
+ }
1484
+ function normalizeCommandForCacheKey(cmd) {
1485
+ const trimmed = cmd.trim();
1486
+ const tokens = [];
1487
+ let current = "";
1488
+ let quoteChar = null;
1489
+ const flush = () => {
1490
+ if (current.length > 0) {
1491
+ tokens.push(current);
1492
+ current = "";
1493
+ }
1494
+ };
1495
+ for (let i = 0; i < trimmed.length; i++) {
1496
+ const ch = trimmed[i];
1497
+ if (quoteChar !== null) {
1498
+ current += ch;
1499
+ if (ch === quoteChar) quoteChar = null;
1500
+ continue;
1501
+ }
1502
+ if (ch === '"' || ch === "'") {
1503
+ quoteChar = ch;
1504
+ current += ch;
1505
+ continue;
1506
+ }
1507
+ if (/\s/.test(ch)) {
1508
+ flush();
1509
+ continue;
1510
+ }
1511
+ current += ch === "\\" ? "/" : ch;
1512
+ }
1513
+ flush();
1514
+ const normalized_tokens = tokens.map((token) => {
1515
+ if (token.startsWith("-") || ["&&", "||", "|", ">", ">>", ";", "&"].includes(token)) {
1516
+ return token;
1517
+ }
1518
+ if (token.startsWith("./") && !token.startsWith("../")) {
1519
+ token = token.slice(2);
1520
+ }
1521
+ if (token.endsWith("/") && token !== "/") {
1522
+ token = token.slice(0, -1);
1523
+ }
1524
+ return token || ".";
1525
+ });
1526
+ return normalized_tokens.join(" ");
1527
+ }
1528
+ function commandHashSync(command, cwd = null) {
1529
+ const normalized = normalizeCommandForCacheKey(command);
1530
+ let key = cwd ? `${normalizePath(cwd)}\0${normalized}` : normalized;
1531
+ if (cwd && isGitMutableCommand(command)) {
1532
+ const fp = gitStateFingerprintSync(cwd);
1533
+ if (fp) key = `${key}\0git:${fp}`;
1534
+ }
1535
+ if (cwd && isDirListingCommand(command)) {
1536
+ const target = extractFirstPathArg(command, cwd, cwd);
1537
+ if (target) {
1538
+ const fp = dirStateFingerprintSync(target);
1539
+ if (fp) key = `${key}\0dir:${fp}`;
1540
+ }
1541
+ }
1542
+ if (isDepListCommand(command)) {
1543
+ const fp = depLockfileFingerprintSync(command, cwd);
1544
+ if (fp) key = `${key}\0lockfile:${fp}`;
1545
+ }
1546
+ if (cwd && (isNpmInstallCommand(command) || isNpmAuditCommand(command) || isNpmOutdatedCommand(command))) {
1547
+ const fp = depLockfileFingerprintSync(command, cwd);
1548
+ if (fp) key = `${key}\0npm-install:${fp}`;
1549
+ }
1550
+ return shortFingerprint(key);
1551
+ }
1552
+ function commandHash(command, cwd = null) {
1553
+ return Promise.resolve(commandHashSync(command, cwd));
1554
+ }
1555
+ function bashOutputIdSync(command, output, cwd = null) {
1556
+ return shortFingerprint(`${commandHashSync(command, cwd)}\0${output}`);
1557
+ }
1558
+ function computeBashFingerprints(command, cwd) {
1559
+ const fingerprints = {};
1560
+ if (cwd && (isGitMutableCommand(command) || isGitPushCommand(command) || isTestRunnerCommand2(command) || isLintCommand(command) || isBuildCommand(command) || isNpmRunScriptCommand(command))) {
1561
+ const fp = gitStateFingerprintSync(cwd);
1562
+ if (fp) fingerprints.git = fp;
1563
+ }
1564
+ if (cwd && isDirListingCommand(command)) {
1565
+ const target = extractFirstPathArg(command, cwd, cwd);
1566
+ if (target) {
1567
+ const fp = dirStateFingerprintSync(target);
1568
+ if (fp) fingerprints.dir = fp;
1569
+ }
1570
+ }
1571
+ if (isDepListCommand(command) || cwd && (isNpmInstallCommand(command) || isNpmAuditCommand(command) || isNpmOutdatedCommand(command))) {
1572
+ const fp = depLockfileFingerprintSync(command, cwd);
1573
+ if (fp) fingerprints.lockfile = fp;
1574
+ }
1575
+ if (cwd && isCatCommand(command)) {
1576
+ const target = extractFirstPathArg(command, cwd, null);
1577
+ if (target) {
1578
+ const fp = fileStateFingerprintSync(target);
1579
+ if (fp) fingerprints.file = fp;
1580
+ }
1581
+ }
1582
+ return Object.keys(fingerprints).length > 0 ? fingerprints : void 0;
1583
+ }
1584
+ function isBashEntryStale(entry, command, cwd) {
1585
+ const stored = entry.fingerprints;
1586
+ if (!stored) return false;
1587
+ const current = computeBashFingerprints(command, cwd);
1588
+ if (stored.git !== void 0 && stored.git !== current?.git) return true;
1589
+ if (stored.dir !== void 0 && stored.dir !== current?.dir) return true;
1590
+ if (stored.lockfile !== void 0 && stored.lockfile !== current?.lockfile) return true;
1591
+ if (stored.file !== void 0 && stored.file !== current?.file) return true;
1592
+ return false;
1593
+ }
1594
+ function tokenizeShellArgs(cmd) {
1595
+ const trimmed = cmd.trim();
1596
+ const tokens = [];
1597
+ let current = "";
1598
+ let quoteChar = null;
1599
+ let hasToken = false;
1600
+ for (let i = 0; i < trimmed.length; i++) {
1601
+ const ch = trimmed[i];
1602
+ if (quoteChar !== null) {
1603
+ if (ch === quoteChar) {
1604
+ quoteChar = null;
1605
+ } else {
1606
+ current += ch;
1607
+ }
1608
+ continue;
1609
+ }
1610
+ if (ch === '"' || ch === "'") {
1611
+ quoteChar = ch;
1612
+ hasToken = true;
1613
+ continue;
1614
+ }
1615
+ if (/\s/.test(ch)) {
1616
+ if (hasToken) {
1617
+ tokens.push(current);
1618
+ current = "";
1619
+ hasToken = false;
1620
+ }
1621
+ continue;
1622
+ }
1623
+ current += ch;
1624
+ hasToken = true;
1625
+ }
1626
+ if (hasToken) tokens.push(current);
1627
+ return tokens;
1628
+ }
1629
+ function extractFirstPathArg(cmd, cwd, fallback) {
1630
+ const tokens = tokenizeShellArgs(cmd);
1631
+ for (let i = 1; i < tokens.length; i++) {
1632
+ const token = tokens[i];
1633
+ if (!token.startsWith("-")) {
1634
+ if (!token.startsWith("/")) {
1635
+ return resolve(cwd, token);
1636
+ }
1637
+ return token;
1638
+ }
1639
+ }
1640
+ return fallback;
1641
+ }
1642
+ var ISSUE_LINE_PATTERN = /\b(?:error|fail(?:ed|ure)?|warning)\b/i;
1643
+ function summarizeOutputDelta(oldOutput, newOutput) {
1644
+ if (oldOutput === newOutput) return null;
1645
+ const oldLines = oldOutput.split("\n");
1646
+ const newLines = newOutput.split("\n");
1647
+ const oldIssueLines = oldLines.filter((l) => ISSUE_LINE_PATTERN.test(l));
1648
+ if (oldIssueLines.length > 0) {
1649
+ const newIssueLines = newLines.filter((l) => ISSUE_LINE_PATTERN.test(l));
1650
+ const priorTotal = oldIssueLines.length;
1651
+ const availableCounts = /* @__PURE__ */ new Map();
1652
+ for (const l of newIssueLines) availableCounts.set(l, (availableCounts.get(l) ?? 0) + 1);
1653
+ let resolved = 0;
1654
+ for (const l of oldIssueLines) {
1655
+ const avail = availableCounts.get(l) ?? 0;
1656
+ if (avail > 0) {
1657
+ availableCounts.set(l, avail - 1);
1658
+ } else {
1659
+ resolved++;
1660
+ }
1661
+ }
1662
+ const remaining = newIssueLines.length;
1663
+ return `[token-goat: delta] ${resolved} of ${priorTotal} prior issues resolved; remaining: ${remaining}`;
1664
+ }
1665
+ return `[token-goat: delta] output changed: ${oldLines.length} -> ${newLines.length} lines`;
1666
+ }
1667
+ function storeBashOutputSync(command, output, exitCode, cwd = null) {
1668
+ const id = bashOutputIdSync(command, output, cwd);
1669
+ const commandKey = commandHashSync(command, cwd);
1670
+ const fingerprints = computeBashFingerprints(command, cwd);
1671
+ const redactedOutput = redactSecrets(output).text;
1672
+ const redactedCommand = redactSecrets(command).text;
1673
+ const entry = {
1674
+ id,
1675
+ command: redactedCommand,
1676
+ output: redactedOutput,
1677
+ exitCode,
1678
+ storedAt: Date.now(),
1679
+ sizeBytes: Buffer.byteLength(redactedOutput, "utf-8"),
1680
+ ...fingerprints ? { fingerprints } : {}
1681
+ };
1682
+ _byId.set(id, entry);
1683
+ _byId.set(commandKey, entry);
1684
+ storeBlob(BASH_OUTPUT_SUBDIR, id, entry);
1685
+ if (commandKey !== id) storeBlob(BASH_OUTPUT_SUBDIR, commandKey, { latestId: id });
1686
+ indexRecallEntry("bash", id, redactedCommand, `${redactedCommand}
1687
+ ${redactedOutput}`, entry.storedAt);
1688
+ return id;
1689
+ }
1690
+ function coerceBashRedirect(raw) {
1691
+ if (raw === null || typeof raw !== "object") return null;
1692
+ const latest = raw["latestId"];
1693
+ return typeof latest === "string" && latest.length > 0 ? latest : null;
1694
+ }
1695
+ function storeBashOutput(command, output, exitCode, cwd = null) {
1696
+ return Promise.resolve(storeBashOutputSync(command, output, exitCode, cwd));
1697
+ }
1698
+ function coerceBashEntry(raw) {
1699
+ if (raw === null || typeof raw !== "object") return null;
1700
+ const o = raw;
1701
+ 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") {
1702
+ return null;
1703
+ }
1704
+ const entry = {
1705
+ id: o["id"],
1706
+ command: o["command"],
1707
+ output: o["output"],
1708
+ exitCode: o["exitCode"],
1709
+ storedAt: o["storedAt"],
1710
+ sizeBytes: o["sizeBytes"]
1711
+ };
1712
+ const rawFingerprints = o["fingerprints"];
1713
+ if (rawFingerprints !== null && typeof rawFingerprints === "object") {
1714
+ const f = rawFingerprints;
1715
+ const fingerprints = {};
1716
+ if (typeof f["git"] === "string") fingerprints.git = f["git"];
1717
+ if (typeof f["dir"] === "string") fingerprints.dir = f["dir"];
1718
+ if (typeof f["lockfile"] === "string") fingerprints.lockfile = f["lockfile"];
1719
+ if (typeof f["file"] === "string") fingerprints.file = f["file"];
1720
+ if (Object.keys(fingerprints).length > 0) return { ...entry, fingerprints };
1721
+ }
1722
+ return entry;
1723
+ }
1724
+ function getBashOutput(id) {
1725
+ const hit = _byId.get(id);
1726
+ if (hit !== void 0) return hit;
1727
+ if (isBlobStale(BASH_OUTPUT_SUBDIR, id)) return null;
1728
+ const raw = loadBlob(BASH_OUTPUT_SUBDIR, id);
1729
+ const target = coerceBashRedirect(raw);
1730
+ if (target !== null) {
1731
+ if (isBlobStale(BASH_OUTPUT_SUBDIR, target)) return null;
1732
+ const latest = coerceBashEntry(loadBlob(BASH_OUTPUT_SUBDIR, target));
1733
+ if (latest === null) return null;
1734
+ _byId.set(target, latest);
1735
+ _byId.set(id, latest);
1736
+ return latest;
1737
+ }
1738
+ const entry = coerceBashEntry(raw);
1739
+ if (entry === null) return null;
1740
+ _byId.set(id, entry);
1741
+ return entry;
1742
+ }
1743
+ registerReset(() => {
1744
+ _byId = /* @__PURE__ */ new Map();
1745
+ });
1746
+
1747
+ export {
1748
+ DEFAULT_MAX_LINES,
1749
+ DEFAULT_MAX_BYTES,
1750
+ MAX_INSPECT_BYTES,
1751
+ LONG_LINE_MAX_CHARS,
1752
+ getMaxInputBytes,
1753
+ INPUT_MAX_LINE_CHARS,
1754
+ clipWideLines,
1755
+ clampKeepingEnds,
1756
+ compressionMarker,
1757
+ combineStreams,
1758
+ ERROR_SIGNAL_RE,
1759
+ TIMESTAMP_PREFIX_RE,
1760
+ REDIRECT_TOKEN_RE,
1761
+ hasBareBackgroundOrNewline,
1762
+ isRedirectAmpersand,
1763
+ hasUnquotedOperator,
1764
+ splitOwnTrailingNotices,
1765
+ safeDecode,
1766
+ normalise,
1767
+ hasHighEntropyToken,
1768
+ dedupeConsecutive,
1769
+ truncateMiddleSmart,
1770
+ capBytes,
1771
+ capTokens,
1772
+ headTailCompress,
1773
+ truncateTableRows,
1774
+ trimRepeatedPrefix,
1775
+ stripTimestamps,
1776
+ splitBlocks,
1777
+ maybeNote,
1778
+ squeezeBlankLines,
1779
+ preserveStderrOnError,
1780
+ pathName,
1781
+ pathStem,
1782
+ positionalArgs,
1783
+ stripPrefixes,
1784
+ resolvePackageManagerScript,
1785
+ shlexSplit,
1786
+ capLongLines,
1787
+ clipLongMatchLine,
1788
+ clipGrepLines,
1789
+ fallbackTruncate,
1790
+ compressBashOutput,
1791
+ dedupeCombinedOutput,
1792
+ compressTestOutput,
1793
+ byteLength,
1794
+ isLockFile,
1795
+ isManifestFile,
1796
+ isInBuildDir,
1797
+ isGeneratedFile,
1798
+ isBuildCommand,
1799
+ isTestRunnerCommand,
1800
+ getMonitoringRecallHint,
1801
+ RECALL_DEFAULT_LIMIT,
1802
+ isRecallCacheType,
1803
+ indexRecallEntry,
1804
+ listRecentRecall,
1805
+ searchRecall,
1806
+ BASH_OUTPUT_SUBDIR,
1807
+ isScopedGitStatusOrDiffStatCommand,
1808
+ normalizeCommandForCacheKey,
1809
+ commandHash,
1810
+ bashOutputIdSync,
1811
+ isBashEntryStale,
1812
+ summarizeOutputDelta,
1813
+ storeBashOutputSync,
1814
+ storeBashOutput,
1815
+ getBashOutput
1816
+ };