token-goat 2.9.13 → 2.9.14

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 (44) hide show
  1. package/README.md +13 -0
  2. package/dist/token-goat-chunk-2ESBO4IN.mjs +209 -0
  3. package/dist/token-goat-chunk-3BTK54F3.mjs +1733 -0
  4. package/dist/{token-goat-chunk-2WC4ZUXN.mjs → token-goat-chunk-3XQPEJMV.mjs} +1 -1
  5. package/dist/token-goat-chunk-4SDX3QP3.mjs +122 -0
  6. package/dist/token-goat-chunk-7OGKZ7AP.mjs +142 -0
  7. package/dist/token-goat-chunk-AH6QILZM.mjs +26 -0
  8. package/dist/token-goat-chunk-AMYCQJX4.mjs +1548 -0
  9. package/dist/{token-goat-chunk-6B44WLIF.mjs → token-goat-chunk-ASVVF4JV.mjs} +10 -7
  10. package/dist/{token-goat-chunk-3ESRORNM.mjs → token-goat-chunk-DAMXYVIW.mjs} +12026 -11796
  11. package/dist/{token-goat-chunk-YOA4N6WA.mjs → token-goat-chunk-DBNY4RLN.mjs} +2 -2
  12. package/dist/token-goat-chunk-EG3663UT.mjs +226 -0
  13. package/dist/token-goat-chunk-EZNVAIR3.mjs +21 -0
  14. package/dist/token-goat-chunk-GMOUBOX4.mjs +386 -0
  15. package/dist/{token-goat-chunk-B3CTCQTH.mjs → token-goat-chunk-HF6H7RNK.mjs} +1 -1
  16. package/dist/{token-goat-chunk-ZZI3IDQZ.mjs → token-goat-chunk-IT6O3PNN.mjs} +2874 -7088
  17. package/dist/token-goat-chunk-NEI4NC54.mjs +424 -0
  18. package/dist/token-goat-chunk-O5WAMISC.mjs +167 -0
  19. package/dist/{token-goat-chunk-RDITECDL.mjs → token-goat-chunk-PGGDW7DZ.mjs} +28 -13
  20. package/dist/token-goat-chunk-POBYR64E.mjs +1632 -0
  21. package/dist/{token-goat-chunk-4NXUKV7D.mjs → token-goat-chunk-PRJVGIC5.mjs} +6 -4
  22. package/dist/{token-goat-chunk-U7X6LQD2.mjs → token-goat-chunk-RUDOKYPJ.mjs} +10160 -10053
  23. package/dist/token-goat-chunk-SAQ5PG4L.mjs +2634 -0
  24. package/dist/{token-goat-chunk-UMXJN7DI.mjs → token-goat-chunk-T2IWWTHB.mjs} +288 -2654
  25. package/dist/{token-goat-chunk-JOXLE672.mjs → token-goat-chunk-VZYD4OZB.mjs} +1164 -92
  26. package/dist/token-goat-chunk-XEH6KBWW.mjs +23 -0
  27. package/dist/token-goat-chunk-XQF5J25J.mjs +33 -0
  28. package/dist/token-goat-chunk-XTQAOTSO.mjs +89 -0
  29. package/dist/{token-goat-chunk-QWSUZWFP.mjs → token-goat-chunk-XVZ4MNQC.mjs} +587 -587
  30. package/dist/token-goat-chunk-Y4AFKTHK.mjs +22 -0
  31. package/dist/token-goat-chunk-YHGTGG6K.mjs +396 -0
  32. package/dist/{token-goat-chunk-FZU7GMUS.mjs → token-goat-chunk-YTQHZJXW.mjs} +20 -11
  33. package/dist/token-goat-chunk-Z6UXPYJA.mjs +62 -0
  34. package/dist/token-goat-chunk-ZD4EM4LR.mjs +30 -0
  35. package/dist/token-goat-chunk-ZFM4PWXL.mjs +585 -0
  36. package/dist/{token-goat-chunk-FQCNJV4V.mjs → token-goat-chunk-ZYNNQ36L.mjs} +116 -330
  37. package/dist/token-goat-hook.mjs +12 -8
  38. package/dist/token-goat.core.mjs +22 -10
  39. package/docs/cli.md +1 -0
  40. package/package.json +4 -2
  41. package/dist/token-goat-chunk-JVNPCQB7.mjs +0 -31
  42. package/dist/token-goat-chunk-P2PU4CR5.mjs +0 -26
  43. package/dist/token-goat-chunk-QKXBGBQR.mjs +0 -3653
  44. package/dist/token-goat-chunk-UM47DRD3.mjs +0 -242
@@ -1,17 +1,26 @@
1
1
  import { createRequire as __cjsRequire } from 'node:module';
2
2
  const require = __cjsRequire(import.meta.url);
3
3
  import {
4
+ DEFAULT_MAX_AGE_MS,
5
+ blobPath,
4
6
  countRedactionPlaceholders,
5
- detectHarness,
6
- loadConfig,
7
- parse,
7
+ getDb,
8
+ isBlobStale,
9
+ loadBlob,
8
10
  recordStat,
9
11
  recordUnmappedTool,
10
12
  redactSecrets,
11
13
  savedTokensFromBytes,
12
- stringify,
14
+ storeBlob,
13
15
  stripAnsiEscapes
14
- } from "./token-goat-chunk-UMXJN7DI.mjs";
16
+ } from "./token-goat-chunk-T2IWWTHB.mjs";
17
+ import {
18
+ detectHarness,
19
+ loadConfig,
20
+ parse,
21
+ shortFingerprint,
22
+ stringify
23
+ } from "./token-goat-chunk-SAQ5PG4L.mjs";
15
24
  import {
16
25
  registerReset
17
26
  } from "./token-goat-chunk-EEIDFMEM.mjs";
@@ -24,16 +33,21 @@ import {
24
33
  ensureDirSync,
25
34
  escapeRegExp,
26
35
  extractErrorMessage,
36
+ foldPath,
37
+ globalDbPath,
27
38
  hookCommandFor,
28
39
  hookPowershellCommand,
40
+ isCaseInsensitiveFs,
29
41
  neutralizeOutsideFences,
30
42
  normalizeDarwinSystemAlias,
43
+ normalizePath,
31
44
  projectPathIsConsultable,
32
45
  projectScopeRoot,
33
46
  recordCreatedConfig,
34
47
  removeCreatedBackups,
35
48
  removeFileInScope,
36
49
  resolveIndexPath,
50
+ runGit,
37
51
  safeSlice,
38
52
  stripDelimitedBlock,
39
53
  stripOwnHooksFromMap,
@@ -43,7 +57,7 @@ import {
43
57
  withInstallScope,
44
58
  writeIfDifferent,
45
59
  writeJsonSettings
46
- } from "./token-goat-chunk-QKXBGBQR.mjs";
60
+ } from "./token-goat-chunk-AMYCQJX4.mjs";
47
61
  import {
48
62
  init_define_import_meta_env
49
63
  } from "./token-goat-chunk-A37V4PBF.mjs";
@@ -842,12 +856,12 @@ async function main() {
842
856
  return
843
857
  }
844
858
 
845
- process.stdout.write(JSON.stringify(translate(copilotEvent, resp, toolName, originalToolArgs)))
859
+ process.stdout.write(JSON.stringify(translate(copilotEvent, resp, toolName, originalToolArgs, payload)))
846
860
  }
847
861
 
848
862
  ${MATERIALIZE_SHRUNK_IMAGE_JS}
849
863
 
850
- function translate(copilotEvent, resp, toolName, originalToolArgs) {
864
+ function translate(copilotEvent, resp, toolName, originalToolArgs, payload) {
851
865
  if (copilotEvent === 'preToolUse') {
852
866
  const hso = resp && resp.hookSpecificOutput
853
867
  const denied = resp && (resp.decision === 'block' || (hso && hso.permissionDecision === 'deny'))
@@ -869,52 +883,20 @@ function translate(copilotEvent, resp, toolName, originalToolArgs) {
869
883
  }
870
884
 
871
885
  if (copilotEvent === 'postToolUse') {
872
- // Verified against the shipping @github/copilot 1.0.80 bundle, not against the docs page.
873
- // NativeHookPipelineProcessor.postToolExecution (app.js offset 2043150) gates on
874
- // toolResult.resultType === "success", then does n.toolResultJson && CSr(e.toolResult,
875
- // n.toolResultJson), where CSr (offset 2032350) is an in-place Object.assign; the mutated
876
- // object is re-serialized back to native in the postTool callback at offset 1793926. So
877
- // modifiedResult IS honored on this event, and token-goat's rewriteOutput producers
878
- // (compression, injection fencing, image shrink) really do reach the model. resultType is
879
- // hardcoded 'success' because success is the only branch this event ever runs on.
880
- //
881
- // additionalContext on THIS event is dropped on the JS path. grep -abo "onAdditionalContext:"
882
- // app.js returns nothing, so the callback is never supplied, and the two "onAdditionalContext?"
883
- // call sites (offsets 2041896 and 2043300) are both no-ops. preToolsExecution (offset 2041832)
884
- // additionally pushes each context into an array that IS drained into the native return
885
- // payload (additional_contexts, offset 1791950); postToolExecution has no such push and its
886
- // native return payload (offset 1793926) has no additional_contexts key. Residual, stated
887
- // honestly: the native hookProcessorPostToolUse might fold additionalContext into the
888
- // toolResultJson it returns, and that was NOT verified. The evidence leans against it, since
889
- // the failure sibling path appends its context explicitly in JS via
890
- // hookAppendPostToolUseFailureContext and there is no success-path counterpart. The
891
- // out.additionalContext below is kept as cheap best-effort, not as a channel anything should
892
- // depend on -- see src/pending_context.ts, whose whole design depended on it.
893
- //
894
- // Failed tool calls never reach this event. postToolUse and postToolUseFailure are two
895
- // distinct hook events (both listed in the runtime.node hook-event enum at offset 101618150),
896
- // and the shipped copilot-sdk/types.d.ts says onPostToolUse "does not fire for non-success
897
- // results". The failure event cannot carry a fence either: PostToolUseFailureHookInput carries
898
- // only a stringified error message, not the tool result, and PostToolUseFailureHookOutput
899
- // consumes only additionalContext -- "modifiedResult or suppressOutput are not honored for
900
- // failure hooks". rejected/denied/timeout results trigger no post hook at all. So on Copilot,
901
- // the output of a failed tool call reaches the model unfenced, uncompressed and unshrunk, and
902
- // no response shape this shim could emit changes that. That gap is real and still open. What
903
- // is now wired is the narrower thing that IS possible there: the postToolUseFailure branch
904
- // below carries advisory text alongside the failure, and never rewrites it.
905
- //
906
- // Emitted camelCase-only. The inbound side around line 247 also tolerates a snake_case "VS
907
- // Code compatible" shape, and this comment used to justify the camelCase choice by claiming
908
- // PascalCase event registration selects the snake_case response format. That reasoning is
909
- // UNVERIFIED in 1.0.80: every hook-response field in the native string tables is camelCase,
910
- // and the snake_case hits in the bundle are unrelated internal Rust identifiers. camelCase
911
- // stays because it is what the bundle reads; the old justification is no longer asserted.
886
+ // modifiedResult is honored on success: rewriteOutput (compression, fencing, image shrink) reaches LLM.
887
+ // additionalContext is dropped on Copilot CLI JS path; we fold context into modifiedResult.textResultForLlm.
912
888
  const hso = resp && resp.hookSpecificOutput
913
889
  const updatedToolOutput = hso && hso.updatedToolOutput
914
890
  const context = extractContext(resp)
915
891
  const out = {}
916
892
  if (typeof updatedToolOutput === 'string') {
917
893
  out.modifiedResult = { resultType: 'success', textResultForLlm: updatedToolOutput }
894
+ } else if (context) {
895
+ const rawResult = payload && payload.toolResult
896
+ const originalText = rawResult && (typeof rawResult.textResultForLlm === 'string' ? rawResult.textResultForLlm : typeof rawResult.text_result_for_llm === 'string' ? rawResult.text_result_for_llm : undefined)
897
+ if (typeof originalText === 'string') {
898
+ out.modifiedResult = { resultType: 'success', textResultForLlm: originalText + '\\n\\n[token-goat: ' + context + ']' }
899
+ }
918
900
  }
919
901
  if (context) out.additionalContext = context
920
902
  return out
@@ -1747,6 +1729,10 @@ function getCwd(event) {
1747
1729
  const value = event.raw && typeof event.raw === "object" ? event.raw["cwd"] : void 0;
1748
1730
  return typeof value === "string" && value !== "" ? value : void 0;
1749
1731
  }
1732
+ function getTranscriptPath(event) {
1733
+ const value = event.raw && typeof event.raw === "object" ? event.raw["transcript_path"] : void 0;
1734
+ return typeof value === "string" && value !== "" ? value : void 0;
1735
+ }
1750
1736
  function extractToolResponseField(raw, keys) {
1751
1737
  const resp = raw["tool_response"];
1752
1738
  if (typeof resp === "string") return resp;
@@ -2954,9 +2940,1032 @@ function byteLength(s) {
2954
2940
  return utf8Len(s);
2955
2941
  }
2956
2942
 
2957
- // src/install.ts
2943
+ // src/index_reader.ts
2944
+ init_define_import_meta_env();
2945
+
2946
+ // src/sql_path.ts
2947
+ init_define_import_meta_env();
2948
+ function pathEqClause(column) {
2949
+ return isCaseInsensitiveFs() ? `TG_LOWER(${column}) = ?` : `${column} = ?`;
2950
+ }
2951
+ function pathSuffixClause(column) {
2952
+ const col = isCaseInsensitiveFs() ? `TG_LOWER(${column})` : column;
2953
+ return {
2954
+ // Both separators are accepted because not every writer into `symbols` stores a normalizePath'd (forward-slash) file_path, and the JS-side path-boundary test this narrowing feeds treats `/` and `\` alike.
2955
+ clause: `(${col} = ? OR substr(${col}, -length(?)) = ? OR substr(${col}, -length(?)) = ?)`,
2956
+ params: (baseName) => {
2957
+ const folded = foldPath(baseName);
2958
+ return [folded, `/${folded}`, `/${folded}`, `\\${folded}`, `\\${folded}`];
2959
+ }
2960
+ };
2961
+ }
2962
+ function projectScopeClause(column) {
2963
+ const caseInsensitive = isCaseInsensitiveFs();
2964
+ const col = caseInsensitive ? `TG_LOWER(${column})` : column;
2965
+ return {
2966
+ // Parenthesised so an OR anywhere in the surrounding WHERE cannot bind tighter than the pair.
2967
+ clause: `(${col} >= ? AND ${col} < ?)`,
2968
+ params: (root) => {
2969
+ const folded = caseInsensitive ? foldPath(normalizePath(root)) : normalizePath(root);
2970
+ const lower = folded.endsWith("/") ? folded : `${folded}/`;
2971
+ const upper = `${lower.slice(0, -1)}0`;
2972
+ return [lower, upper];
2973
+ }
2974
+ };
2975
+ }
2976
+
2977
+ // src/index_reader.ts
2978
+ function toSymbolEntry(row) {
2979
+ return {
2980
+ filePath: row.file_path,
2981
+ name: row.name,
2982
+ kind: row.kind,
2983
+ lineStart: row.line_start,
2984
+ lineEnd: row.line_end,
2985
+ body: row.body ?? "",
2986
+ docstring: row.docstring ?? "",
2987
+ parent: row.parent ?? ""
2988
+ };
2989
+ }
2990
+ function toRefEntry(row) {
2991
+ return {
2992
+ filePath: row.file_path,
2993
+ name: row.name,
2994
+ line: row.line,
2995
+ col: row.col,
2996
+ context: row.context ?? ""
2997
+ };
2998
+ }
2999
+ function applyRootDirScope(rootDir, column, where, params) {
3000
+ if (rootDir === void 0) return;
3001
+ const { clause, params: bounds } = projectScopeClause(column);
3002
+ where.push(clause);
3003
+ params.push(...bounds(rootDir));
3004
+ }
3005
+ function buildSymbolWhere(opts) {
3006
+ const where = [];
3007
+ const params = [];
3008
+ if (opts.name !== void 0) {
3009
+ where.push("name = ?");
3010
+ params.push(opts.name);
3011
+ }
3012
+ if (opts.filePath !== void 0) {
3013
+ where.push(pathEqClause("file_path"));
3014
+ params.push(foldPath(opts.filePath));
3015
+ }
3016
+ if (opts.kind !== void 0) {
3017
+ where.push("kind = ?");
3018
+ params.push(opts.kind);
3019
+ }
3020
+ if (opts.fileBaseName !== void 0) {
3021
+ const { clause: suffixClause, params: suffixParams } = pathSuffixClause("file_path");
3022
+ where.push(suffixClause);
3023
+ params.push(...suffixParams(opts.fileBaseName));
3024
+ }
3025
+ if (opts.enclosingLine !== void 0) {
3026
+ where.push("line_start <= ? AND ? <= line_end");
3027
+ params.push(opts.enclosingLine, opts.enclosingLine);
3028
+ }
3029
+ applyRootDirScope(opts.rootDir, "file_path", where, params);
3030
+ return { clause: where.length > 0 ? `WHERE ${where.join(" AND ")}` : "", params };
3031
+ }
3032
+ var DEFAULT_QUERY_LIMIT = 100;
3033
+ function querySymbols(opts = {}, dbPath = globalDbPath()) {
3034
+ const { clause, params } = buildSymbolWhere(opts);
3035
+ const limit = opts.limit ?? DEFAULT_QUERY_LIMIT;
3036
+ const offset = opts.offset ?? 0;
3037
+ const sql = `SELECT file_path, name, kind, line_start, line_end, body, docstring, parent FROM symbols ${clause} ORDER BY file_path, line_start, rowid LIMIT ? OFFSET ?`;
3038
+ const db = getDb(dbPath);
3039
+ const rows = db.prepare(sql).all(...params, limit, offset);
3040
+ return rows.map(toSymbolEntry);
3041
+ }
3042
+ function distinctSymbolKinds(rootDir, dbPath = globalDbPath()) {
3043
+ const where = [];
3044
+ const params = [];
3045
+ applyRootDirScope(rootDir, "file_path", where, params);
3046
+ const clause = where.length > 0 ? `WHERE ${where.join(" AND ")}` : "";
3047
+ const db = getDb(dbPath);
3048
+ const rows = db.prepare(`SELECT DISTINCT kind FROM symbols ${clause} ORDER BY kind`).all(...params);
3049
+ return rows.map((r) => r.kind);
3050
+ }
3051
+ function countSymbols(opts = {}, dbPath = globalDbPath()) {
3052
+ const { clause, params } = buildSymbolWhere(opts);
3053
+ const sql = `SELECT COUNT(*) as cnt FROM symbols ${clause}`;
3054
+ const db = getDb(dbPath);
3055
+ const row = db.prepare(sql).get(...params);
3056
+ return row.cnt;
3057
+ }
3058
+ function buildRefsWhere(opts) {
3059
+ const where = ["name = ?"];
3060
+ const params = [opts.name];
3061
+ if (opts.filePath !== void 0) {
3062
+ where.push(pathEqClause("file_path"));
3063
+ params.push(foldPath(opts.filePath));
3064
+ }
3065
+ applyRootDirScope(opts.rootDir, "file_path", where, params);
3066
+ return { clause: `WHERE ${where.join(" AND ")}`, params };
3067
+ }
3068
+ function queryRefs(opts, dbPath = globalDbPath()) {
3069
+ const { clause, params } = buildRefsWhere(opts);
3070
+ const limit = opts.limit ?? DEFAULT_QUERY_LIMIT;
3071
+ const sql = `SELECT file_path, name, line, col, context FROM refs ${clause} ORDER BY file_path, line LIMIT ?`;
3072
+ const db = getDb(dbPath);
3073
+ const rows = db.prepare(sql).all(...params, limit);
3074
+ return rows.map(toRefEntry);
3075
+ }
3076
+ function countRefs(opts, dbPath = globalDbPath()) {
3077
+ const { clause, params } = buildRefsWhere(opts);
3078
+ const sql = `SELECT COUNT(*) as cnt FROM refs ${clause}`;
3079
+ const db = getDb(dbPath);
3080
+ const row = db.prepare(sql).get(...params);
3081
+ return row.cnt;
3082
+ }
3083
+ function queryRefsByContext(context, filePath, dbPath = globalDbPath()) {
3084
+ const sql = `SELECT file_path, name, line, col, context FROM refs WHERE context = ? AND ${pathEqClause("file_path")} ORDER BY line LIMIT 20`;
3085
+ const db = getDb(dbPath);
3086
+ const rows = db.prepare(sql).all(context, foldPath(filePath));
3087
+ return rows.map(toRefEntry);
3088
+ }
3089
+ var REF_COUNT_BATCH = 900;
3090
+ function queryRefCounts(names, dbPath = globalDbPath(), rootDir) {
3091
+ const counts = /* @__PURE__ */ new Map();
3092
+ if (names.length === 0) return counts;
3093
+ const db = getDb(dbPath);
3094
+ const scopeWhere = [];
3095
+ const scopeParams = [];
3096
+ applyRootDirScope(rootDir, "file_path", scopeWhere, scopeParams);
3097
+ const scopeSql = scopeWhere.length > 0 ? ` AND ${scopeWhere.join(" AND ")}` : "";
3098
+ const sqlFor = (width) => `SELECT name, COUNT(*) as c FROM refs WHERE name IN (${Array.from({ length: width }, () => "?").join(", ")})${scopeSql} GROUP BY name`;
3099
+ const full = names.length >= REF_COUNT_BATCH ? db.prepare(sqlFor(REF_COUNT_BATCH)) : void 0;
3100
+ db.transaction(() => {
3101
+ for (let start = 0; start < names.length; start += REF_COUNT_BATCH) {
3102
+ const batch = names.slice(start, start + REF_COUNT_BATCH);
3103
+ const stmt = batch.length === REF_COUNT_BATCH && full !== void 0 ? full : db.prepare(sqlFor(batch.length));
3104
+ for (const row of stmt.all(...batch, ...scopeParams)) {
3105
+ counts.set(row.name, row.c);
3106
+ }
3107
+ }
3108
+ })();
3109
+ return counts;
3110
+ }
3111
+ function getProjectFileEntries(rootDir, dbPath = globalDbPath()) {
3112
+ const db = getDb(dbPath);
3113
+ const { clause, params } = projectScopeClause("path");
3114
+ const rows = db.prepare(`SELECT path, sha, mtime, language, indexed_at, embed_sha, parser_sha FROM files WHERE ${clause}`).all(...params(rootDir));
3115
+ const out = /* @__PURE__ */ new Map();
3116
+ for (const row of rows) {
3117
+ out.set(foldPath(row.path), {
3118
+ filePath: row.path,
3119
+ sha: row.sha ?? "",
3120
+ mtime: row.mtime ?? 0,
3121
+ language: row.language ?? "unknown",
3122
+ indexedAt: row.indexed_at ?? 0,
3123
+ embedSha: row.embed_sha ?? "",
3124
+ parserSha: row.parser_sha ?? ""
3125
+ });
3126
+ }
3127
+ return out;
3128
+ }
3129
+ function getFileEntry(filePath, dbPath = globalDbPath()) {
3130
+ const db = getDb(dbPath);
3131
+ const row = db.prepare(
3132
+ `SELECT path, sha, mtime, language, indexed_at, embed_sha, parser_sha FROM files WHERE ${pathEqClause("path")}`
3133
+ ).get(foldPath(filePath));
3134
+ if (row === void 0) return null;
3135
+ return {
3136
+ filePath: row.path,
3137
+ sha: row.sha ?? "",
3138
+ mtime: row.mtime ?? 0,
3139
+ language: row.language ?? "unknown",
3140
+ indexedAt: row.indexed_at ?? 0,
3141
+ embedSha: row.embed_sha ?? "",
3142
+ parserSha: row.parser_sha ?? ""
3143
+ };
3144
+ }
3145
+ function sanitizeFtsQuery(query, join11 = "AND") {
3146
+ const terms = query.split(/\s+/).filter((t) => t.length > 0).map((t) => '"' + t.replace(/"/g, '""') + '"');
3147
+ return terms.join(join11 === "OR" ? " OR " : " ");
3148
+ }
3149
+ function runFtsQuery(db, match, limit, scope, rootDir) {
3150
+ const sql = `SELECT s.file_path, s.name, s.kind, s.line_start, s.line_end, s.body, s.docstring, s.parent FROM symbols_fts JOIN symbols s ON s.id = symbols_fts.rowid WHERE symbols_fts MATCH ?${scope !== void 0 ? ` AND ${scope.clause}` : ""} ORDER BY bm25(symbols_fts) LIMIT ?`;
3151
+ const params = [match];
3152
+ if (scope !== void 0 && rootDir !== void 0) {
3153
+ params.push(...scope.params(rootDir));
3154
+ }
3155
+ params.push(limit);
3156
+ const rows = db.prepare(sql).all(...params);
3157
+ return rows.map(toSymbolEntry);
3158
+ }
3159
+ function searchSymbolsFts(query, limit = 50, dbPath = globalDbPath(), rootDir) {
3160
+ const andMatch = sanitizeFtsQuery(query, "AND");
3161
+ if (andMatch === "") return [];
3162
+ const db = getDb(dbPath);
3163
+ const scope = rootDir !== void 0 ? projectScopeClause("s.file_path") : void 0;
3164
+ try {
3165
+ const andResults = runFtsQuery(db, andMatch, limit, scope, rootDir);
3166
+ if (andResults.length > 0) return andResults;
3167
+ const orMatch = sanitizeFtsQuery(query, "OR");
3168
+ if (orMatch === andMatch) return andResults;
3169
+ return runFtsQuery(db, orMatch, limit, scope, rootDir);
3170
+ } catch {
3171
+ return [];
3172
+ }
3173
+ }
3174
+
3175
+ // src/bash_output_cache.ts
3176
+ init_define_import_meta_env();
3177
+ import { readdirSync as readdirSync2, readFileSync as readFileSync5, statSync as statSync3 } from "fs";
3178
+ import { resolve as resolve4 } from "path";
3179
+
3180
+ // src/hints/lang_patterns.ts
3181
+ init_define_import_meta_env();
3182
+ var LOCK_FILE_NAMES = /* @__PURE__ */ new Set([
3183
+ "package-lock.json",
3184
+ "yarn.lock",
3185
+ "pnpm-lock.yaml",
3186
+ "cargo.lock",
3187
+ "poetry.lock",
3188
+ "pipfile.lock",
3189
+ "uv.lock",
3190
+ "gemfile.lock",
3191
+ "go.sum",
3192
+ "composer.lock",
3193
+ "mix.lock",
3194
+ "pubspec.lock",
3195
+ "package.resolved"
3196
+ ]);
3197
+ var MANIFEST_FILE_NAMES = /* @__PURE__ */ new Set([
3198
+ "package.json",
3199
+ "pyproject.toml",
3200
+ "cargo.toml",
3201
+ "go.mod",
3202
+ "go.work",
3203
+ "pom.xml",
3204
+ "build.gradle",
3205
+ "build.gradle.kts",
3206
+ "settings.gradle",
3207
+ "composer.json",
3208
+ "gemfile",
3209
+ "mix.exs",
3210
+ "pubspec.yaml",
3211
+ "cmakelists.txt",
3212
+ "makefile",
3213
+ "project.clj",
3214
+ // TypeScript / JavaScript project configs
3215
+ "tsconfig.json",
3216
+ "jsconfig.json",
3217
+ // Bundler / framework configs
3218
+ "vite.config.ts",
3219
+ "vite.config.js",
3220
+ "webpack.config.js",
3221
+ "webpack.config.ts",
3222
+ "rollup.config.js",
3223
+ "rollup.config.ts",
3224
+ "esbuild.config.js",
3225
+ "next.config.js",
3226
+ "next.config.ts",
3227
+ "nuxt.config.ts"
3228
+ ]);
3229
+ var MANIFEST_EXTENSIONS = /* @__PURE__ */ new Set([
3230
+ ".cabal"
3231
+ ]);
3232
+ var MANIFEST_BASENAME_PATTERNS = [
3233
+ /^tsconfig(\..+)?\.json$/i
3234
+ ];
3235
+ var BUILD_DIR_NAMES = /* @__PURE__ */ new Set([
3236
+ "dist",
3237
+ "target",
3238
+ "build",
3239
+ "out",
3240
+ "__pycache__",
3241
+ ".next",
3242
+ ".nuxt",
3243
+ ".output",
3244
+ ".gradle",
3245
+ "_build",
3246
+ ".build",
3247
+ "pkg",
3248
+ "obj"
3249
+ ]);
3250
+ var ALWAYS_GENERATED_EXTS = /* @__PURE__ */ new Set([
3251
+ ".pyc",
3252
+ ".pyo",
3253
+ ".pyd",
3254
+ ".class",
3255
+ ".o",
3256
+ ".a",
3257
+ ".so",
3258
+ ".dylib",
3259
+ ".dll",
3260
+ ".tsbuildinfo"
3261
+ ]);
3262
+ var CONDITIONALLY_GENERATED_EXTS = /* @__PURE__ */ new Set([
3263
+ ".map",
3264
+ ".d.ts"
3265
+ ]);
3266
+ var BUILD_COMMAND_PATTERNS = [
3267
+ // Rust / Cargo
3268
+ /^\s*cargo\s+(build|test|run|check|clippy)\b/i,
3269
+ // Go
3270
+ /^\s*go\s+(build|test|run|vet)\b/i,
3271
+ // Maven
3272
+ /^\s*mvn\b/i,
3273
+ // Gradle (direct or wrapper)
3274
+ /^\s*(?:gradle|\.\/gradlew|gradlew)\b/i,
3275
+ // Python / pip
3276
+ /^\s*pip\s+(install|freeze)\b/i,
3277
+ // 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.
3278
+ /^\s*npm\s+(audit|outdated)\b/i,
3279
+ // Poetry
3280
+ /^\s*poetry\s+(install|update)\b/i,
3281
+ // uv
3282
+ /^\s*uv\s+(sync|pip\s+install)\b/i,
3283
+ // Bundler (Ruby)
3284
+ /^\s*bundle\s+(install|update)\b/i,
3285
+ // Mix (Elixir)
3286
+ /^\s*mix\s+(deps\.get|compile|test)\b/i,
3287
+ // dotnet
3288
+ /^\s*dotnet\s+(build|test|restore)\b/i,
3289
+ // Make
3290
+ /^\s*make\b/i,
3291
+ // CMake build
3292
+ /^\s*cmake\s+--build\b/i,
3293
+ // Rake (Ruby)
3294
+ /^\s*rake\b/i,
3295
+ // TypeScript compiler (direct and via npx)
3296
+ /^\s*tsc(?:\s|$)/i,
3297
+ /^\s*npx\s+tsc(?:\s|$)/i,
3298
+ // Vite
3299
+ /^\s*vite\s+(build|dev|preview)\b/i,
3300
+ // Next.js
3301
+ /^\s*next\s+(build|dev|start)\b/i,
3302
+ // Nuxt
3303
+ /^\s*nuxt\s+(build|dev)\b/i,
3304
+ // Webpack
3305
+ /^\s*webpack\b/i,
3306
+ // esbuild
3307
+ /^\s*esbuild\b/i,
3308
+ // Rollup
3309
+ /^\s*rollup\b/i,
3310
+ // Turbo
3311
+ /^\s*turbo\s+(build|dev)\b/i
3312
+ ];
3313
+ var LOCK_FILE_COUNT = LOCK_FILE_NAMES.size;
3314
+ var MANIFEST_FILE_COUNT = MANIFEST_FILE_NAMES.size + MANIFEST_EXTENSIONS.size + MANIFEST_BASENAME_PATTERNS.length;
3315
+ var BUILD_DIR_COUNT = BUILD_DIR_NAMES.size;
3316
+ var GENERATED_EXT_COUNT = ALWAYS_GENERATED_EXTS.size + CONDITIONALLY_GENERATED_EXTS.size;
3317
+ function isLockFile(basename2) {
3318
+ return LOCK_FILE_NAMES.has(basename2.toLowerCase());
3319
+ }
3320
+ function isManifestFile(basename2) {
3321
+ const lower = basename2.toLowerCase();
3322
+ if (MANIFEST_FILE_NAMES.has(lower)) return true;
3323
+ const dot = lower.lastIndexOf(".");
3324
+ if (dot !== -1 && MANIFEST_EXTENSIONS.has(lower.slice(dot))) return true;
3325
+ if (MANIFEST_BASENAME_PATTERNS.some((re) => re.test(basename2))) return true;
3326
+ return false;
3327
+ }
3328
+ function pathSegments(filePath) {
3329
+ return filePath.split(/[/\\]/).filter((s) => s.length > 0);
3330
+ }
3331
+ function isInBuildDir(filePath) {
3332
+ const segments = pathSegments(filePath);
3333
+ for (let i = 0; i < segments.length - 1; i++) {
3334
+ const seg = segments[i];
3335
+ if (seg !== void 0 && BUILD_DIR_NAMES.has(seg.toLowerCase())) return true;
3336
+ }
3337
+ return false;
3338
+ }
3339
+ function isGeneratedFile(filePath) {
3340
+ const lower = filePath.toLowerCase();
3341
+ for (const ext of ALWAYS_GENERATED_EXTS) {
3342
+ if (lower.endsWith(ext)) return true;
3343
+ }
3344
+ for (const ext of CONDITIONALLY_GENERATED_EXTS) {
3345
+ if (lower.endsWith(ext) && isInBuildDir(filePath)) return true;
3346
+ }
3347
+ return false;
3348
+ }
3349
+ function isBuildCommand(cmd) {
3350
+ return BUILD_COMMAND_PATTERNS.some((re) => re.test(cmd));
3351
+ }
3352
+ var TEST_RUNNER_COMMAND_PATTERNS = [
3353
+ // pytest (Python)
3354
+ /^pytest(?:\s|$)/i,
3355
+ // Jest / Vitest (direct or via npx)
3356
+ /^(?:npx\s+)?jest(?:\s|$)/i,
3357
+ /^(?:npx\s+)?vitest(?:\s+run|\s+watch)?(?:\s|$)/i,
3358
+ // Go
3359
+ /^go test(?:\s|$)/i,
3360
+ // Rust / Cargo
3361
+ /^cargo test(?:\s|$)/i,
3362
+ // npm test / npm run test
3363
+ /^npm (?:test|run test)(?:\s|$)/i,
3364
+ // yarn test / yarn run test / pnpm test / pnpm run test
3365
+ /^(?:yarn|pnpm)(?:\s+run)? test(?:\s|$)/i
3366
+ ];
3367
+ function isTestRunnerCommand(cmd) {
3368
+ return TEST_RUNNER_COMMAND_PATTERNS.some((re) => re.test(cmd));
3369
+ }
3370
+ var MONITORING_COMMAND_PATTERNS = [
3371
+ // GitHub CI
3372
+ { pattern: /^gh run (?:watch|view|list)/, recallHint: '--grep "fail|error|pass|\u2713|\u2717|conclusion"' },
3373
+ { pattern: /^gh run view.*--log/, recallHint: '--tail 100 --grep "Error|FAIL|error"' },
3374
+ { pattern: /^gh pr checks/, recallHint: '--grep "fail|error|pass|pending"' },
3375
+ { pattern: /^gh pr view/, recallHint: '--grep "state|title|mergeable|review|OPEN|CLOSED|MERGED"' },
3376
+ { pattern: /^gh pr list/, recallHint: '--grep "OPEN|DRAFT|MERGED|#"' },
3377
+ { pattern: /^gh issue view/, recallHint: '--grep "state|title|label|OPEN|CLOSED"' },
3378
+ { pattern: /^gh workflow (?:run|list|view)/, recallHint: '--grep "completed|failed|in_progress"' },
3379
+ // Dev servers (Next, Vite, Nuxt, Remix, Astro)
3380
+ { pattern: /^(?:npx\s+)?next dev/, recallHint: '--tail 30 --grep "error|warn|ready|compiled"' },
3381
+ { pattern: /^(?:npx\s+)?next build/, recallHint: '--grep "error|warn|Failed|\u2713"' },
3382
+ { pattern: /^(?:npx\s+)?vite\b(?:\s+dev|\s+build|\s+preview)?/, recallHint: '--tail 20 --grep "error|warn|ready"' },
3383
+ { pattern: /^(?:npx\s+)?nuxt dev/, recallHint: '--tail 30 --grep "error|warn|ready"' },
3384
+ { pattern: /^(?:npx\s+)?remix dev/, recallHint: '--tail 20 --grep "error|warn|ready"' },
3385
+ { pattern: /^(?:npx\s+)?astro dev/, recallHint: '--tail 20 --grep "error|warn|ready"' },
3386
+ // Test watchers
3387
+ { pattern: /^(?:npx\s+)?vitest(?:\s+run|\s+watch)?/, recallHint: '--grep "FAIL|PASS|Error|\u2713|\u2717"' },
3388
+ { pattern: /^(?:npx\s+)?jest(?:\s+--watch)?/, recallHint: '--grep "FAIL|PASS|Error|Tests:"' },
3389
+ { pattern: /^pytest(?:\s|$)/, recallHint: '--grep "FAILED|PASSED|ERROR|passed|failed"' },
3390
+ { pattern: /^(?:cargo\s+test|cargo\s+watch)/, recallHint: '--grep "FAILED|ok|error\\["' },
3391
+ { pattern: /^go test/, recallHint: '--grep "FAIL|ok|---"' },
3392
+ // Docker / compose
3393
+ { pattern: /^docker(?:\s+compose)?\s+logs/, recallHint: '--tail 50 --grep "error|warn|Error|WARN"' },
3394
+ { pattern: /^docker-compose\s+logs/, recallHint: '--tail 50 --grep "error|warn|Error|WARN"' },
3395
+ // File watchers / hot-reload
3396
+ { pattern: /^nodemon/, recallHint: '--tail 20 --grep "error|crash|restart"' },
3397
+ { pattern: /^air(?:\s|$)/, recallHint: '--tail 20 --grep "error|build failed"' },
3398
+ { pattern: /^cargo watch/, recallHint: '--tail 20 --grep "error\\[|warning\\["' },
3399
+ { pattern: /^watchexec/, recallHint: '--tail 20 --grep "error|warn"' },
3400
+ // Linters / formatters run repeatedly
3401
+ { pattern: /^(?:npx\s+)?eslint(?:\s|$)/, recallHint: '--grep "error|warning|\u2716|problems"' },
3402
+ { pattern: /^(?:npx\s+)?prettier(?:\s|$)/, recallHint: '--grep "unchanged|reformatted|error"' },
3403
+ { pattern: /^npx\s+tsc(?:\s|$)/, recallHint: '--grep "error TS|Cannot find|Type "' },
3404
+ { pattern: /^ruff(?:\s|$)/, recallHint: '--grep "error|warning|Found"' },
3405
+ { pattern: /^(?:cargo\s+)?clippy/, recallHint: '--grep "error\\[|warning\\["' },
3406
+ // git diff (full diff output — can be very large; excludes --stat which is small)
3407
+ { pattern: /^git diff(?!\s+--stat)(?:\s+HEAD)?(?:\s|$)/, recallHint: '--grep "@@|\\+\\+\\+|---|diff --git"' },
3408
+ { pattern: /^git diff\s+--cached(?!\s+--stat)/, recallHint: '--grep "@@|\\+\\+\\+|---|diff --git"' },
3409
+ // 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
3410
+ { pattern: /^git push(?:\s|$)/, recallHint: '--tail 100 --grep "error|fail|FAIL|rejected|To "' },
3411
+ // npm run * wrappers (npm test is excluded — too generic; npm run test is explicit)
3412
+ { pattern: /^npm run (?:test|spec)(?:\s|$)/, recallHint: '--grep "FAIL|PASS|Error|Tests:|\u2713|\u2717"' },
3413
+ { pattern: /^npm run build(?:\s|$)/, recallHint: '--grep "error|Built|Failed|\u2713|\u2717"' },
3414
+ { pattern: /^npm run (?:lint|typecheck|check|type-check)(?:\s|$)/, recallHint: '--grep "error|warning|\u2716|problems"' },
3415
+ // node scripts (migration runners, seed generators, etc. run repeatedly)
3416
+ { pattern: /^node\s+(?:scripts|src\/scripts)\/\S+\.m?js\b/, recallHint: '--tail 50 --grep "error|Error|done|complete|inserted|migrated"' },
3417
+ // External AI peer-review CLI tools (produce large outputs, run repeatedly per session)
3418
+ { pattern: /^codex(?:\s|$)/, recallHint: '--tail 100 --grep "error|suggestion|verdict|conclusion"' },
3419
+ { pattern: /^(?:~\/\.claude\/bin\/|\.claude\/bin\/)?glm\.sh(?:\s|$)/, recallHint: '--tail 100 --grep "error|verdict|conclusion|suggestion"' },
3420
+ // cat of a single source file — output is the full file; pre-bash emits a token-goat read suggestion
3421
+ { 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" },
3422
+ // PowerShell read-only system-state queries (stable over 60-120s)
3423
+ {
3424
+ pattern: /^(?:powershell(?:\.exe)?|pwsh(?:\.exe)?)\s+(?:-\w+\s+)*-Command\s+["']?Get-(?:CimInstance|Process|Counter|Service|PSDrive|WmiObject)\b/i,
3425
+ recallHint: "--tail 50"
3426
+ },
3427
+ // token-goat section/outline/symbol repeat calls — output is stable until the file changes
3428
+ { pattern: /^(?:token-goat|tg)\s+section\s+["'][^"']+["']/, recallHint: "" },
3429
+ { pattern: /^(?:token-goat|tg)\s+outline\s+\S+/, recallHint: "" },
3430
+ { pattern: /^(?:token-goat|tg)\s+symbol\s+\S+/, recallHint: "" }
3431
+ ];
3432
+ function isPsMultilineSystemQuery(cmd) {
3433
+ if (!/^(?:powershell(?:\.exe)?|pwsh(?:\.exe)?)\s+/i.test(cmd)) return false;
3434
+ const cmdIdx = cmd.search(/-Command\b/i);
3435
+ if (cmdIdx === -1) return false;
3436
+ const afterCmd = cmd.slice(cmdIdx + "-Command".length).trimStart();
3437
+ if (!/^["']/.test(afterCmd)) return false;
3438
+ const body = afterCmd.slice(1);
3439
+ if (!body.includes("\n")) return false;
3440
+ if (!/\bGet-(?:CimInstance|Process|Counter|Service|PSDrive|WmiObject)\b/i.test(body)) return false;
3441
+ if (/\b(?:Remove|Set|New|Restart|Install|Uninstall|Enable|Disable|Grant|Revoke|Register|Unregister)-/i.test(body)) return false;
3442
+ if (/\b(?:Stop-(?:Process|Service|Computer)|Invoke-(?:Expression|Command)|Clear-(?:Content|EventLog|Item))\b/i.test(body)) return false;
3443
+ return true;
3444
+ }
3445
+ function getMonitoringRecallHint(cmd) {
3446
+ const trimmed = cmd.trim();
3447
+ if (hasUnquotedOperator(trimmed, ["&&", "||", ";"])) return null;
3448
+ for (const { pattern, recallHint } of MONITORING_COMMAND_PATTERNS) {
3449
+ if (pattern.test(trimmed)) return recallHint;
3450
+ }
3451
+ if (isPsMultilineSystemQuery(trimmed)) return "--tail 50";
3452
+ return null;
3453
+ }
3454
+
3455
+ // src/recall_index.ts
2958
3456
  init_define_import_meta_env();
2959
3457
  import * as fs7 from "node:fs";
3458
+ function subdirForCacheType(cacheType) {
3459
+ return cacheType === "web" ? "web_outputs" : "bash_outputs";
3460
+ }
3461
+ function blobStillExists(cacheType, id) {
3462
+ try {
3463
+ const p = blobPath(subdirForCacheType(cacheType), id);
3464
+ return p !== null && fs7.existsSync(p);
3465
+ } catch {
3466
+ return false;
3467
+ }
3468
+ }
3469
+ var VALID_TYPES = ["bash", "web", "mcp"];
3470
+ var RECALL_DEFAULT_LIMIT = 10;
3471
+ function isRecallCacheType(value) {
3472
+ return VALID_TYPES.includes(value);
3473
+ }
3474
+ function indexRecallEntry(cacheType, id, label, content, storedAt) {
3475
+ try {
3476
+ const db = getDb(globalDbPath());
3477
+ db.prepare(
3478
+ `INSERT INTO cache_recall (cache_type, entry_id, label, content, stored_at)
3479
+ VALUES (@cacheType, @id, @label, @content, @storedAt)
3480
+ ON CONFLICT(cache_type, entry_id) DO UPDATE SET
3481
+ label = excluded.label,
3482
+ content = excluded.content,
3483
+ stored_at = excluded.stored_at`
3484
+ ).run({ cacheType, id, label, content, storedAt });
3485
+ } catch {
3486
+ }
3487
+ pruneCacheRecallRows();
3488
+ }
3489
+ function pruneCacheRecallRows(maxAgeMs = DEFAULT_MAX_AGE_MS) {
3490
+ try {
3491
+ const db = getDb(globalDbPath());
3492
+ const cutoff = Date.now() - maxAgeMs;
3493
+ db.prepare(`DELETE FROM cache_recall WHERE stored_at < ?`).run(cutoff);
3494
+ } catch {
3495
+ }
3496
+ }
3497
+ var _ftsAvailable = null;
3498
+ function hasFtsTable() {
3499
+ if (_ftsAvailable !== null) return _ftsAvailable;
3500
+ try {
3501
+ const db = getDb(globalDbPath());
3502
+ const row = db.prepare(`SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'cache_recall_fts'`).get();
3503
+ _ftsAvailable = row !== void 0;
3504
+ } catch {
3505
+ _ftsAvailable = false;
3506
+ }
3507
+ return _ftsAvailable;
3508
+ }
3509
+ function buildSnippet(content, query, maxLen = 160) {
3510
+ const flat = content.replace(/\s+/g, " ").trim();
3511
+ const tokens = query.split(/\s+/).filter(Boolean);
3512
+ let idx = -1;
3513
+ const lowerFlat = flat.toLowerCase();
3514
+ for (const token of tokens) {
3515
+ const at = lowerFlat.indexOf(token.toLowerCase());
3516
+ if (at !== -1) {
3517
+ idx = at;
3518
+ break;
3519
+ }
3520
+ }
3521
+ if (idx === -1) {
3522
+ return flat.length > maxLen ? flat.slice(0, maxLen) + "..." : flat;
3523
+ }
3524
+ const start = Math.max(0, idx - Math.floor(maxLen / 3));
3525
+ const end = Math.min(flat.length, start + maxLen);
3526
+ const prefix = start > 0 ? "..." : "";
3527
+ const suffix = end < flat.length ? "..." : "";
3528
+ return prefix + flat.slice(start, end) + suffix;
3529
+ }
3530
+ function toFtsMatchExpr(query) {
3531
+ if (query.trim().length === 0) return null;
3532
+ return sanitizeFtsQuery(query);
3533
+ }
3534
+ function mapRowsToHits(rows, snippetQuery) {
3535
+ return rows.filter((r) => isRecallCacheType(r.cacheType)).map((r) => ({
3536
+ id: r.id,
3537
+ cacheType: r.cacheType,
3538
+ label: r.label ?? "",
3539
+ snippet: buildSnippet(r.content ?? "", snippetQuery),
3540
+ storedAt: r.storedAt ?? 0,
3541
+ blobPresent: blobStillExists(r.cacheType, r.id)
3542
+ }));
3543
+ }
3544
+ function ftsSearch(query, type, limit) {
3545
+ const matchExpr = toFtsMatchExpr(query);
3546
+ if (matchExpr === null) return [];
3547
+ const db = getDb(globalDbPath());
3548
+ const rows = type !== void 0 ? db.prepare(
3549
+ `SELECT c.entry_id AS id, c.cache_type AS cacheType, c.label AS label, c.content AS content, c.stored_at AS storedAt
3550
+ FROM cache_recall_fts
3551
+ JOIN cache_recall c ON c.row_id = cache_recall_fts.rowid
3552
+ WHERE cache_recall_fts MATCH ? AND c.cache_type = ?
3553
+ ORDER BY bm25(cache_recall_fts)
3554
+ LIMIT ?`
3555
+ ).all(matchExpr, type, limit) : db.prepare(
3556
+ `SELECT c.entry_id AS id, c.cache_type AS cacheType, c.label AS label, c.content AS content, c.stored_at AS storedAt
3557
+ FROM cache_recall_fts
3558
+ JOIN cache_recall c ON c.row_id = cache_recall_fts.rowid
3559
+ WHERE cache_recall_fts MATCH ?
3560
+ ORDER BY bm25(cache_recall_fts)
3561
+ LIMIT ?`
3562
+ ).all(matchExpr, limit);
3563
+ return mapRowsToHits(rows, query);
3564
+ }
3565
+ function likeSearch(query, type, limit) {
3566
+ const trimmed = query.trim();
3567
+ if (!trimmed) return [];
3568
+ const db = getDb(globalDbPath());
3569
+ const needle = `%${trimmed.replace(/\\/g, "\\\\").replace(/[%_]/g, (c) => `\\${c}`)}%`;
3570
+ const rows = type !== void 0 ? db.prepare(
3571
+ `SELECT entry_id AS id, cache_type AS cacheType, label, content, stored_at AS storedAt
3572
+ FROM cache_recall
3573
+ WHERE cache_type = ? AND (label LIKE ? ESCAPE '\\' OR content LIKE ? ESCAPE '\\')
3574
+ ORDER BY stored_at DESC
3575
+ LIMIT ?`
3576
+ ).all(type, needle, needle, limit) : db.prepare(
3577
+ `SELECT entry_id AS id, cache_type AS cacheType, label, content, stored_at AS storedAt
3578
+ FROM cache_recall
3579
+ WHERE label LIKE ? ESCAPE '\\' OR content LIKE ? ESCAPE '\\'
3580
+ ORDER BY stored_at DESC
3581
+ LIMIT ?`
3582
+ ).all(needle, needle, limit);
3583
+ return mapRowsToHits(rows, trimmed);
3584
+ }
3585
+ function listRecentRecall(opts = {}) {
3586
+ const limit = opts.limit ?? RECALL_DEFAULT_LIMIT;
3587
+ const columns = `SELECT entry_id AS id, cache_type AS cacheType, label, content, stored_at AS storedAt FROM cache_recall`;
3588
+ try {
3589
+ const db = getDb(globalDbPath());
3590
+ 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);
3591
+ return mapRowsToHits(rows, "");
3592
+ } catch {
3593
+ return [];
3594
+ }
3595
+ }
3596
+ function searchRecall(query, opts = {}) {
3597
+ const limit = opts.limit ?? RECALL_DEFAULT_LIMIT;
3598
+ if (query.trim() === "") return [];
3599
+ try {
3600
+ if (hasFtsTable()) {
3601
+ return ftsSearch(query, opts.type, limit);
3602
+ }
3603
+ return likeSearch(query, opts.type, limit);
3604
+ } catch {
3605
+ try {
3606
+ return likeSearch(query, opts.type, limit);
3607
+ } catch {
3608
+ return [];
3609
+ }
3610
+ }
3611
+ }
3612
+
3613
+ // src/bash_output_cache.ts
3614
+ var BASH_OUTPUT_SUBDIR = "bash_outputs";
3615
+ var _byId = /* @__PURE__ */ new Map();
3616
+ var COMMAND_PATTERNS = {
3617
+ gitMutable: /^\s*git\s+(diff|status)\b/i,
3618
+ gitImmutable: /^\s*git\s+show\s+[0-9a-f]{40}\b/i,
3619
+ gitDiffScoped: /\s--\s+\S/,
3620
+ dirListing: /^\s*(?:ls|eza|exa|dir|Get-ChildItem|gci)\b/i,
3621
+ 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,
3622
+ npmInstall: /^\s*npm\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=))*(?:install|ci)\b/i,
3623
+ npmAudit: /^\s*npm\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=))*audit\b(?!.*(?:--fix|fix)\b)/i,
3624
+ npmOutdated: /^\s*npm\s+(?:--?[a-zA-Z0-9_-]+(?:\s+|=))*outdated\b/i,
3625
+ 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,
3626
+ 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,
3627
+ gitPush: /^\s*git\s+push\b/i,
3628
+ testRunner: /^\s*(?:npx\s+)?(?:pytest|vitest|jest|go\s+test)\b/i,
3629
+ lintCommand: /^\s*(?:(?:npx\s+)?eslint|(?:uv\s+run\s+)?ruff)\b/i,
3630
+ npmRunScript: /^\s*npm\s+run(?:-script)?\b/i,
3631
+ catCommand: /^\s*cat\b/i
3632
+ };
3633
+ var DEP_LOCKFILES = {
3634
+ npm: ["package-lock.json", "yarn.lock"],
3635
+ pip: ["requirements.txt"],
3636
+ uv: ["uv.lock", "requirements.txt"],
3637
+ pnpm: ["pnpm-lock.yaml"],
3638
+ yarn: ["yarn.lock"],
3639
+ cargo: ["Cargo.lock"],
3640
+ bundle: ["Gemfile.lock"],
3641
+ composer: ["composer.lock"]
3642
+ };
3643
+ function isCommandOfType(cmd, type) {
3644
+ const pattern = COMMAND_PATTERNS[type];
3645
+ return pattern?.test(cmd) ?? false;
3646
+ }
3647
+ var isGitMutableCommand = (cmd) => isCommandOfType(cmd, "gitMutable");
3648
+ var isDirListingCommand = (cmd) => isCommandOfType(cmd, "dirListing");
3649
+ var isDepListCommand = (cmd) => isCommandOfType(cmd, "depList");
3650
+ var isNpmInstallCommand = (cmd) => isCommandOfType(cmd, "npmInstall");
3651
+ var isNpmAuditCommand = (cmd) => isCommandOfType(cmd, "npmAudit");
3652
+ var isNpmOutdatedCommand = (cmd) => isCommandOfType(cmd, "npmOutdated");
3653
+ var isGitPushCommand = (cmd) => isCommandOfType(cmd, "gitPush");
3654
+ var isTestRunnerCommand2 = (cmd) => isCommandOfType(cmd, "testRunner");
3655
+ var isLintCommand = (cmd) => isCommandOfType(cmd, "lintCommand");
3656
+ var isNpmRunScriptCommand = (cmd) => isCommandOfType(cmd, "npmRunScript");
3657
+ var isCatCommand = (cmd) => isCommandOfType(cmd, "catCommand");
3658
+ function isScopedGitStatusOrDiffStatCommand(cmd) {
3659
+ if (!isCommandOfType(cmd, "gitMutable")) return false;
3660
+ if (!isCommandOfType(cmd, "gitDiffScoped")) return false;
3661
+ if (/^\s*git\s+diff\b/i.test(cmd) && !/--stat\b/.test(cmd)) return false;
3662
+ return true;
3663
+ }
3664
+ function gitStateFingerprintSync(cwd) {
3665
+ try {
3666
+ const headResult = runGit(["rev-parse", "HEAD"], { cwd });
3667
+ if (headResult.exitCode !== 0) return null;
3668
+ const headSha = headResult.stdout.trim();
3669
+ let statusHash = "";
3670
+ const statusResult = runGit(["status", "--porcelain"], { cwd });
3671
+ if (statusResult.exitCode === 0) {
3672
+ statusHash = shortFingerprint(statusResult.stdout);
3673
+ }
3674
+ const key = `${headSha}\0${statusHash}`;
3675
+ return shortFingerprint(key);
3676
+ } catch {
3677
+ return null;
3678
+ }
3679
+ }
3680
+ var DIR_FINGERPRINT_LISTING_CAP_ENTRIES = 1e4;
3681
+ function dirStateFingerprintSync(path12) {
3682
+ try {
3683
+ const stat = statSync3(path12);
3684
+ if (!stat.isDirectory()) return null;
3685
+ const entries = readdirSync2(path12);
3686
+ if (entries.length <= DIR_FINGERPRINT_LISTING_CAP_ENTRIES) {
3687
+ return shortFingerprint(entries.slice().sort().join("\0"));
3688
+ }
3689
+ return shortFingerprint(stat.mtimeMs.toString());
3690
+ } catch {
3691
+ return null;
3692
+ }
3693
+ }
3694
+ var FILE_FINGERPRINT_CONTENT_CAP_BYTES = 2 * 1024 * 1024;
3695
+ function fileStateFingerprintSync(path12) {
3696
+ try {
3697
+ const stat = statSync3(path12);
3698
+ if (!stat.isFile()) return null;
3699
+ if (stat.size <= FILE_FINGERPRINT_CONTENT_CAP_BYTES) {
3700
+ return shortFingerprint(readFileSync5(path12));
3701
+ }
3702
+ return shortFingerprint(`${stat.mtimeMs}\0${stat.size}`);
3703
+ } catch {
3704
+ return null;
3705
+ }
3706
+ }
3707
+ function depLockfileFingerprintSync(cmd, cwd) {
3708
+ if (!cwd) return null;
3709
+ const stripped = cmd.trim();
3710
+ const firstToken = stripped.split(/\s+/)[0]?.toLowerCase() || "";
3711
+ if (!firstToken) return null;
3712
+ const candidates = firstToken === "uv" ? DEP_LOCKFILES["uv"] : DEP_LOCKFILES[firstToken];
3713
+ if (!candidates) return null;
3714
+ for (const lockfile of candidates) {
3715
+ try {
3716
+ const content = readFileSync5(resolve4(cwd, lockfile));
3717
+ return shortFingerprint(content);
3718
+ } catch {
3719
+ continue;
3720
+ }
3721
+ }
3722
+ return null;
3723
+ }
3724
+ function normalizeCommandForCacheKey(cmd) {
3725
+ const trimmed = cmd.trim();
3726
+ const tokens = [];
3727
+ let current = "";
3728
+ let quoteChar = null;
3729
+ const flush = () => {
3730
+ if (current.length > 0) {
3731
+ tokens.push(current);
3732
+ current = "";
3733
+ }
3734
+ };
3735
+ for (let i = 0; i < trimmed.length; i++) {
3736
+ const ch = trimmed[i];
3737
+ if (quoteChar !== null) {
3738
+ current += ch;
3739
+ if (ch === quoteChar) quoteChar = null;
3740
+ continue;
3741
+ }
3742
+ if (ch === '"' || ch === "'") {
3743
+ quoteChar = ch;
3744
+ current += ch;
3745
+ continue;
3746
+ }
3747
+ if (/\s/.test(ch)) {
3748
+ flush();
3749
+ continue;
3750
+ }
3751
+ current += ch === "\\" ? "/" : ch;
3752
+ }
3753
+ flush();
3754
+ const normalized_tokens = tokens.map((token) => {
3755
+ if (token.startsWith("-") || ["&&", "||", "|", ">", ">>", ";", "&"].includes(token)) {
3756
+ return token;
3757
+ }
3758
+ if (token.startsWith("./") && !token.startsWith("../")) {
3759
+ token = token.slice(2);
3760
+ }
3761
+ if (token.endsWith("/") && token !== "/") {
3762
+ token = token.slice(0, -1);
3763
+ }
3764
+ return token || ".";
3765
+ });
3766
+ return normalized_tokens.join(" ");
3767
+ }
3768
+ function commandHashSync(command, cwd = null) {
3769
+ const normalized = normalizeCommandForCacheKey(command);
3770
+ let key = cwd ? `${normalizePath(cwd)}\0${normalized}` : normalized;
3771
+ if (cwd && isGitMutableCommand(command)) {
3772
+ const fp = gitStateFingerprintSync(cwd);
3773
+ if (fp) key = `${key}\0git:${fp}`;
3774
+ }
3775
+ if (cwd && isDirListingCommand(command)) {
3776
+ const target = extractFirstPathArg(command, cwd, cwd);
3777
+ if (target) {
3778
+ const fp = dirStateFingerprintSync(target);
3779
+ if (fp) key = `${key}\0dir:${fp}`;
3780
+ }
3781
+ }
3782
+ if (isDepListCommand(command)) {
3783
+ const fp = depLockfileFingerprintSync(command, cwd);
3784
+ if (fp) key = `${key}\0lockfile:${fp}`;
3785
+ }
3786
+ if (cwd && (isNpmInstallCommand(command) || isNpmAuditCommand(command) || isNpmOutdatedCommand(command))) {
3787
+ const fp = depLockfileFingerprintSync(command, cwd);
3788
+ if (fp) key = `${key}\0npm-install:${fp}`;
3789
+ }
3790
+ return shortFingerprint(key);
3791
+ }
3792
+ function commandHash(command, cwd = null) {
3793
+ return Promise.resolve(commandHashSync(command, cwd));
3794
+ }
3795
+ function computeBashFingerprints(command, cwd) {
3796
+ const fingerprints = {};
3797
+ if (cwd && (isGitMutableCommand(command) || isGitPushCommand(command) || isTestRunnerCommand2(command) || isLintCommand(command) || isBuildCommand(command) || isNpmRunScriptCommand(command))) {
3798
+ const fp = gitStateFingerprintSync(cwd);
3799
+ if (fp) fingerprints.git = fp;
3800
+ }
3801
+ if (cwd && isDirListingCommand(command)) {
3802
+ const target = extractFirstPathArg(command, cwd, cwd);
3803
+ if (target) {
3804
+ const fp = dirStateFingerprintSync(target);
3805
+ if (fp) fingerprints.dir = fp;
3806
+ }
3807
+ }
3808
+ if (isDepListCommand(command) || cwd && (isNpmInstallCommand(command) || isNpmAuditCommand(command) || isNpmOutdatedCommand(command))) {
3809
+ const fp = depLockfileFingerprintSync(command, cwd);
3810
+ if (fp) fingerprints.lockfile = fp;
3811
+ }
3812
+ if (cwd && isCatCommand(command)) {
3813
+ const target = extractFirstPathArg(command, cwd, null);
3814
+ if (target) {
3815
+ const fp = fileStateFingerprintSync(target);
3816
+ if (fp) fingerprints.file = fp;
3817
+ }
3818
+ }
3819
+ return Object.keys(fingerprints).length > 0 ? fingerprints : void 0;
3820
+ }
3821
+ function isBashEntryStale(entry, command, cwd) {
3822
+ const stored = entry.fingerprints;
3823
+ if (!stored) return false;
3824
+ const current = computeBashFingerprints(command, cwd);
3825
+ if (stored.git !== void 0 && stored.git !== current?.git) return true;
3826
+ if (stored.dir !== void 0 && stored.dir !== current?.dir) return true;
3827
+ if (stored.lockfile !== void 0 && stored.lockfile !== current?.lockfile) return true;
3828
+ if (stored.file !== void 0 && stored.file !== current?.file) return true;
3829
+ return false;
3830
+ }
3831
+ function tokenizeShellArgs(cmd) {
3832
+ const trimmed = cmd.trim();
3833
+ const tokens = [];
3834
+ let current = "";
3835
+ let quoteChar = null;
3836
+ let hasToken = false;
3837
+ for (let i = 0; i < trimmed.length; i++) {
3838
+ const ch = trimmed[i];
3839
+ if (quoteChar !== null) {
3840
+ if (ch === quoteChar) {
3841
+ quoteChar = null;
3842
+ } else {
3843
+ current += ch;
3844
+ }
3845
+ continue;
3846
+ }
3847
+ if (ch === '"' || ch === "'") {
3848
+ quoteChar = ch;
3849
+ hasToken = true;
3850
+ continue;
3851
+ }
3852
+ if (/\s/.test(ch)) {
3853
+ if (hasToken) {
3854
+ tokens.push(current);
3855
+ current = "";
3856
+ hasToken = false;
3857
+ }
3858
+ continue;
3859
+ }
3860
+ current += ch;
3861
+ hasToken = true;
3862
+ }
3863
+ if (hasToken) tokens.push(current);
3864
+ return tokens;
3865
+ }
3866
+ function extractFirstPathArg(cmd, cwd, fallback) {
3867
+ const tokens = tokenizeShellArgs(cmd);
3868
+ for (let i = 1; i < tokens.length; i++) {
3869
+ const token = tokens[i];
3870
+ if (!token.startsWith("-")) {
3871
+ if (!token.startsWith("/")) {
3872
+ return resolve4(cwd, token);
3873
+ }
3874
+ return token;
3875
+ }
3876
+ }
3877
+ return fallback;
3878
+ }
3879
+ var ISSUE_LINE_PATTERN = /\b(?:error|fail(?:ed|ure)?|warning)\b/i;
3880
+ function summarizeOutputDelta(oldOutput, newOutput) {
3881
+ if (oldOutput === newOutput) return null;
3882
+ const oldLines = oldOutput.split("\n");
3883
+ const newLines = newOutput.split("\n");
3884
+ const oldIssueLines = oldLines.filter((l) => ISSUE_LINE_PATTERN.test(l));
3885
+ if (oldIssueLines.length > 0) {
3886
+ const newIssueLines = newLines.filter((l) => ISSUE_LINE_PATTERN.test(l));
3887
+ const priorTotal = oldIssueLines.length;
3888
+ const availableCounts = /* @__PURE__ */ new Map();
3889
+ for (const l of newIssueLines) availableCounts.set(l, (availableCounts.get(l) ?? 0) + 1);
3890
+ let resolved = 0;
3891
+ for (const l of oldIssueLines) {
3892
+ const avail = availableCounts.get(l) ?? 0;
3893
+ if (avail > 0) {
3894
+ availableCounts.set(l, avail - 1);
3895
+ } else {
3896
+ resolved++;
3897
+ }
3898
+ }
3899
+ const remaining = newIssueLines.length;
3900
+ return `[token-goat: delta] ${resolved} of ${priorTotal} prior issues resolved; remaining: ${remaining}`;
3901
+ }
3902
+ return `[token-goat: delta] output changed: ${oldLines.length} -> ${newLines.length} lines`;
3903
+ }
3904
+ function storeBashOutputSync(command, output, exitCode, cwd = null) {
3905
+ const id = commandHashSync(command, cwd);
3906
+ const fingerprints = computeBashFingerprints(command, cwd);
3907
+ const redactedOutput = redactSecrets(output).text;
3908
+ const redactedCommand = redactSecrets(command).text;
3909
+ const entry = {
3910
+ id,
3911
+ command: redactedCommand,
3912
+ output: redactedOutput,
3913
+ exitCode,
3914
+ storedAt: Date.now(),
3915
+ sizeBytes: Buffer.byteLength(redactedOutput, "utf-8"),
3916
+ ...fingerprints ? { fingerprints } : {}
3917
+ };
3918
+ _byId.set(id, entry);
3919
+ storeBlob(BASH_OUTPUT_SUBDIR, id, entry);
3920
+ indexRecallEntry("bash", id, redactedCommand, `${redactedCommand}
3921
+ ${redactedOutput}`, entry.storedAt);
3922
+ return id;
3923
+ }
3924
+ function storeBashOutput(command, output, exitCode, cwd = null) {
3925
+ return Promise.resolve(storeBashOutputSync(command, output, exitCode, cwd));
3926
+ }
3927
+ function coerceBashEntry(raw) {
3928
+ if (raw === null || typeof raw !== "object") return null;
3929
+ const o = raw;
3930
+ 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") {
3931
+ return null;
3932
+ }
3933
+ const entry = {
3934
+ id: o["id"],
3935
+ command: o["command"],
3936
+ output: o["output"],
3937
+ exitCode: o["exitCode"],
3938
+ storedAt: o["storedAt"],
3939
+ sizeBytes: o["sizeBytes"]
3940
+ };
3941
+ const rawFingerprints = o["fingerprints"];
3942
+ if (rawFingerprints !== null && typeof rawFingerprints === "object") {
3943
+ const f = rawFingerprints;
3944
+ const fingerprints = {};
3945
+ if (typeof f["git"] === "string") fingerprints.git = f["git"];
3946
+ if (typeof f["dir"] === "string") fingerprints.dir = f["dir"];
3947
+ if (typeof f["lockfile"] === "string") fingerprints.lockfile = f["lockfile"];
3948
+ if (typeof f["file"] === "string") fingerprints.file = f["file"];
3949
+ if (Object.keys(fingerprints).length > 0) return { ...entry, fingerprints };
3950
+ }
3951
+ return entry;
3952
+ }
3953
+ function getBashOutput(id) {
3954
+ const hit = _byId.get(id);
3955
+ if (hit !== void 0) return hit;
3956
+ if (isBlobStale(BASH_OUTPUT_SUBDIR, id)) return null;
3957
+ const entry = coerceBashEntry(loadBlob(BASH_OUTPUT_SUBDIR, id));
3958
+ if (entry === null) return null;
3959
+ _byId.set(id, entry);
3960
+ return entry;
3961
+ }
3962
+ registerReset(() => {
3963
+ _byId = /* @__PURE__ */ new Map();
3964
+ });
3965
+
3966
+ // src/install.ts
3967
+ init_define_import_meta_env();
3968
+ import * as fs8 from "node:fs";
2960
3969
  import * as os4 from "node:os";
2961
3970
  import * as path7 from "node:path";
2962
3971
 
@@ -3140,7 +4149,7 @@ var SettingsParseError = class extends Error {
3140
4149
  function readSettings(p, opts = {}) {
3141
4150
  let raw;
3142
4151
  try {
3143
- raw = fs7.readFileSync(p, "utf8");
4152
+ raw = fs8.readFileSync(p, "utf8");
3144
4153
  } catch {
3145
4154
  return {};
3146
4155
  }
@@ -3272,7 +4281,7 @@ function isInstalled(scope = "user") {
3272
4281
  const hooks = settings.hooks;
3273
4282
  if (hooks === void 0) return false;
3274
4283
  const scriptPath = claudeHookScriptPath();
3275
- if (!fs7.existsSync(scriptPath)) return false;
4284
+ if (!fs8.existsSync(scriptPath)) return false;
3276
4285
  for (const [eventKey, eventArg] of HOOK_EVENT_MAP) {
3277
4286
  const expectedCommand = hookCommandFor(scriptPath, eventArg);
3278
4287
  if (!groupHasTokenGoat(hooks[eventKey], (c) => c === expectedCommand)) return false;
@@ -3310,7 +4319,7 @@ function findStrayClaudeMdBlocks(searchRoot) {
3310
4319
  const root = searchRoot ?? path7.join(os4.homedir(), ".claude");
3311
4320
  const canonical = path7.resolve(claudeMdPath());
3312
4321
  const found = [];
3313
- if (!fs7.existsSync(root)) return found;
4322
+ if (!fs8.existsSync(root)) return found;
3314
4323
  const SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git"]);
3315
4324
  const MAX_DEPTH = 6;
3316
4325
  const hasRealBlock = (text) => {
@@ -3326,7 +4335,7 @@ function findStrayClaudeMdBlocks(searchRoot) {
3326
4335
  if (depth > MAX_DEPTH) return;
3327
4336
  let entries;
3328
4337
  try {
3329
- entries = fs7.readdirSync(dir, { withFileTypes: true });
4338
+ entries = fs8.readdirSync(dir, { withFileTypes: true });
3330
4339
  } catch {
3331
4340
  return;
3332
4341
  }
@@ -3340,7 +4349,7 @@ function findStrayClaudeMdBlocks(searchRoot) {
3340
4349
  if (path7.resolve(full) === canonical) continue;
3341
4350
  let text;
3342
4351
  try {
3343
- text = fs7.readFileSync(full, "utf8");
4352
+ text = fs8.readFileSync(full, "utf8");
3344
4353
  } catch {
3345
4354
  continue;
3346
4355
  }
@@ -3378,7 +4387,7 @@ function installSkill() {
3378
4387
  const p = skillPath();
3379
4388
  let existing = null;
3380
4389
  try {
3381
- existing = fs7.readFileSync(p, "utf8");
4390
+ existing = fs8.readFileSync(p, "utf8");
3382
4391
  } catch {
3383
4392
  }
3384
4393
  const skillContent = skillMdContent();
@@ -3390,17 +4399,17 @@ function installSkill() {
3390
4399
  }
3391
4400
  function uninstallSkill() {
3392
4401
  const dir = skillDir();
3393
- if (!fs7.existsSync(dir)) return false;
4402
+ if (!fs8.existsSync(dir)) return false;
3394
4403
  removeCreatedBackups(skillPath());
3395
4404
  assertWriteInScope(dir);
3396
- fs7.rmSync(dir, { recursive: true, force: true });
4405
+ fs8.rmSync(dir, { recursive: true, force: true });
3397
4406
  return true;
3398
4407
  }
3399
4408
 
3400
4409
  // src/bridges/codex_install.ts
3401
4410
  init_define_import_meta_env();
3402
4411
  import * as crypto2 from "node:crypto";
3403
- import * as fs8 from "node:fs";
4412
+ import * as fs9 from "node:fs";
3404
4413
  import * as os5 from "node:os";
3405
4414
  import * as path8 from "node:path";
3406
4415
 
@@ -3567,7 +4576,7 @@ function codexHookScriptPath() {
3567
4576
  function readCodexConfig(p, opts = {}) {
3568
4577
  let raw;
3569
4578
  try {
3570
- raw = fs8.readFileSync(p, "utf8");
4579
+ raw = fs9.readFileSync(p, "utf8");
3571
4580
  } catch {
3572
4581
  return {};
3573
4582
  }
@@ -3773,7 +4782,7 @@ function uninstallCodex() {
3773
4782
  removedAny = true;
3774
4783
  }
3775
4784
  try {
3776
- fs8.unlinkSync(scriptPath);
4785
+ fs9.unlinkSync(scriptPath);
3777
4786
  removedAny = true;
3778
4787
  } catch {
3779
4788
  }
@@ -3809,10 +4818,10 @@ function isCodexInstalled() {
3809
4818
  const expectedHash = computeCodexHookHash(eventArg, expectedCommand);
3810
4819
  if (hooksState[stateKey]?.trusted_hash !== expectedHash) return false;
3811
4820
  }
3812
- if (!fs8.existsSync(scriptPath)) return false;
4821
+ if (!fs9.existsSync(scriptPath)) return false;
3813
4822
  let agents;
3814
4823
  try {
3815
- agents = fs8.readFileSync(codexAgentsPath(), "utf8");
4824
+ agents = fs9.readFileSync(codexAgentsPath(), "utf8");
3816
4825
  } catch {
3817
4826
  return false;
3818
4827
  }
@@ -3837,7 +4846,7 @@ function stripAgentsBlock(p) {
3837
4846
 
3838
4847
  // src/bridges/kimi_install.ts
3839
4848
  init_define_import_meta_env();
3840
- import * as fs9 from "node:fs";
4849
+ import * as fs10 from "node:fs";
3841
4850
  import * as os6 from "node:os";
3842
4851
  import * as path9 from "node:path";
3843
4852
 
@@ -3967,7 +4976,7 @@ function kimiSkillPath() {
3967
4976
  function readKimiConfig(p, opts = {}) {
3968
4977
  let raw;
3969
4978
  try {
3970
- raw = fs9.readFileSync(p, "utf8");
4979
+ raw = fs10.readFileSync(p, "utf8");
3971
4980
  } catch {
3972
4981
  return {};
3973
4982
  }
@@ -4070,16 +5079,16 @@ function uninstallKimi() {
4070
5079
  }
4071
5080
  if (stripKimiAgentsBlock(kimiAgentsPath())) removed = true;
4072
5081
  try {
4073
- if (fs9.existsSync(kimiSkillDir())) {
5082
+ if (fs10.existsSync(kimiSkillDir())) {
4074
5083
  removeCreatedBackups(kimiSkillPath());
4075
- fs9.rmSync(kimiSkillDir(), { recursive: true, force: true });
5084
+ fs10.rmSync(kimiSkillDir(), { recursive: true, force: true });
4076
5085
  removed = true;
4077
5086
  }
4078
5087
  } catch {
4079
5088
  }
4080
5089
  try {
4081
- if (fs9.existsSync(kimiHookScriptPath())) {
4082
- fs9.rmSync(kimiHookScriptPath(), { force: true });
5090
+ if (fs10.existsSync(kimiHookScriptPath())) {
5091
+ fs10.rmSync(kimiHookScriptPath(), { force: true });
4083
5092
  removed = true;
4084
5093
  }
4085
5094
  } catch {
@@ -4100,7 +5109,7 @@ function isKimiInstalled() {
4100
5109
 
4101
5110
  // src/bridges/grok_install.ts
4102
5111
  init_define_import_meta_env();
4103
- import * as fs10 from "node:fs";
5112
+ import * as fs11 from "node:fs";
4104
5113
  import * as os7 from "node:os";
4105
5114
  import * as path10 from "node:path";
4106
5115
 
@@ -4244,12 +5253,12 @@ function uninstallGrok() {
4244
5253
  const scriptPath = grokHookScriptPath();
4245
5254
  let removedAny = false;
4246
5255
  try {
4247
- fs10.unlinkSync(configPath);
5256
+ fs11.unlinkSync(configPath);
4248
5257
  removedAny = true;
4249
5258
  } catch {
4250
5259
  }
4251
5260
  try {
4252
- fs10.unlinkSync(scriptPath);
5261
+ fs11.unlinkSync(scriptPath);
4253
5262
  removedAny = true;
4254
5263
  } catch {
4255
5264
  }
@@ -4257,12 +5266,12 @@ function uninstallGrok() {
4257
5266
  return removedAny;
4258
5267
  }
4259
5268
  function isGrokInstalled() {
4260
- return fs10.existsSync(grokConfigPath()) && fs10.existsSync(grokHookScriptPath());
5269
+ return fs11.existsSync(grokConfigPath()) && fs11.existsSync(grokHookScriptPath());
4261
5270
  }
4262
5271
 
4263
5272
  // src/bridges/vscode_install.ts
4264
5273
  init_define_import_meta_env();
4265
- import * as fs11 from "node:fs";
5274
+ import * as fs12 from "node:fs";
4266
5275
  import * as os8 from "node:os";
4267
5276
  import * as path11 from "node:path";
4268
5277
  var VSCODE_GUIDANCE_BEGIN = "<!-- token-goat-vscode-begin -->";
@@ -4311,7 +5320,7 @@ function vscodeUserSettingsPath() {
4311
5320
  function vscodeUsesClaudeHooks(settingsPath2 = vscodeUserSettingsPath()) {
4312
5321
  let text;
4313
5322
  try {
4314
- text = fs11.readFileSync(settingsPath2, "utf8");
5323
+ text = fs12.readFileSync(settingsPath2, "utf8");
4315
5324
  } catch {
4316
5325
  return false;
4317
5326
  }
@@ -4326,7 +5335,7 @@ var updateConfig = setTokenGoatServer;
4326
5335
  function otherScopeHasManagedServer(opts) {
4327
5336
  const otherPath = otherScopeMcpPath2(opts);
4328
5337
  if (opts.project !== true && !projectPathIsConsultable(otherPath, path11.resolve(opts.projectRoot ?? process.cwd()))) return false;
4329
- if (!fs11.existsSync(otherPath)) return false;
5338
+ if (!fs12.existsSync(otherPath)) return false;
4330
5339
  try {
4331
5340
  const config = readConfig(otherPath);
4332
5341
  const servers = config.value["servers"];
@@ -4343,7 +5352,7 @@ function vscodeDecoderConfigured(opts = {}) {
4343
5352
  if (projectPathIsConsultable(projectPath, path11.resolve(opts.projectRoot))) checkedPaths.push(projectPath);
4344
5353
  }
4345
5354
  for (const candidate of checkedPaths) {
4346
- if (!fs11.existsSync(candidate)) continue;
5355
+ if (!fs12.existsSync(candidate)) continue;
4347
5356
  try {
4348
5357
  const config = readConfig(candidate);
4349
5358
  const servers = config.value["servers"];
@@ -4358,7 +5367,7 @@ function vscodeDecoderConfigured(opts = {}) {
4358
5367
  }
4359
5368
  function writeGuidance2(filePath, userScope) {
4360
5369
  let created = false;
4361
- if (userScope && !fs11.existsSync(filePath)) {
5370
+ if (userScope && !fs12.existsSync(filePath)) {
4362
5371
  ensureDirSync(path11.dirname(filePath));
4363
5372
  atomicWriteText(filePath, USER_INSTRUCTIONS_FRONTMATTER);
4364
5373
  created = true;
@@ -4409,7 +5418,7 @@ function installVscodeScoped(opts) {
4409
5418
  throw new Error(`VS Code MCP JSON at ${mcpPath} already has a non-token-goat-managed server named "token-goat"`);
4410
5419
  }
4411
5420
  const next = updateConfig(config.text, managedServer());
4412
- const mcpExisted = fs11.existsSync(mcpPath);
5421
+ const mcpExisted = fs12.existsSync(mcpPath);
4413
5422
  ensureDirSync(path11.dirname(mcpPath));
4414
5423
  if (config.text !== next) {
4415
5424
  backupFile(mcpPath);
@@ -4435,7 +5444,7 @@ function uninstallVscodeScoped(opts) {
4435
5444
  assertProjectTargetsAreInTheProject(opts);
4436
5445
  const mcpPath = vscodeMcpPath(opts);
4437
5446
  let removed = false;
4438
- if (fs11.existsSync(mcpPath)) {
5447
+ if (fs12.existsSync(mcpPath)) {
4439
5448
  const config = readConfig(mcpPath);
4440
5449
  const servers = config.value["servers"];
4441
5450
  if (servers !== void 0 && (servers === null || typeof servers !== "object" || Array.isArray(servers))) {
@@ -4455,7 +5464,7 @@ function uninstallVscodeScoped(opts) {
4455
5464
  const instructionsPath = vscodeInstructionsPath(opts);
4456
5465
  if (stripDelimitedBlock(instructionsPath, BEGIN, END, opts.keepBackups === true)) {
4457
5466
  removed = true;
4458
- if (opts.project !== true && fs11.readFileSync(instructionsPath, "utf8").trim() === USER_INSTRUCTIONS_FRONTMATTER.trim()) removeFileInScope(instructionsPath);
5467
+ if (opts.project !== true && fs12.readFileSync(instructionsPath, "utf8").trim() === USER_INSTRUCTIONS_FRONTMATTER.trim()) removeFileInScope(instructionsPath);
4459
5468
  }
4460
5469
  if (opts.project === true) syncVisualStudioProjectGuidance(instructionsPath);
4461
5470
  if (releaseCopilotHooksFile(vscodeHooksDir(opts), "vscode", opts.keepBackups === true)) removed = true;
@@ -5318,25 +6327,25 @@ function makeLanguageFilter(cfg) {
5318
6327
  // src/tool_filters/ai_clis.ts
5319
6328
  var _GH_COPILOT_SPINNER_RE = /^\s*(?:Asking GitHub Copilot|Generating|Thinking|Fetching)\s*(?:\.{1,3}\s*)?$/i;
5320
6329
  var _GH_COPILOT_BANNER_RE = /^\s*(?:Welcome to GitHub Copilot|Using GitHub Copilot|Authenticated as|GitHub Copilot\s+v\d+)/i;
5321
- var _GH_COPILOT_DISCLAIMER_RE = /^\s*(?:Disclaimer:|This response was|GitHub Copilot|The commands?\s+(?:above|below)|Please review|Always review|Remember to|Note:|Tip:)/i;
6330
+ var _GH_COPILOT_DISCLAIMER_RE = /^\s*(?:Disclaimer:|This response was|GitHub Copilot|The commands?\s+(?:above|below)|Please review|Always review|Remember to)/i;
5322
6331
  var _AIDER_APPLYING_RE = /^\s*(?:Applying\s+edits?(?:\s+to\s+\S+)?|Applied\s+edit\s+to\s+\S+)\s*(?:\.{1,3}\s*)?$/i;
5323
6332
  var _AIDER_TOKENS_RE = /^\s*Tokens:\s+\d[\d,]*\s+sent,\s+\d[\d,]*\s+received/i;
5324
6333
  var _AIDER_COST_RE = /^\s*Cost:\s+\$[\d.]+\s+message,\s+\$[\d.]+\s+session/i;
5325
6334
  var _AIDER_REPOMAP_RE = /^\s*(?:Repo-map:|Added\s+\S+\s+to\s+the\s+chat|Removed\s+\S+\s+from\s+the\s+chat|Loading\s+repo\s+map|Updating\s+repo\s+map|Scanning\s+repo\s+contents|Using\s+\d+\s+tokens\s+of\s+repo\s+map)/i;
5326
6335
  var _AIDER_BANNER_RE = /^\s*aider\s+v\d+\.\d+/i;
5327
- var _AIDER_FOOTER_NOISE_RE = /^\s*(?:Use\s+ctrl-c|Run\s+with\s+--help|You\s+can\s+skip\s+this|Tip:|Note:)/i;
6336
+ var _AIDER_FOOTER_NOISE_RE = /^\s*(?:Use\s+ctrl-c|Run\s+with\s+--help|You\s+can\s+skip\s+this)/i;
5328
6337
  var _COPILOT_WORKSPACE_NOISE_RE = /^\s*(?:Starting\s+Copilot\s+workspace|Loading\s+model:|Copilot\s+workspace\s+(?:starting|loaded|ready)|Streaming\.\.\.|▌\s*$|Turn\s+\d+\s*:)/i;
5329
6338
  var _COPILOT_COMPLETION_STATS_RE = /^\s*(?:Completion\s+tokens:|Prompt\s+tokens:|Total\s+tokens:|Input\s+tokens:|Output\s+tokens:)\s*\d/i;
5330
6339
  var _GEMINI_STARTUP_RE = /^\s*(?:[✓✗►]|>)\s*(?:Model:|Theme:|Tools:|Sandbox:|Checkpointing:|Context(?:\s+limit)?:|Version:|Authenticated(?:\s+as)?:|Connecting)/i;
5331
6340
  var _GEMINI_BANNER_RE = /^\s*Gemini\s+CLI\s+v\d+/i;
5332
6341
  var _GEMINI_TOKEN_METER_RE = /^\s*(?:Token\s+usage|Context|Tokens):\s+[\d,]+\s*\/\s*[\d,]+/i;
5333
6342
  var _GEMINI_TOOL_SPINNER_RE = /^\s*[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏✓✗►✦]\s+(?:Call(?:ing|ed)|Execut(?:ing|ed)|Running)\s+\S+/;
5334
- var _GEMINI_FOOTER_RE = /^\s*(?:Type\s+\/help|Press\s+Ctrl|Use\s+Ctrl|Tip:|Note:)/i;
6343
+ var _GEMINI_FOOTER_RE = /^\s*(?:Type\s+\/help|Press\s+Ctrl|Use\s+Ctrl)/i;
5335
6344
  var _GEMINI_THINKING_RE = /^\s*(?:Thinking|Generating|Processing)\s*(?:\.{1,3}\s*)?$/i;
5336
6345
  var _CLAUDE_CLI_MODEL_HDR_RE = /^\s*[◆◇►✦]\s+claude-/i;
5337
6346
  var _CLAUDE_CLI_STATS_RE = /^\s*[↑↓⇑⇓]\s*\d[\d,]*(?:(?:\s*[↑↓⇑⇓]\s*|\s+)\d[\d,]*)?\s*tokens/i;
5338
6347
  var _CLAUDE_CLI_CONTEXT_RE = /^\s*(?:Context(?:\s+window)?|Token\s+limit):\s+[\d,]+\s*\/\s*[\d,]+/i;
5339
- var _CLAUDE_CLI_FOOTER_RE = /^\s*(?:Press\s+Ctrl|Enter\s+\/|Type\s+\/|Use\s+Ctrl|Tip:|Note:)/i;
6348
+ var _CLAUDE_CLI_FOOTER_RE = /^\s*(?:Press\s+Ctrl|Enter\s+\/|Type\s+\/|Use\s+Ctrl)/i;
5340
6349
  var _CLAUDE_CLI_SPINNER_RE = /^\s*[◎⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏]\s+(?:Thinking|Generating|Processing|Running)\s*(?:\.{1,3}\s*)?$/;
5341
6350
  var _CLAUDE_CLI_TOOL_LOG_RE = /^\s*(?:>\s+Using\s+tool:|✓\s+Tool\s+result:|◎\s+Tool:)/i;
5342
6351
  var _CLAUDE_CLI_SKIP_SUBCMDS = /* @__PURE__ */ new Set(["install", "update", "doctor", "config", "login", "logout"]);
@@ -8315,7 +9324,8 @@ ${stderr.replace(/\s+$/, "")}` : stderr;
8315
9324
  var awsFilter = new AwsFilter();
8316
9325
  var _AWS_UPLOAD_RE = /^upload:\s+\S+\s+to\s+s3:\/\//i;
8317
9326
  var _AWS_DOWNLOAD_RE = /^download:\s+s3:\/\//i;
8318
- var _AWS_S3_PROGRESS_RE = /^(?:Completed\s+\d|\d+(?:\.\d+)?\s*(?:KiB|MiB|GiB|B)\/s|Calculating|upload\s+failed:|download\s+failed:)/i;
9327
+ var _AWS_S3_TRANSFER_FAILED_RE = /^(?:upload|download|copy|delete|move)\s+failed:/i;
9328
+ var _AWS_S3_PROGRESS_RE = /^(?:Completed\s+\d|\d+(?:\.\d+)?\s*(?:KiB|MiB|GiB|B)\/s|Calculating)/i;
8319
9329
  var AWS_GLOBAL_VALUE_FLAGS = /* @__PURE__ */ new Set([
8320
9330
  "--profile",
8321
9331
  "--region",
@@ -8337,7 +9347,8 @@ var AwsCliFilter = class extends ToolFilter {
8337
9347
  _JSON_ARRAY_KEEP = 3;
8338
9348
  compressBody(stdout, stderr, _exitCode, argv) {
8339
9349
  const positionals = positionalArgs(argv.slice(1), AWS_GLOBAL_VALUE_FLAGS);
8340
- const isS3Transfer = positionals.length >= 2 && positionals[0] === "s3" && (positionals[1] === "cp" || positionals[1] === "sync" || positionals[1] === "mv");
9350
+ const isS3Transfer = positionals.length >= 2 && positionals[0] === "s3" && // `rm` belongs here for the failure path, not the volume one: `aws s3 rm --recursive` reports `delete failed:` per object, and routing it anywhere else meant those lines were never counted. Its `delete:` success lines are not folded into a count -- only `upload:`/`download:` are -- so adding it drops nothing that used to survive.
9351
+ (positionals[1] === "cp" || positionals[1] === "sync" || positionals[1] === "mv" || positionals[1] === "rm");
8341
9352
  const isCfnEvents = positionals.length >= 2 && positionals[0] === "cloudformation" && positionals[1] === "describe-stack-events";
8342
9353
  let text = stdout;
8343
9354
  if (isS3Transfer) {
@@ -8369,6 +9380,7 @@ ${stderr.replace(/\s+$/, "")}` : stderr;
8369
9380
  const kept = [];
8370
9381
  let uploadCount = 0;
8371
9382
  let downloadCount = 0;
9383
+ let failedCount = 0;
8372
9384
  let progressDropped = 0;
8373
9385
  for (const line of lines) {
8374
9386
  if (_AWS_UPLOAD_RE.test(line)) {
@@ -8379,6 +9391,11 @@ ${stderr.replace(/\s+$/, "")}` : stderr;
8379
9391
  downloadCount++;
8380
9392
  continue;
8381
9393
  }
9394
+ if (_AWS_S3_TRANSFER_FAILED_RE.test(line)) {
9395
+ failedCount++;
9396
+ kept.push(line);
9397
+ continue;
9398
+ }
8382
9399
  if (_AWS_S3_PROGRESS_RE.test(line)) {
8383
9400
  progressDropped++;
8384
9401
  continue;
@@ -8388,6 +9405,7 @@ ${stderr.replace(/\s+$/, "")}` : stderr;
8388
9405
  const notes = [];
8389
9406
  maybeNote(notes, uploadCount, `uploaded ${uploadCount} file(s)`);
8390
9407
  maybeNote(notes, downloadCount, `downloaded ${downloadCount} file(s)`);
9408
+ maybeNote(notes, failedCount, `${failedCount} transfer(s) failed`);
8391
9409
  maybeNote(notes, progressDropped, `dropped ${progressDropped} progress line(s)`);
8392
9410
  this.emitNotes(kept, notes);
8393
9411
  return this.finalize(kept);
@@ -9488,8 +10506,9 @@ var RgFilter = class _RgFilter extends ToolFilter {
9488
10506
  name = "rg";
9489
10507
  binaries = /* @__PURE__ */ new Set(["rg", "grep"]);
9490
10508
  static _SEP = "--";
9491
- static _CTX_LINE_RE = /^.+-\d+-/;
9492
- static _MATCH_LINE_RE = /^.+:\d+:/;
10509
+ // The path prefix is optional because ripgrep only prints one when the search covers more than one file: `rg -n -C2 pat one.ts` emits `12-text` and `14:text`, not `one.ts-12-text` and `one.ts:14:text`. Requiring the prefix meant neither regex matched anything on a single-file search -- the ordinary shape of the command -- so no context line was ever suppressed there, and the only thing removed was the cosmetic `--` separator, under a note announcing a compression that had not happened.
10510
+ static _CTX_LINE_RE = /^(?:.+-)?\d+-/;
10511
+ static _MATCH_LINE_RE = /^(?:.+:)?\d+:/;
9493
10512
  static _parseContextDepth(argv) {
9494
10513
  let depth = 0;
9495
10514
  const longFlags = /* @__PURE__ */ new Set(["--after-context", "--before-context", "--context"]);
@@ -9529,8 +10548,14 @@ var RgFilter = class _RgFilter extends ToolFilter {
9529
10548
  // RgFilter only handles context-block output (-A/-B/-C/--context); a plain
9530
10549
  // grep/rg with no context flags falls through to GrepFilter's per-file
9531
10550
  // match-count summarizer, which produces dramatically smaller output.
10551
+ // Both line regexes hard-code ripgrep's default field separators (`:` after a match's line number, `-` after a context line's). `--field-match-separator=-` makes a real match read `12-text`, which is exactly the shape this filter drops, so a search that overrides either separator is released to the generic path rather than compressed against an assumption it broke.
10552
+ static _hasCustomFieldSeparator(argv) {
10553
+ const flags = ["--field-match-separator", "--field-context-separator"];
10554
+ return argv.some((a) => flags.some((f) => a === f || a.startsWith(f + "=")));
10555
+ }
9532
10556
  matches(argv) {
9533
10557
  if (!super.matches(argv)) return false;
10558
+ if (_RgFilter._hasCustomFieldSeparator(argv)) return false;
9534
10559
  return _RgFilter._hasContextFlags(argv);
9535
10560
  }
9536
10561
  _compressGroups(groups) {
@@ -9563,7 +10588,8 @@ var RgFilter = class _RgFilter extends ToolFilter {
9563
10588
  const kept = [];
9564
10589
  let suppressed = 0;
9565
10590
  for (const ln of lines) {
9566
- if (ln === _RgFilter._SEP || _RgFilter._CTX_LINE_RE.test(ln) && !_RgFilter._MATCH_LINE_RE.test(ln)) {
10591
+ if (ln === _RgFilter._SEP) continue;
10592
+ if (_RgFilter._CTX_LINE_RE.test(ln) && !_RgFilter._MATCH_LINE_RE.test(ln)) {
9567
10593
  suppressed++;
9568
10594
  } else {
9569
10595
  kept.push(ln);
@@ -13231,6 +14257,8 @@ var _DOCKER_OLD_STEP_ERROR_RE = /error|returned a non-zero code/i;
13231
14257
  var DockerFilter = class extends ToolFilter {
13232
14258
  name = "docker";
13233
14259
  binaries = /* @__PURE__ */ new Set(["docker", "buildah", "podman", "nerdctl"]);
14260
+ // Every rule below is about image transfer and build noise -- layer digests, pull/push progress, cached step lines, step bodies -- so the filter is declared for exactly the subcommands that emit them, including buildah's `bud` spelling of build, `create` and `plugin install`/`plugin upgrade`, which pull layers and emit the same per-layer status lines as `pull`. The base matcher scans the first three positionals, so the management-command spellings (`docker image pull`, `docker container create`) are covered by the same entries. With no subcommand set it claimed every `docker …` invocation and removed a single blank line from the ones it has no rules for, which matters because dispatch stops at the first match: `docker logs` on a service repeating a heartbeat went 8702 bytes in, 8701 out, where the generic compressor it was displacing returns 6987. On output with nothing repeated (`docker images` here: 15912 bytes) neither saves anything, so the gain is confined to the repetitive case -- but a filter that claims a command it cannot compress blocks whatever could.
14261
+ subcommands = /* @__PURE__ */ new Set(["build", "bud", "buildx", "pull", "push", "run", "load", "create", "install", "upgrade"]);
13234
14262
  // Docker writes progress/errors to stderr; only stdout carries build bodies. Merge with stderr first so errors appear before raw build output.
13235
14263
  compress(stdout, stderr, _exitCode, _argv) {
13236
14264
  const merged = this.combineOutput(stderr, stdout);
@@ -13457,6 +14485,7 @@ var _KEY_PREFIXES = [
13457
14485
  "Namespace:",
13458
14486
  "Status:",
13459
14487
  "State:",
14488
+ "Last State:",
13460
14489
  "Node:",
13461
14490
  "IP:",
13462
14491
  "PodIP:",
@@ -13464,6 +14493,7 @@ var _KEY_PREFIXES = [
13464
14493
  "QoS Class:",
13465
14494
  "Priority:",
13466
14495
  "Image:",
14496
+ "Container ID:",
13467
14497
  "Ready:",
13468
14498
  "Restart Count:",
13469
14499
  "Started:",
@@ -14401,12 +15431,18 @@ function _compressGitStatusVerbose(stdout, stderr, argv = null) {
14401
15431
  const kept = [];
14402
15432
  let section = null;
14403
15433
  let counts = {};
15434
+ let fileLines = [];
14404
15435
  function flush() {
14405
- if (section !== null && section !== "unmerged" && Object.keys(counts).length) {
14406
- const parts = Object.entries(counts).map(([label, n]) => `${n} ${label}`);
14407
- kept.push(" " + parts.join(", "));
15436
+ if (section !== null && section !== "unmerged" && fileLines.length) {
15437
+ if (fileLines.length > _DIFF_STAT_DIR_ROLLUP_THRESHOLD) {
15438
+ const parts = Object.entries(counts).map(([label, n]) => `${n} ${label}`);
15439
+ kept.push(" " + parts.join(", "));
15440
+ } else {
15441
+ kept.push(...fileLines);
15442
+ }
14408
15443
  }
14409
15444
  counts = {};
15445
+ fileLines = [];
14410
15446
  }
14411
15447
  for (const line of lines) {
14412
15448
  const headerMatch = _GIT_STATUS_SECTION_RE.exec(line);
@@ -14423,6 +15459,7 @@ function _compressGitStatusVerbose(stdout, stderr, argv = null) {
14423
15459
  } else {
14424
15460
  const label = _gitStatusFileLabel(line, section);
14425
15461
  counts[label] = (counts[label] ?? 0) + 1;
15462
+ fileLines.push(line);
14426
15463
  }
14427
15464
  continue;
14428
15465
  }
@@ -17726,10 +18763,13 @@ function filterByName(name) {
17726
18763
  init_define_import_meta_env();
17727
18764
 
17728
18765
  export {
18766
+ pathEqClause,
18767
+ projectScopeClause,
17729
18768
  getToolName,
17730
18769
  getToolInput,
17731
18770
  getFilePath,
17732
18771
  getCwd,
18772
+ getTranscriptPath,
17733
18773
  extractToolResponseField,
17734
18774
  OUTPUT_FIRST_TOOL_RESPONSE_KEYS,
17735
18775
  BODY_FIRST_TOOL_RESPONSE_KEYS,
@@ -17793,6 +18833,38 @@ export {
17793
18833
  capTokens,
17794
18834
  shlexSplit,
17795
18835
  clipLongMatchLine,
18836
+ isLockFile,
18837
+ isManifestFile,
18838
+ isInBuildDir,
18839
+ isGeneratedFile,
18840
+ isBuildCommand,
18841
+ isTestRunnerCommand,
18842
+ getMonitoringRecallHint,
18843
+ DEFAULT_QUERY_LIMIT,
18844
+ querySymbols,
18845
+ distinctSymbolKinds,
18846
+ countSymbols,
18847
+ queryRefs,
18848
+ countRefs,
18849
+ queryRefsByContext,
18850
+ queryRefCounts,
18851
+ getProjectFileEntries,
18852
+ getFileEntry,
18853
+ searchSymbolsFts,
18854
+ RECALL_DEFAULT_LIMIT,
18855
+ isRecallCacheType,
18856
+ indexRecallEntry,
18857
+ listRecentRecall,
18858
+ searchRecall,
18859
+ BASH_OUTPUT_SUBDIR,
18860
+ isScopedGitStatusOrDiffStatCommand,
18861
+ normalizeCommandForCacheKey,
18862
+ commandHash,
18863
+ isBashEntryStale,
18864
+ summarizeOutputDelta,
18865
+ storeBashOutputSync,
18866
+ storeBashOutput,
18867
+ getBashOutput,
17796
18868
  anchoredMarkerPattern,
17797
18869
  settingsPath,
17798
18870
  installHooks,