token-goat 2.2.3 → 2.2.4

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 (2) hide show
  1. package/dist/token-goat.mjs +2092 -390
  2. package/package.json +1 -1
@@ -981,8 +981,8 @@ var require_command = __commonJS({
981
981
  init_define_import_meta_env();
982
982
  var EventEmitter = __require("node:events").EventEmitter;
983
983
  var childProcess = __require("node:child_process");
984
- var path19 = __require("node:path");
985
- var fs18 = __require("node:fs");
984
+ var path22 = __require("node:path");
985
+ var fs20 = __require("node:fs");
986
986
  var process2 = __require("node:process");
987
987
  var { Argument: Argument2, humanReadableArgName } = require_argument();
988
988
  var { CommanderError: CommanderError2 } = require_error();
@@ -1914,11 +1914,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
1914
1914
  let launchWithNode = false;
1915
1915
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
1916
1916
  function findFile(baseDir, baseName) {
1917
- const localBin = path19.resolve(baseDir, baseName);
1918
- if (fs18.existsSync(localBin)) return localBin;
1919
- if (sourceExt.includes(path19.extname(baseName))) return void 0;
1917
+ const localBin = path22.resolve(baseDir, baseName);
1918
+ if (fs20.existsSync(localBin)) return localBin;
1919
+ if (sourceExt.includes(path22.extname(baseName))) return void 0;
1920
1920
  const foundExt = sourceExt.find(
1921
- (ext) => fs18.existsSync(`${localBin}${ext}`)
1921
+ (ext) => fs20.existsSync(`${localBin}${ext}`)
1922
1922
  );
1923
1923
  if (foundExt) return `${localBin}${foundExt}`;
1924
1924
  return void 0;
@@ -1930,21 +1930,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
1930
1930
  if (this._scriptPath) {
1931
1931
  let resolvedScriptPath;
1932
1932
  try {
1933
- resolvedScriptPath = fs18.realpathSync(this._scriptPath);
1933
+ resolvedScriptPath = fs20.realpathSync(this._scriptPath);
1934
1934
  } catch (err2) {
1935
1935
  resolvedScriptPath = this._scriptPath;
1936
1936
  }
1937
- executableDir = path19.resolve(
1938
- path19.dirname(resolvedScriptPath),
1937
+ executableDir = path22.resolve(
1938
+ path22.dirname(resolvedScriptPath),
1939
1939
  executableDir
1940
1940
  );
1941
1941
  }
1942
1942
  if (executableDir) {
1943
1943
  let localFile = findFile(executableDir, executableFile);
1944
1944
  if (!localFile && !subcommand._executableFile && this._scriptPath) {
1945
- const legacyName = path19.basename(
1945
+ const legacyName = path22.basename(
1946
1946
  this._scriptPath,
1947
- path19.extname(this._scriptPath)
1947
+ path22.extname(this._scriptPath)
1948
1948
  );
1949
1949
  if (legacyName !== this._name) {
1950
1950
  localFile = findFile(
@@ -1955,7 +1955,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1955
1955
  }
1956
1956
  executableFile = localFile || executableFile;
1957
1957
  }
1958
- launchWithNode = sourceExt.includes(path19.extname(executableFile));
1958
+ launchWithNode = sourceExt.includes(path22.extname(executableFile));
1959
1959
  let proc;
1960
1960
  if (process2.platform !== "win32") {
1961
1961
  if (launchWithNode) {
@@ -2795,7 +2795,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2795
2795
  * @return {Command}
2796
2796
  */
2797
2797
  nameFromFilename(filename) {
2798
- this._name = path19.basename(filename, path19.extname(filename));
2798
+ this._name = path22.basename(filename, path22.extname(filename));
2799
2799
  return this;
2800
2800
  }
2801
2801
  /**
@@ -2809,9 +2809,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
2809
2809
  * @param {string} [path]
2810
2810
  * @return {(string|null|Command)}
2811
2811
  */
2812
- executableDir(path20) {
2813
- if (path20 === void 0) return this._executableDir;
2814
- this._executableDir = path20;
2812
+ executableDir(path23) {
2813
+ if (path23 === void 0) return this._executableDir;
2814
+ this._executableDir = path23;
2815
2815
  return this;
2816
2816
  }
2817
2817
  /**
@@ -3158,27 +3158,27 @@ var require_filesystem = __commonJS({
3158
3158
  "node_modules/detect-libc/lib/filesystem.js"(exports, module) {
3159
3159
  "use strict";
3160
3160
  init_define_import_meta_env();
3161
- var fs18 = __require("fs");
3161
+ var fs20 = __require("fs");
3162
3162
  var LDD_PATH = "/usr/bin/ldd";
3163
3163
  var SELF_PATH = "/proc/self/exe";
3164
3164
  var MAX_LENGTH = 2048;
3165
- var readFileSync14 = (path19) => {
3166
- const fd = fs18.openSync(path19, "r");
3165
+ var readFileSync16 = (path22) => {
3166
+ const fd = fs20.openSync(path22, "r");
3167
3167
  const buffer = Buffer.alloc(MAX_LENGTH);
3168
- const bytesRead = fs18.readSync(fd, buffer, 0, MAX_LENGTH, 0);
3169
- fs18.close(fd, () => {
3168
+ const bytesRead = fs20.readSync(fd, buffer, 0, MAX_LENGTH, 0);
3169
+ fs20.close(fd, () => {
3170
3170
  });
3171
3171
  return buffer.subarray(0, bytesRead);
3172
3172
  };
3173
- var readFile3 = (path19) => new Promise((resolve7, reject) => {
3174
- fs18.open(path19, "r", (err2, fd) => {
3173
+ var readFile3 = (path22) => new Promise((resolve8, reject) => {
3174
+ fs20.open(path22, "r", (err2, fd) => {
3175
3175
  if (err2) {
3176
3176
  reject(err2);
3177
3177
  } else {
3178
3178
  const buffer = Buffer.alloc(MAX_LENGTH);
3179
- fs18.read(fd, buffer, 0, MAX_LENGTH, 0, (_, bytesRead) => {
3180
- resolve7(buffer.subarray(0, bytesRead));
3181
- fs18.close(fd, () => {
3179
+ fs20.read(fd, buffer, 0, MAX_LENGTH, 0, (_, bytesRead) => {
3180
+ resolve8(buffer.subarray(0, bytesRead));
3181
+ fs20.close(fd, () => {
3182
3182
  });
3183
3183
  });
3184
3184
  }
@@ -3187,7 +3187,7 @@ var require_filesystem = __commonJS({
3187
3187
  module.exports = {
3188
3188
  LDD_PATH,
3189
3189
  SELF_PATH,
3190
- readFileSync: readFileSync14,
3190
+ readFileSync: readFileSync16,
3191
3191
  readFile: readFile3
3192
3192
  };
3193
3193
  }
@@ -3238,7 +3238,7 @@ var require_detect_libc = __commonJS({
3238
3238
  init_define_import_meta_env();
3239
3239
  var childProcess = __require("child_process");
3240
3240
  var { isLinux, getReport } = require_process();
3241
- var { LDD_PATH, SELF_PATH, readFile: readFile3, readFileSync: readFileSync14 } = require_filesystem();
3241
+ var { LDD_PATH, SELF_PATH, readFile: readFile3, readFileSync: readFileSync16 } = require_filesystem();
3242
3242
  var { interpreterPath } = require_elf();
3243
3243
  var cachedFamilyInterpreter;
3244
3244
  var cachedFamilyFilesystem;
@@ -3247,10 +3247,10 @@ var require_detect_libc = __commonJS({
3247
3247
  var commandOut = "";
3248
3248
  var safeCommand = () => {
3249
3249
  if (!commandOut) {
3250
- return new Promise((resolve7) => {
3250
+ return new Promise((resolve8) => {
3251
3251
  childProcess.exec(command, (err2, out2) => {
3252
3252
  commandOut = err2 ? " " : out2;
3253
- resolve7(commandOut);
3253
+ resolve8(commandOut);
3254
3254
  });
3255
3255
  });
3256
3256
  }
@@ -3292,11 +3292,11 @@ var require_detect_libc = __commonJS({
3292
3292
  }
3293
3293
  return null;
3294
3294
  };
3295
- var familyFromInterpreterPath = (path19) => {
3296
- if (path19) {
3297
- if (path19.includes("/ld-musl-")) {
3295
+ var familyFromInterpreterPath = (path22) => {
3296
+ if (path22) {
3297
+ if (path22.includes("/ld-musl-")) {
3298
3298
  return MUSL;
3299
- } else if (path19.includes("/ld-linux-")) {
3299
+ } else if (path22.includes("/ld-linux-")) {
3300
3300
  return GLIBC;
3301
3301
  }
3302
3302
  }
@@ -3330,7 +3330,7 @@ var require_detect_libc = __commonJS({
3330
3330
  }
3331
3331
  cachedFamilyFilesystem = null;
3332
3332
  try {
3333
- const lddContent = readFileSync14(LDD_PATH);
3333
+ const lddContent = readFileSync16(LDD_PATH);
3334
3334
  cachedFamilyFilesystem = getFamilyFromLddContent(lddContent);
3335
3335
  } catch (e) {
3336
3336
  }
@@ -3343,8 +3343,8 @@ var require_detect_libc = __commonJS({
3343
3343
  cachedFamilyInterpreter = null;
3344
3344
  try {
3345
3345
  const selfContent = await readFile3(SELF_PATH);
3346
- const path19 = interpreterPath(selfContent);
3347
- cachedFamilyInterpreter = familyFromInterpreterPath(path19);
3346
+ const path22 = interpreterPath(selfContent);
3347
+ cachedFamilyInterpreter = familyFromInterpreterPath(path22);
3348
3348
  } catch (e) {
3349
3349
  }
3350
3350
  return cachedFamilyInterpreter;
@@ -3355,9 +3355,9 @@ var require_detect_libc = __commonJS({
3355
3355
  }
3356
3356
  cachedFamilyInterpreter = null;
3357
3357
  try {
3358
- const selfContent = readFileSync14(SELF_PATH);
3359
- const path19 = interpreterPath(selfContent);
3360
- cachedFamilyInterpreter = familyFromInterpreterPath(path19);
3358
+ const selfContent = readFileSync16(SELF_PATH);
3359
+ const path22 = interpreterPath(selfContent);
3360
+ cachedFamilyInterpreter = familyFromInterpreterPath(path22);
3361
3361
  } catch (e) {
3362
3362
  }
3363
3363
  return cachedFamilyInterpreter;
@@ -3419,7 +3419,7 @@ var require_detect_libc = __commonJS({
3419
3419
  }
3420
3420
  cachedVersionFilesystem = null;
3421
3421
  try {
3422
- const lddContent = readFileSync14(LDD_PATH);
3422
+ const lddContent = readFileSync16(LDD_PATH);
3423
3423
  const versionMatch = lddContent.match(RE_GLIBC_VERSION);
3424
3424
  if (versionMatch) {
3425
3425
  cachedVersionFilesystem = versionMatch[1];
@@ -3965,7 +3965,7 @@ var require_parse = __commonJS({
3965
3965
  "use strict";
3966
3966
  init_define_import_meta_env();
3967
3967
  var SemVer = require_semver();
3968
- var parse2 = (version, options, throwErrors = false) => {
3968
+ var parse3 = (version, options, throwErrors = false) => {
3969
3969
  if (version instanceof SemVer) {
3970
3970
  return version;
3971
3971
  }
@@ -3978,7 +3978,7 @@ var require_parse = __commonJS({
3978
3978
  throw er;
3979
3979
  }
3980
3980
  };
3981
- module.exports = parse2;
3981
+ module.exports = parse3;
3982
3982
  }
3983
3983
  });
3984
3984
 
@@ -3988,9 +3988,9 @@ var require_coerce = __commonJS({
3988
3988
  "use strict";
3989
3989
  init_define_import_meta_env();
3990
3990
  var SemVer = require_semver();
3991
- var parse2 = require_parse();
3991
+ var parse3 = require_parse();
3992
3992
  var { safeRe: re, t } = require_re();
3993
- var coerce = (version, options) => {
3993
+ var coerce2 = (version, options) => {
3994
3994
  if (version instanceof SemVer) {
3995
3995
  return version;
3996
3996
  }
@@ -4023,9 +4023,9 @@ var require_coerce = __commonJS({
4023
4023
  const patch = match[4] || "0";
4024
4024
  const prerelease = options.includePrerelease && match[5] ? `-${match[5]}` : "";
4025
4025
  const build = options.includePrerelease && match[6] ? `+${match[6]}` : "";
4026
- return parse2(`${major}.${minor}.${patch}${prerelease}${build}`, options);
4026
+ return parse3(`${major}.${minor}.${patch}${prerelease}${build}`, options);
4027
4027
  };
4028
- module.exports = coerce;
4028
+ module.exports = coerce2;
4029
4029
  }
4030
4030
  });
4031
4031
 
@@ -4947,7 +4947,7 @@ var require_libvips = __commonJS({
4947
4947
  "node_modules/sharp/lib/libvips.js"(exports, module) {
4948
4948
  "use strict";
4949
4949
  init_define_import_meta_env();
4950
- var { spawnSync: spawnSync3 } = __require("node:child_process");
4950
+ var { spawnSync: spawnSync4 } = __require("node:child_process");
4951
4951
  var { createHash: createHash3 } = __require("node:crypto");
4952
4952
  var semverCoerce = require_coerce();
4953
4953
  var semverGreaterThanOrEqualTo = require_gte();
@@ -5032,7 +5032,7 @@ var require_libvips = __commonJS({
5032
5032
  };
5033
5033
  var isRosetta = () => {
5034
5034
  if (process.platform === "darwin" && process.arch === "x64") {
5035
- const translated = spawnSync3("sysctl sysctl.proc_translated", spawnSyncOptions).stdout;
5035
+ const translated = spawnSync4("sysctl sysctl.proc_translated", spawnSyncOptions).stdout;
5036
5036
  return (translated || "").trim() === "sysctl.proc_translated: 1";
5037
5037
  }
5038
5038
  return false;
@@ -5047,13 +5047,13 @@ var require_libvips = __commonJS({
5047
5047
  }
5048
5048
  return "";
5049
5049
  };
5050
- var spawnRebuild = () => spawnSync3(`node-gyp rebuild --directory=src ${isEmscripten() ? "--nodedir=emscripten" : ""}`, {
5050
+ var spawnRebuild = () => spawnSync4(`node-gyp rebuild --directory=src ${isEmscripten() ? "--nodedir=emscripten" : ""}`, {
5051
5051
  ...spawnSyncOptions,
5052
5052
  stdio: "inherit"
5053
5053
  }).status;
5054
5054
  var globalLibvipsVersion = () => {
5055
5055
  if (process.platform !== "win32") {
5056
- const globalLibvipsVersion2 = spawnSync3("pkg-config --modversion vips-cpp", {
5056
+ const globalLibvipsVersion2 = spawnSync4("pkg-config --modversion vips-cpp", {
5057
5057
  ...spawnSyncOptions,
5058
5058
  env: {
5059
5059
  ...process.env,
@@ -5067,7 +5067,7 @@ var require_libvips = __commonJS({
5067
5067
  };
5068
5068
  var pkgConfigPath = () => {
5069
5069
  if (process.platform !== "win32") {
5070
- const brewPkgConfigPath = spawnSync3(
5070
+ const brewPkgConfigPath = spawnSync4(
5071
5071
  'which brew >/dev/null 2>&1 && brew environment --plain | grep PKG_CONFIG_LIBDIR | cut -d" " -f2',
5072
5072
  spawnSyncOptions
5073
5073
  ).stdout || "";
@@ -5138,9 +5138,9 @@ var require_sharp = __commonJS({
5138
5138
  ];
5139
5139
  var sharp;
5140
5140
  var errors = [];
5141
- for (const path19 of paths) {
5141
+ for (const path22 of paths) {
5142
5142
  try {
5143
- sharp = __require(path19);
5143
+ sharp = __require(path22);
5144
5144
  break;
5145
5145
  } catch (err2) {
5146
5146
  errors.push(err2);
@@ -5149,7 +5149,7 @@ var require_sharp = __commonJS({
5149
5149
  if (sharp) {
5150
5150
  module.exports = sharp;
5151
5151
  } else {
5152
- const [isLinux, isMacOs, isWindows2] = ["linux", "darwin", "win32"].map((os4) => runtimePlatform.startsWith(os4));
5152
+ const [isLinux, isMacOs, isWindows2] = ["linux", "darwin", "win32"].map((os7) => runtimePlatform.startsWith(os7));
5153
5153
  const help = [`Could not load the "sharp" module using the ${runtimePlatform} runtime`];
5154
5154
  errors.forEach((err2) => {
5155
5155
  if (err2.code !== "MODULE_NOT_FOUND") {
@@ -5166,15 +5166,15 @@ var require_sharp = __commonJS({
5166
5166
  ` Requires ${expected}`
5167
5167
  );
5168
5168
  } else if (prebuiltPlatforms.includes(runtimePlatform)) {
5169
- const [os4, cpu] = runtimePlatform.split("-");
5170
- const libc = os4.endsWith("musl") ? " --libc=musl" : "";
5169
+ const [os7, cpu] = runtimePlatform.split("-");
5170
+ const libc = os7.endsWith("musl") ? " --libc=musl" : "";
5171
5171
  help.push(
5172
5172
  "- Ensure optional dependencies can be installed:",
5173
5173
  " npm install --include=optional sharp",
5174
5174
  "- Ensure your package manager supports multi-platform installation:",
5175
5175
  " See https://sharp.pixelplumbing.com/install#cross-platform",
5176
5176
  "- Add platform-specific dependencies:",
5177
- ` npm install --os=${os4.replace("musl", "")}${libc} --cpu=${cpu} sharp`
5177
+ ` npm install --os=${os7.replace("musl", "")}${libc} --cpu=${cpu} sharp`
5178
5178
  );
5179
5179
  } else {
5180
5180
  help.push(
@@ -6552,15 +6552,15 @@ var require_route = __commonJS({
6552
6552
  };
6553
6553
  }
6554
6554
  function wrapConversion(toModel, graph) {
6555
- const path19 = [graph[toModel].parent, toModel];
6555
+ const path22 = [graph[toModel].parent, toModel];
6556
6556
  let fn = conversions[graph[toModel].parent][toModel];
6557
6557
  let cur = graph[toModel].parent;
6558
6558
  while (graph[cur].parent) {
6559
- path19.unshift(graph[cur].parent);
6559
+ path22.unshift(graph[cur].parent);
6560
6560
  fn = link(conversions[graph[cur].parent][cur], fn);
6561
6561
  cur = graph[cur].parent;
6562
6562
  }
6563
- fn.conversion = path19;
6563
+ fn.conversion = path22;
6564
6564
  return fn;
6565
6565
  }
6566
6566
  module.exports = function(fromModel) {
@@ -7392,14 +7392,14 @@ var require_input = __commonJS({
7392
7392
  return this;
7393
7393
  } else {
7394
7394
  if (this._isStreamInput()) {
7395
- return new Promise((resolve7, reject) => {
7395
+ return new Promise((resolve8, reject) => {
7396
7396
  const finished = () => {
7397
7397
  this._flattenBufferIn();
7398
7398
  sharp.metadata(this.options, (err2, metadata2) => {
7399
7399
  if (err2) {
7400
7400
  reject(is.nativeError(err2, stack));
7401
7401
  } else {
7402
- resolve7(metadata2);
7402
+ resolve8(metadata2);
7403
7403
  }
7404
7404
  });
7405
7405
  };
@@ -7410,12 +7410,12 @@ var require_input = __commonJS({
7410
7410
  }
7411
7411
  });
7412
7412
  } else {
7413
- return new Promise((resolve7, reject) => {
7413
+ return new Promise((resolve8, reject) => {
7414
7414
  sharp.metadata(this.options, (err2, metadata2) => {
7415
7415
  if (err2) {
7416
7416
  reject(is.nativeError(err2, stack));
7417
7417
  } else {
7418
- resolve7(metadata2);
7418
+ resolve8(metadata2);
7419
7419
  }
7420
7420
  });
7421
7421
  });
@@ -7448,25 +7448,25 @@ var require_input = __commonJS({
7448
7448
  return this;
7449
7449
  } else {
7450
7450
  if (this._isStreamInput()) {
7451
- return new Promise((resolve7, reject) => {
7451
+ return new Promise((resolve8, reject) => {
7452
7452
  this.on("finish", function() {
7453
7453
  this._flattenBufferIn();
7454
7454
  sharp.stats(this.options, (err2, stats2) => {
7455
7455
  if (err2) {
7456
7456
  reject(is.nativeError(err2, stack));
7457
7457
  } else {
7458
- resolve7(stats2);
7458
+ resolve8(stats2);
7459
7459
  }
7460
7460
  });
7461
7461
  });
7462
7462
  });
7463
7463
  } else {
7464
- return new Promise((resolve7, reject) => {
7464
+ return new Promise((resolve8, reject) => {
7465
7465
  sharp.stats(this.options, (err2, stats2) => {
7466
7466
  if (err2) {
7467
7467
  reject(is.nativeError(err2, stack));
7468
7468
  } else {
7469
- resolve7(stats2);
7469
+ resolve8(stats2);
7470
7470
  }
7471
7471
  });
7472
7472
  });
@@ -8107,7 +8107,7 @@ var require_operation = __commonJS({
8107
8107
  }
8108
8108
  return this;
8109
8109
  }
8110
- function normalise(options) {
8110
+ function normalise2(options) {
8111
8111
  if (is.plainObject(options)) {
8112
8112
  if (is.defined(options.lower)) {
8113
8113
  if (is.number(options.lower) && is.inRange(options.lower, 0, 99)) {
@@ -8295,7 +8295,7 @@ var require_operation = __commonJS({
8295
8295
  unflatten,
8296
8296
  gamma,
8297
8297
  negate,
8298
- normalise,
8298
+ normalise: normalise2,
8299
8299
  normalize,
8300
8300
  clahe,
8301
8301
  convolve,
@@ -8464,7 +8464,7 @@ var require_output = __commonJS({
8464
8464
  "node_modules/sharp/lib/output.js"(exports, module) {
8465
8465
  "use strict";
8466
8466
  init_define_import_meta_env();
8467
- var path19 = __require("node:path");
8467
+ var path22 = __require("node:path");
8468
8468
  var is = require_is();
8469
8469
  var sharp = require_sharp();
8470
8470
  var formats = /* @__PURE__ */ new Map([
@@ -8495,9 +8495,9 @@ var require_output = __commonJS({
8495
8495
  let err2;
8496
8496
  if (!is.string(fileOut)) {
8497
8497
  err2 = new Error("Missing output file path");
8498
- } else if (is.string(this.options.input.file) && path19.resolve(this.options.input.file) === path19.resolve(fileOut)) {
8498
+ } else if (is.string(this.options.input.file) && path22.resolve(this.options.input.file) === path22.resolve(fileOut)) {
8499
8499
  err2 = new Error("Cannot use same file for input and output");
8500
- } else if (jp2Regex.test(path19.extname(fileOut)) && !this.constructor.format.jp2k.output.file) {
8500
+ } else if (jp2Regex.test(path22.extname(fileOut)) && !this.constructor.format.jp2k.output.file) {
8501
8501
  err2 = errJp2Save();
8502
8502
  }
8503
8503
  if (err2) {
@@ -9242,7 +9242,7 @@ var require_output = __commonJS({
9242
9242
  return this;
9243
9243
  } else {
9244
9244
  if (this._isStreamInput()) {
9245
- return new Promise((resolve7, reject) => {
9245
+ return new Promise((resolve8, reject) => {
9246
9246
  this.once("finish", () => {
9247
9247
  this._flattenBufferIn();
9248
9248
  sharp.pipeline(this.options, (err2, data, info) => {
@@ -9250,24 +9250,24 @@ var require_output = __commonJS({
9250
9250
  reject(is.nativeError(err2, stack));
9251
9251
  } else {
9252
9252
  if (this.options.resolveWithObject) {
9253
- resolve7({ data, info });
9253
+ resolve8({ data, info });
9254
9254
  } else {
9255
- resolve7(data);
9255
+ resolve8(data);
9256
9256
  }
9257
9257
  }
9258
9258
  });
9259
9259
  });
9260
9260
  });
9261
9261
  } else {
9262
- return new Promise((resolve7, reject) => {
9262
+ return new Promise((resolve8, reject) => {
9263
9263
  sharp.pipeline(this.options, (err2, data, info) => {
9264
9264
  if (err2) {
9265
9265
  reject(is.nativeError(err2, stack));
9266
9266
  } else {
9267
9267
  if (this.options.resolveWithObject) {
9268
- resolve7({ data, info });
9268
+ resolve8({ data, info });
9269
9269
  } else {
9270
- resolve7(data);
9270
+ resolve8(data);
9271
9271
  }
9272
9272
  }
9273
9273
  });
@@ -9475,8 +9475,8 @@ var {
9475
9475
  } = import_index.default;
9476
9476
 
9477
9477
  // src/cli.ts
9478
- import * as fs17 from "fs";
9479
- import * as path18 from "path";
9478
+ import * as fs19 from "fs";
9479
+ import * as path21 from "path";
9480
9480
 
9481
9481
  // src/baseline.ts
9482
9482
  init_define_import_meta_env();
@@ -9493,7 +9493,7 @@ init_define_import_meta_env();
9493
9493
  import { createRequire } from "node:module";
9494
9494
  function resolveVersion() {
9495
9495
  if (true) {
9496
- return "2.2.3";
9496
+ return "2.2.4";
9497
9497
  }
9498
9498
  const require2 = createRequire(import.meta.url);
9499
9499
  const pkg = require2("../package.json");
@@ -10191,23 +10191,23 @@ function isNoisePath(inputPath) {
10191
10191
  }
10192
10192
  }
10193
10193
  const slashIdx = p.lastIndexOf("/");
10194
- const basename8 = slashIdx >= 0 ? p.slice(slashIdx + 1) : p;
10195
- if (NOISE_BASENAMES.has(basename8)) {
10194
+ const basename9 = slashIdx >= 0 ? p.slice(slashIdx + 1) : p;
10195
+ if (NOISE_BASENAMES.has(basename9)) {
10196
10196
  return true;
10197
10197
  }
10198
- if (basename8.startsWith(".improve-state-") || basename8.startsWith("improve_commit_msg_")) {
10198
+ if (basename9.startsWith(".improve-state-") || basename9.startsWith("improve_commit_msg_")) {
10199
10199
  return true;
10200
10200
  }
10201
- const dotIdx = basename8.lastIndexOf(".");
10201
+ const dotIdx = basename9.lastIndexOf(".");
10202
10202
  if (dotIdx >= 0) {
10203
- const ext = basename8.slice(dotIdx);
10203
+ const ext = basename9.slice(dotIdx);
10204
10204
  if (NOISE_EXTS.has(ext)) {
10205
10205
  return true;
10206
10206
  }
10207
10207
  }
10208
10208
  for (const ext of NOISE_EXTS) {
10209
10209
  if (ext.includes(".") && ext.split(".").length > 2) {
10210
- if (basename8.endsWith(ext)) {
10210
+ if (basename9.endsWith(ext)) {
10211
10211
  return true;
10212
10212
  }
10213
10213
  }
@@ -10294,6 +10294,48 @@ function formatMap(entries, opts = {}) {
10294
10294
  return lines.join("\n");
10295
10295
  }
10296
10296
 
10297
+ // src/walk_index.ts
10298
+ init_define_import_meta_env();
10299
+ import * as os2 from "node:os";
10300
+ import * as path7 from "node:path";
10301
+ function isWalkExcluded(file) {
10302
+ const base = path7.basename(file).toLowerCase();
10303
+ if (base === ".env" || base.startsWith(".env.")) return true;
10304
+ if (base.endsWith(".d.ts")) return true;
10305
+ return false;
10306
+ }
10307
+ function assertWalkableRoot(root) {
10308
+ const resolved = path7.resolve(root);
10309
+ const fold = (p) => process.platform === "win32" || process.platform === "darwin" ? p.toLowerCase() : p;
10310
+ const norm = fold(normalizePath(resolved));
10311
+ const fsRoot = fold(normalizePath(path7.parse(resolved).root));
10312
+ if (norm === fsRoot) {
10313
+ throw new Error(`refusing to walk-index a filesystem root: ${resolved}`);
10314
+ }
10315
+ const home = os2.homedir();
10316
+ if (home) {
10317
+ const normHome = fold(normalizePath(path7.resolve(home)));
10318
+ if (norm === normHome) {
10319
+ throw new Error(`refusing to walk-index the home directory: ${resolved}`);
10320
+ }
10321
+ const boundary = norm.endsWith("/") ? norm : `${norm}/`;
10322
+ if (normHome.startsWith(boundary)) {
10323
+ throw new Error(`refusing to walk-index '${resolved}' \u2014 it contains the home directory`);
10324
+ }
10325
+ }
10326
+ }
10327
+ function collectWalkIndexFiles(root) {
10328
+ const resolved = path7.resolve(root);
10329
+ assertWalkableRoot(resolved);
10330
+ const { files } = walkProject(resolved);
10331
+ if (files.length >= MAX_FILES_SCANNED) {
10332
+ throw new Error(
10333
+ `'${resolved}' has too many source files (>= ${MAX_FILES_SCANNED}); refusing to walk-index \u2014 index a git repo or point at a narrower path`
10334
+ );
10335
+ }
10336
+ return files.filter((f) => !isWalkExcluded(f));
10337
+ }
10338
+
10297
10339
  // src/session.ts
10298
10340
  init_define_import_meta_env();
10299
10341
  import { randomBytes, randomUUID } from "node:crypto";
@@ -10361,6 +10403,9 @@ function wasFileReadThisSession(filePath) {
10361
10403
  const entry = _files.get(normalizePath(filePath));
10362
10404
  return entry !== void 0 && entry.readCount > 0;
10363
10405
  }
10406
+ function recordWebFetch(url, cacheId) {
10407
+ _webFetches.set(url, cacheId);
10408
+ }
10364
10409
  function getSessionWebFetches() {
10365
10410
  return _webFetches;
10366
10411
  }
@@ -10409,6 +10454,25 @@ function getSessionId() {
10409
10454
  _sessionId = fromEnv !== void 0 && fromEnv !== "" ? fromEnv : generateSessionId();
10410
10455
  return _sessionId;
10411
10456
  }
10457
+ function exportSessionState() {
10458
+ return {
10459
+ files: Array.from(_files.values()),
10460
+ hintsShown: Array.from(_hintsShown),
10461
+ webFetches: Array.from(_webFetches.entries()),
10462
+ bashOutputs: Array.from(_bashOutputs.entries()),
10463
+ curlDownloads: Array.from(_curlDownloads.entries())
10464
+ };
10465
+ }
10466
+ function importSessionState(s) {
10467
+ _files = /* @__PURE__ */ new Map();
10468
+ for (const e of s.files) {
10469
+ if (e && typeof e.path === "string") _files.set(e.path, e);
10470
+ }
10471
+ _hintsShown = new Set(s.hintsShown);
10472
+ _webFetches = new Map(s.webFetches);
10473
+ _bashOutputs = new Map(s.bashOutputs);
10474
+ _curlDownloads = new Map(s.curlDownloads);
10475
+ }
10412
10476
  registerReset(() => {
10413
10477
  _files = /* @__PURE__ */ new Map();
10414
10478
  _hintsShown = /* @__PURE__ */ new Set();
@@ -10728,7 +10792,8 @@ function assignFlatEndLines(sections, totalLines) {
10728
10792
  const next = sections[i + 1];
10729
10793
  const s = sections[i];
10730
10794
  if (s === void 0) continue;
10731
- s.endLine = next !== void 0 ? next.line - 1 : totalLines;
10795
+ const end = next !== void 0 ? next.line - 1 : totalLines;
10796
+ s.endLine = end < s.line ? s.line : end;
10732
10797
  }
10733
10798
  }
10734
10799
  function propagateEndLinesToSymbols(symbols, sections) {
@@ -10860,7 +10925,7 @@ function extractHtml(content, filePath) {
10860
10925
 
10861
10926
  // src/languages/liquid.ts
10862
10927
  init_define_import_meta_env();
10863
- import * as path7 from "node:path";
10928
+ import * as path8 from "node:path";
10864
10929
  var INCLUDE_RE = /{%\s*include\s+['"]([^'"]+)['"]/gi;
10865
10930
  var SECTION_RE = /{%\s*section\s+['"]([^'"]+)['"]/gi;
10866
10931
  var RENDER_RE = /{%\s*render\s+['"]([^'"]+)['"]/gi;
@@ -10904,7 +10969,7 @@ function extractLiquid(content, filePath, relPath) {
10904
10969
  const resolvedRel = relPath ?? filePath;
10905
10970
  const relPosix = resolvedRel.replace(/\\/g, "/");
10906
10971
  if (relPosix.startsWith("sections/") || relPosix.includes("/sections/")) {
10907
- const stem = path7.basename(resolvedRel, path7.extname(resolvedRel));
10972
+ const stem = path8.basename(resolvedRel, path8.extname(resolvedRel));
10908
10973
  symbols.push({ filePath, name: stem, kind: "liquid_section_file", lineStart: 1, lineEnd: 1, body: "", docstring: "" });
10909
10974
  }
10910
10975
  const totalLines = content.split("\n").length;
@@ -12204,6 +12269,14 @@ function serializeOutput(output) {
12204
12269
  return JSON.stringify({ context: output.context });
12205
12270
  case "update":
12206
12271
  return JSON.stringify({ updatedInput: { content: output.content } });
12272
+ case "rewriteInput":
12273
+ return JSON.stringify({
12274
+ hookSpecificOutput: {
12275
+ hookEventName: "PreToolUse",
12276
+ permissionDecision: "allow",
12277
+ updatedInput: output.updatedInput
12278
+ }
12279
+ });
12207
12280
  case "pass":
12208
12281
  return JSON.stringify({});
12209
12282
  }
@@ -12222,11 +12295,239 @@ var HOOK_EVENTS = [
12222
12295
  "subagent_stop"
12223
12296
  ];
12224
12297
 
12225
- // src/hooks_read.ts
12298
+ // src/session_store.ts
12226
12299
  init_define_import_meta_env();
12227
- import * as fs8 from "node:fs";
12300
+ import * as fs7 from "node:fs";
12228
12301
  import * as path10 from "node:path";
12229
12302
 
12303
+ // src/disk_cache.ts
12304
+ init_define_import_meta_env();
12305
+ import * as fs6 from "node:fs";
12306
+ import * as path9 from "node:path";
12307
+ import * as os3 from "node:os";
12308
+ var DEFAULT_MAX_COUNT = 200;
12309
+ var DEFAULT_MAX_AGE_MS = 24 * 3600 * 1e3;
12310
+ var ID_SANITIZE_RE = /[^a-zA-Z0-9_-]/g;
12311
+ function tokenGoatHome() {
12312
+ const override = process.env["TOKEN_GOAT_HOME"];
12313
+ if (override !== void 0 && override !== "") return override;
12314
+ return path9.join(os3.homedir(), ".token-goat");
12315
+ }
12316
+ function sanitizeId(id) {
12317
+ return id.replace(ID_SANITIZE_RE, "_").slice(0, 64);
12318
+ }
12319
+ function blobDir(subdir) {
12320
+ return path9.join(tokenGoatHome(), subdir);
12321
+ }
12322
+ function blobPath(subdir, id) {
12323
+ const safe = sanitizeId(id);
12324
+ if (!safe) return null;
12325
+ const dir = blobDir(subdir);
12326
+ const candidate = path9.join(dir, `${safe}.json`);
12327
+ try {
12328
+ const rel = path9.relative(dir, candidate);
12329
+ if (rel.startsWith("..")) return null;
12330
+ } catch {
12331
+ return null;
12332
+ }
12333
+ return candidate;
12334
+ }
12335
+ function storeBlob(subdir, id, value, opts = {}) {
12336
+ const p = blobPath(subdir, id);
12337
+ if (!p) return false;
12338
+ try {
12339
+ const dir = path9.dirname(p);
12340
+ if (!fs6.existsSync(dir)) fs6.mkdirSync(dir, { recursive: true });
12341
+ atomicWriteText(p, JSON.stringify(value));
12342
+ } catch {
12343
+ return false;
12344
+ }
12345
+ pruneBlobs(subdir, opts.maxCount ?? DEFAULT_MAX_COUNT, opts.maxAgeMs ?? DEFAULT_MAX_AGE_MS);
12346
+ return true;
12347
+ }
12348
+ function loadBlob(subdir, id) {
12349
+ const p = blobPath(subdir, id);
12350
+ if (!p) return null;
12351
+ try {
12352
+ if (!fs6.existsSync(p)) return null;
12353
+ return JSON.parse(fs6.readFileSync(p, "utf8"));
12354
+ } catch {
12355
+ return null;
12356
+ }
12357
+ }
12358
+ function pruneBlobs(subdir, maxCount = DEFAULT_MAX_COUNT, maxAgeMs = DEFAULT_MAX_AGE_MS) {
12359
+ const dir = blobDir(subdir);
12360
+ let removed = 0;
12361
+ try {
12362
+ if (!fs6.existsSync(dir)) return 0;
12363
+ const cutoff = Date.now() - maxAgeMs;
12364
+ const kept = [];
12365
+ for (const file of fs6.readdirSync(dir)) {
12366
+ if (!file.endsWith(".json")) continue;
12367
+ const full = path9.join(dir, file);
12368
+ let mtime;
12369
+ try {
12370
+ mtime = fs6.statSync(full).mtimeMs;
12371
+ } catch {
12372
+ continue;
12373
+ }
12374
+ if (mtime < cutoff) {
12375
+ try {
12376
+ fs6.unlinkSync(full);
12377
+ removed++;
12378
+ } catch {
12379
+ continue;
12380
+ }
12381
+ } else {
12382
+ kept.push([full, mtime]);
12383
+ }
12384
+ }
12385
+ if (kept.length > maxCount) {
12386
+ kept.sort((a, b) => a[1] - b[1]);
12387
+ for (const [full] of kept.slice(0, kept.length - maxCount)) {
12388
+ try {
12389
+ fs6.unlinkSync(full);
12390
+ removed++;
12391
+ } catch {
12392
+ continue;
12393
+ }
12394
+ }
12395
+ }
12396
+ } catch {
12397
+ return removed;
12398
+ }
12399
+ return removed;
12400
+ }
12401
+
12402
+ // src/session_store.ts
12403
+ var SAFE_RE = /[^a-zA-Z0-9_-]/g;
12404
+ var MAX_FILES = 500;
12405
+ var SESSIONS_SUBDIR = "sessions";
12406
+ function sessionPath(sessionId) {
12407
+ if (!sessionId) return null;
12408
+ const safe = sessionId.replace(SAFE_RE, "_").slice(0, 64);
12409
+ if (!safe) return null;
12410
+ const dir = path10.join(tokenGoatHome(), SESSIONS_SUBDIR);
12411
+ const candidate = path10.join(dir, `${safe}.json`);
12412
+ try {
12413
+ const rel = path10.relative(dir, candidate);
12414
+ if (rel.startsWith("..")) return null;
12415
+ } catch {
12416
+ return null;
12417
+ }
12418
+ return candidate;
12419
+ }
12420
+ function asStringPairs(raw) {
12421
+ if (!Array.isArray(raw)) return [];
12422
+ const out2 = [];
12423
+ for (const item of raw) {
12424
+ if (Array.isArray(item) && typeof item[0] === "string" && typeof item[1] === "string") {
12425
+ out2.push([item[0], item[1]]);
12426
+ }
12427
+ }
12428
+ return out2;
12429
+ }
12430
+ function asFileEntry(raw) {
12431
+ if (raw === null || typeof raw !== "object") return null;
12432
+ const o = raw;
12433
+ if (typeof o["path"] !== "string" || typeof o["readCount"] !== "number" || typeof o["lastReadAt"] !== "number" || typeof o["wasEdited"] !== "boolean" || typeof o["sizeBytes"] !== "number") {
12434
+ return null;
12435
+ }
12436
+ const entry = {
12437
+ path: o["path"],
12438
+ readCount: o["readCount"],
12439
+ lastReadAt: o["lastReadAt"],
12440
+ wasEdited: o["wasEdited"],
12441
+ sizeBytes: o["sizeBytes"]
12442
+ };
12443
+ return o["wasTruncated"] === true ? { ...entry, wasTruncated: true } : entry;
12444
+ }
12445
+ function coerce(raw) {
12446
+ const o = raw !== null && typeof raw === "object" ? raw : {};
12447
+ const files = [];
12448
+ if (Array.isArray(o["files"])) {
12449
+ for (const f of o["files"]) {
12450
+ const e = asFileEntry(f);
12451
+ if (e !== null) files.push(e);
12452
+ }
12453
+ }
12454
+ const hintsShown = Array.isArray(o["hintsShown"]) ? o["hintsShown"].filter((h) => typeof h === "string") : [];
12455
+ return {
12456
+ files,
12457
+ hintsShown,
12458
+ webFetches: asStringPairs(o["webFetches"]),
12459
+ bashOutputs: asStringPairs(o["bashOutputs"]),
12460
+ curlDownloads: asStringPairs(o["curlDownloads"])
12461
+ };
12462
+ }
12463
+ function mergeFileEntry(a, b) {
12464
+ const newest = a.lastReadAt >= b.lastReadAt ? a : b;
12465
+ const merged = {
12466
+ path: a.path,
12467
+ readCount: Math.max(a.readCount, b.readCount),
12468
+ lastReadAt: Math.max(a.lastReadAt, b.lastReadAt),
12469
+ wasEdited: a.wasEdited || b.wasEdited,
12470
+ sizeBytes: newest.sizeBytes
12471
+ };
12472
+ return a.wasTruncated || b.wasTruncated ? { ...merged, wasTruncated: true } : merged;
12473
+ }
12474
+ function mergePairs(disk, mem) {
12475
+ return Array.from(new Map([...disk, ...mem]).entries());
12476
+ }
12477
+ function mergeSessionState(disk, mem) {
12478
+ const byPath = /* @__PURE__ */ new Map();
12479
+ for (const e of disk.files) byPath.set(e.path, e);
12480
+ for (const e of mem.files) {
12481
+ const prev = byPath.get(e.path);
12482
+ byPath.set(e.path, prev ? mergeFileEntry(prev, e) : e);
12483
+ }
12484
+ return {
12485
+ files: Array.from(byPath.values()),
12486
+ hintsShown: Array.from(/* @__PURE__ */ new Set([...disk.hintsShown, ...mem.hintsShown])),
12487
+ webFetches: mergePairs(disk.webFetches, mem.webFetches),
12488
+ bashOutputs: mergePairs(disk.bashOutputs, mem.bashOutputs),
12489
+ curlDownloads: mergePairs(disk.curlDownloads, mem.curlDownloads)
12490
+ };
12491
+ }
12492
+ function capFiles(s, max) {
12493
+ if (s.files.length <= max) return s;
12494
+ const kept = [...s.files].sort((a, b) => b.lastReadAt - a.lastReadAt).slice(0, max);
12495
+ return { ...s, files: kept };
12496
+ }
12497
+ function readDiskState(p) {
12498
+ try {
12499
+ if (!fs7.existsSync(p)) return null;
12500
+ return coerce(JSON.parse(fs7.readFileSync(p, "utf8")));
12501
+ } catch {
12502
+ return null;
12503
+ }
12504
+ }
12505
+ function loadSessionState(sessionId) {
12506
+ const p = sessionPath(sessionId);
12507
+ if (!p) return;
12508
+ const disk = readDiskState(p);
12509
+ if (disk === null) return;
12510
+ importSessionState(disk);
12511
+ }
12512
+ function saveSessionState(sessionId) {
12513
+ const p = sessionPath(sessionId);
12514
+ if (!p) return;
12515
+ try {
12516
+ const mem = exportSessionState();
12517
+ const disk = readDiskState(p);
12518
+ const merged = capFiles(disk ? mergeSessionState(disk, mem) : mem, MAX_FILES);
12519
+ const dir = path10.dirname(p);
12520
+ if (!fs7.existsSync(dir)) fs7.mkdirSync(dir, { recursive: true });
12521
+ atomicWriteText(p, JSON.stringify(merged));
12522
+ } catch {
12523
+ }
12524
+ }
12525
+
12526
+ // src/hooks_read.ts
12527
+ init_define_import_meta_env();
12528
+ import * as fs10 from "node:fs";
12529
+ import * as path13 from "node:path";
12530
+
12230
12531
  // src/hooks_common.ts
12231
12532
  init_define_import_meta_env();
12232
12533
  function getToolName(event) {
@@ -12251,7 +12552,7 @@ function contextOutput(context) {
12251
12552
 
12252
12553
  // src/config.ts
12253
12554
  init_define_import_meta_env();
12254
- import * as fs6 from "node:fs";
12555
+ import * as fs8 from "node:fs";
12255
12556
 
12256
12557
  // node_modules/smol-toml/dist/index.js
12257
12558
  init_define_import_meta_env();
@@ -12886,7 +13187,7 @@ function peekTable(key, table, meta, type) {
12886
13187
  }
12887
13188
  return [k, t, state.c];
12888
13189
  }
12889
- function parse(toml, { maxDepth = 1e3, integersAsBigInt } = {}) {
13190
+ function parse2(toml, { maxDepth = 1e3, integersAsBigInt } = {}) {
12890
13191
  let res = {};
12891
13192
  let meta = {};
12892
13193
  let tbl = res;
@@ -13199,7 +13500,7 @@ function loadConfig() {
13199
13500
  const p = configPath();
13200
13501
  let currentMtime = 0;
13201
13502
  try {
13202
- currentMtime = fs6.statSync(p).mtimeMs;
13503
+ currentMtime = fs8.statSync(p).mtimeMs;
13203
13504
  } catch {
13204
13505
  }
13205
13506
  const envFp = configEnvFingerprint();
@@ -13209,8 +13510,8 @@ function loadConfig() {
13209
13510
  let raw = {};
13210
13511
  if (currentMtime !== 0) {
13211
13512
  try {
13212
- const text = fs6.readFileSync(p, "utf8");
13213
- raw = parse(text);
13513
+ const text = fs8.readFileSync(p, "utf8");
13514
+ raw = parse2(text);
13214
13515
  } catch {
13215
13516
  }
13216
13517
  }
@@ -13419,10 +13720,10 @@ function _buildConfig(raw) {
13419
13720
 
13420
13721
  // src/snapshots.ts
13421
13722
  init_define_import_meta_env();
13422
- import * as fs7 from "node:fs";
13423
- import * as path8 from "node:path";
13723
+ import * as fs9 from "node:fs";
13724
+ import * as path11 from "node:path";
13424
13725
  import * as crypto from "node:crypto";
13425
- import * as os2 from "node:os";
13726
+ import * as os4 from "node:os";
13426
13727
  var MAX_SNAPSHOTS_PER_SESSION = 150;
13427
13728
  var MAX_SNAPSHOT_BYTES = 256 * 1024;
13428
13729
  var SNAPSHOT_TRUNCATE_BYTES = 50 * 1024;
@@ -13434,10 +13735,10 @@ var SESSION_DIR_RE = /[^a-zA-Z0-9_-]/g;
13434
13735
  function sessionDir(sessionId) {
13435
13736
  if (!sessionId) return null;
13436
13737
  const safe = sessionId.replace(SESSION_DIR_RE, "_").slice(0, 64) || "anon";
13437
- const base = path8.join(os2.homedir(), ".token-goat", "session_snapshots");
13438
- const candidate = path8.join(base, safe);
13738
+ const base = path11.join(os4.homedir(), ".token-goat", "session_snapshots");
13739
+ const candidate = path11.join(base, safe);
13439
13740
  try {
13440
- const rel = path8.relative(base, candidate);
13741
+ const rel = path11.relative(base, candidate);
13441
13742
  if (rel.startsWith("..")) return null;
13442
13743
  } catch {
13443
13744
  return null;
@@ -13450,7 +13751,7 @@ function pathKey(filePath) {
13450
13751
  function snapshot_path(sessionId, filePath) {
13451
13752
  const d = sessionDir(sessionId);
13452
13753
  if (!d) return null;
13453
- return path8.join(d, `${pathKey(filePath)}.bin`);
13754
+ return path11.join(d, `${pathKey(filePath)}.bin`);
13454
13755
  }
13455
13756
  function kindSidecarPath(snapshotPath) {
13456
13757
  return snapshotPath + ".kind";
@@ -13458,11 +13759,11 @@ function kindSidecarPath(snapshotPath) {
13458
13759
  function writeSnapshotKind(sidecarPath, kind) {
13459
13760
  try {
13460
13761
  const safeKind = VALID_KINDS.has(kind) ? kind : KIND_READ;
13461
- const dir = path8.dirname(sidecarPath);
13462
- if (!fs7.existsSync(dir)) {
13463
- fs7.mkdirSync(dir, { recursive: true });
13762
+ const dir = path11.dirname(sidecarPath);
13763
+ if (!fs9.existsSync(dir)) {
13764
+ fs9.mkdirSync(dir, { recursive: true });
13464
13765
  }
13465
- fs7.writeFileSync(sidecarPath, safeKind, "utf8");
13766
+ fs9.writeFileSync(sidecarPath, safeKind, "utf8");
13466
13767
  return true;
13467
13768
  } catch {
13468
13769
  return false;
@@ -13471,12 +13772,12 @@ function writeSnapshotKind(sidecarPath, kind) {
13471
13772
  function evictOldest(d, maxCount) {
13472
13773
  try {
13473
13774
  const entries = [];
13474
- const files = fs7.readdirSync(d);
13775
+ const files = fs9.readdirSync(d);
13475
13776
  for (const file of files) {
13476
- const fullPath = path8.join(d, file);
13777
+ const fullPath = path11.join(d, file);
13477
13778
  if (!file.endsWith(".bin")) continue;
13478
13779
  try {
13479
- const stat3 = fs7.statSync(fullPath);
13780
+ const stat3 = fs9.statSync(fullPath);
13480
13781
  entries.push([fullPath, stat3.mtimeMs]);
13481
13782
  } catch {
13482
13783
  continue;
@@ -13488,10 +13789,10 @@ function evictOldest(d, maxCount) {
13488
13789
  const over = entries.length - maxCount;
13489
13790
  for (const [p] of entries.slice(0, over)) {
13490
13791
  try {
13491
- fs7.unlinkSync(p);
13792
+ fs9.unlinkSync(p);
13492
13793
  removed++;
13493
13794
  try {
13494
- fs7.unlinkSync(kindSidecarPath(p));
13795
+ fs9.unlinkSync(kindSidecarPath(p));
13495
13796
  } catch {
13496
13797
  }
13497
13798
  } catch {
@@ -13520,9 +13821,9 @@ function store(sessionId, filePath, content, opts = {}) {
13520
13821
  if (!p) return null;
13521
13822
  const sha = crypto.createHash("sha256").update(stored).digest("hex");
13522
13823
  try {
13523
- if (fs7.existsSync(p)) {
13824
+ if (fs9.existsSync(p)) {
13524
13825
  try {
13525
- const existing = fs7.readFileSync(p);
13826
+ const existing = fs9.readFileSync(p);
13526
13827
  if (Buffer.from(existing).equals(stored)) {
13527
13828
  return {
13528
13829
  path: p,
@@ -13533,14 +13834,14 @@ function store(sessionId, filePath, content, opts = {}) {
13533
13834
  } catch {
13534
13835
  }
13535
13836
  }
13536
- const dir = path8.dirname(p);
13537
- if (!fs7.existsSync(dir)) {
13538
- fs7.mkdirSync(dir, { recursive: true });
13837
+ const dir = path11.dirname(p);
13838
+ if (!fs9.existsSync(dir)) {
13839
+ fs9.mkdirSync(dir, { recursive: true });
13539
13840
  }
13540
13841
  evictOldest(dir, MAX_SNAPSHOTS_PER_SESSION - 1);
13541
13842
  const tempPath = p + ".tmp";
13542
- fs7.writeFileSync(tempPath, stored);
13543
- fs7.renameSync(tempPath, p);
13843
+ fs9.writeFileSync(tempPath, stored);
13844
+ fs9.renameSync(tempPath, p);
13544
13845
  const sidecar = kindSidecarPath(p);
13545
13846
  writeSnapshotKind(sidecar, kind);
13546
13847
  return {
@@ -13554,9 +13855,9 @@ function store(sessionId, filePath, content, opts = {}) {
13554
13855
  }
13555
13856
  function load(sessionId, filePath, opts = {}) {
13556
13857
  const p = snapshot_path(sessionId, filePath);
13557
- if (!p || !fs7.existsSync(p)) return null;
13858
+ if (!p || !fs9.existsSync(p)) return null;
13558
13859
  try {
13559
- const stat3 = fs7.statSync(p);
13860
+ const stat3 = fs9.statSync(p);
13560
13861
  if (stat3.size > MAX_SNAPSHOT_BYTES) {
13561
13862
  return null;
13562
13863
  }
@@ -13564,7 +13865,7 @@ function load(sessionId, filePath, opts = {}) {
13564
13865
  return null;
13565
13866
  }
13566
13867
  try {
13567
- const data = fs7.readFileSync(p);
13868
+ const data = fs9.readFileSync(p);
13568
13869
  if (opts.expected_sha) {
13569
13870
  const actualSha = crypto.createHash("sha256").update(data).digest("hex");
13570
13871
  if (actualSha.toLowerCase() !== opts.expected_sha.toLowerCase()) {
@@ -13609,11 +13910,11 @@ function buildPackageManifestHint(options) {
13609
13910
  return null;
13610
13911
  }
13611
13912
  }
13612
- function _sanitizeHintPath(path19) {
13613
- if (typeof path19 !== "string") {
13913
+ function _sanitizeHintPath(path22) {
13914
+ if (typeof path22 !== "string") {
13614
13915
  return "???";
13615
13916
  }
13616
- return path19.replace(/[\x00]/g, "").slice(0, 200);
13917
+ return path22.replace(/[\x00]/g, "").slice(0, 200);
13617
13918
  }
13618
13919
 
13619
13920
  // src/hints/lang_patterns.ts
@@ -13751,15 +14052,15 @@ var LOCK_FILE_COUNT = LOCK_FILE_NAMES.size;
13751
14052
  var MANIFEST_FILE_COUNT = MANIFEST_FILE_NAMES.size + MANIFEST_EXTENSIONS.size + MANIFEST_BASENAME_PATTERNS.length;
13752
14053
  var BUILD_DIR_COUNT = BUILD_DIR_NAMES.size;
13753
14054
  var GENERATED_EXT_COUNT = ALWAYS_GENERATED_EXTS.size + CONDITIONALLY_GENERATED_EXTS.size;
13754
- function isLockFile(basename8) {
13755
- return LOCK_FILE_NAMES.has(basename8.toLowerCase());
14055
+ function isLockFile(basename9) {
14056
+ return LOCK_FILE_NAMES.has(basename9.toLowerCase());
13756
14057
  }
13757
- function isManifestFile(basename8) {
13758
- const lower = basename8.toLowerCase();
14058
+ function isManifestFile(basename9) {
14059
+ const lower = basename9.toLowerCase();
13759
14060
  if (MANIFEST_FILE_NAMES.has(lower)) return true;
13760
14061
  const dot = lower.lastIndexOf(".");
13761
14062
  if (dot !== -1 && MANIFEST_EXTENSIONS.has(lower.slice(dot))) return true;
13762
- if (MANIFEST_BASENAME_PATTERNS.some((re) => re.test(basename8))) return true;
14063
+ if (MANIFEST_BASENAME_PATTERNS.some((re) => re.test(basename9))) return true;
13763
14064
  return false;
13764
14065
  }
13765
14066
  function pathSegments(filePath) {
@@ -13917,8 +14218,8 @@ var WELL_KNOWN_SECTIONS = {
13917
14218
  "CLAUDE.md": ["Commands", "Architecture"],
13918
14219
  "CLAUDE.arch.md": ["Component Map", "Architecture"]
13919
14220
  };
13920
- function getWellKnownSections(basename8) {
13921
- return WELL_KNOWN_SECTIONS[basename8] ?? [];
14221
+ function getWellKnownSections(basename9) {
14222
+ return WELL_KNOWN_SECTIONS[basename9] ?? [];
13922
14223
  }
13923
14224
  function extractChangelogVersionHint(content, filePath) {
13924
14225
  const lines = content.split("\n");
@@ -14066,7 +14367,7 @@ function dispatchFileTypeHandler(filePath, content, contentLengthHint) {
14066
14367
 
14067
14368
  // src/stats.ts
14068
14369
  init_define_import_meta_env();
14069
- import * as path9 from "node:path";
14370
+ import * as path12 from "node:path";
14070
14371
 
14071
14372
  // src/render/stats_renderer.ts
14072
14373
  init_define_import_meta_env();
@@ -14846,7 +15147,7 @@ CREATE INDEX IF NOT EXISTS idx_stats_kind ON stats(kind);
14846
15147
  var _globalSchemaApplied = /* @__PURE__ */ new Set();
14847
15148
  registerReset(() => _globalSchemaApplied.clear());
14848
15149
  function getGlobalDb() {
14849
- const dbPath = path9.join(dataDir(), "global.db");
15150
+ const dbPath = path12.join(dataDir(), "global.db");
14850
15151
  const db = getDb(dbPath);
14851
15152
  if (!_globalSchemaApplied.has(dbPath)) {
14852
15153
  db.exec(GLOBAL_SCHEMA_SQL);
@@ -15038,8 +15339,8 @@ function renderStats2(opts) {
15038
15339
  }
15039
15340
 
15040
15341
  // src/hooks_read.ts
15041
- function isTsConfigFile(basename8) {
15042
- const lower = basename8.toLowerCase();
15342
+ function isTsConfigFile(basename9) {
15343
+ const lower = basename9.toLowerCase();
15043
15344
  return /^tsconfig(\..+)?\.json$/i.test(lower) || lower === "jsconfig.json";
15044
15345
  }
15045
15346
  var LARGE_FILE_BYTES = 100 * 1024;
@@ -15058,17 +15359,20 @@ function isSessionArtifactFile(filePath) {
15058
15359
  if (/[/\\]tool-results[/\\][a-z0-9]+\.txt$/i.test(filePath)) return true;
15059
15360
  return false;
15060
15361
  }
15362
+ function sessionArtifactRecall(filePath) {
15363
+ return 'Use `token-goat bash-output --file "' + filePath + '" --tail 50` (or `--grep PATTERN`) to read a slice instead of the full file.';
15364
+ }
15061
15365
  function statSize(absPath) {
15062
15366
  try {
15063
- return fs8.statSync(absPath).size;
15367
+ return fs10.statSync(absPath).size;
15064
15368
  } catch {
15065
15369
  return null;
15066
15370
  }
15067
15371
  }
15068
15372
  var DIFFABLE_SOURCE_RE = /\.(ts|tsx|js|jsx|mjs|cjs|css|scss|sass|less|json|jsonc|py|go|rs|java|rb|php|swift|kt|c|h|cpp|cc|cxx|hpp|cs|sql|yaml|yml|toml)$/i;
15069
- function surgicalHint(filePath, basename8) {
15070
- const isDocFile = /\.(md|mdx|rst|txt)$/i.test(basename8);
15071
- const isSectionFile = /\.(json|jsonc|css|scss|sass|less|yaml|yml|toml)$/i.test(basename8);
15373
+ function surgicalHint(filePath, basename9) {
15374
+ const isDocFile = /\.(md|mdx|rst|txt)$/i.test(basename9);
15375
+ const isSectionFile = /\.(json|jsonc|css|scss|sass|less|yaml|yml|toml)$/i.test(basename9);
15072
15376
  if (isDocFile) {
15073
15377
  return 'Use `token-goat section "' + filePath + '::HeadingName"` to extract a part.';
15074
15378
  } else if (isSectionFile) {
@@ -15119,8 +15423,8 @@ function preReadHandler(event) {
15119
15423
  "node_modules is typically noise; use npm ls, npm outdated, or npm audit instead for dependency info. To force access, use: token-goat read node_modules/package/file.js::symbol-name or token-goat section node_modules/package/file.js::heading"
15120
15424
  );
15121
15425
  }
15122
- const basename8 = path10.basename(normalized);
15123
- if (isLockFile(basename8)) {
15426
+ const basename9 = path13.basename(normalized);
15427
+ if (isLockFile(basename9)) {
15124
15428
  return denyOutput(
15125
15429
  'Lock files are rarely useful to read in full. Use `token-goat section "' + normalized + '::<section>"` to extract a specific dependency, or read the relevant manifest instead.'
15126
15430
  );
@@ -15140,25 +15444,25 @@ function preReadHandler(event) {
15140
15444
  recordFileRead(normalized);
15141
15445
  return contextOutput(manifestHint.text);
15142
15446
  }
15143
- if (isTsConfigFile(basename8) && wasFileReadThisSession(normalized)) {
15447
+ if (isTsConfigFile(basename9) && wasFileReadThisSession(normalized)) {
15144
15448
  recordFileRead(normalized);
15145
15449
  return contextOutput(
15146
- "Already read " + basename8 + '. Use `token-goat section "' + normalized + '::compilerOptions"` to extract compiler options, or `token-goat config-get ' + normalized + " compilerOptions.target` for a single value."
15450
+ "Already read " + basename9 + '. Use `token-goat section "' + normalized + '::compilerOptions"` to extract compiler options, or `token-goat config-get ' + normalized + " compilerOptions.target` for a single value."
15147
15451
  );
15148
15452
  }
15149
- if (isManifestFile(basename8) && wasFileReadThisSession(normalized)) {
15453
+ if (isManifestFile(basename9) && wasFileReadThisSession(normalized)) {
15150
15454
  recordFileRead(normalized);
15151
15455
  return contextOutput(
15152
- "You've already read " + basename8 + '. Use `token-goat section "' + normalized + '::<field>"` or `token-goat config-get ' + normalized + " <key>` to extract just the value you need."
15456
+ "You've already read " + basename9 + '. Use `token-goat section "' + normalized + '::<field>"` or `token-goat config-get ' + normalized + " <key>` to extract just the value you need."
15153
15457
  );
15154
15458
  }
15155
- const isMarkdown = /\.(md|mdx|markdown|rst)$/i.test(basename8);
15459
+ const isMarkdown = /\.(md|mdx|markdown|rst)$/i.test(basename9);
15156
15460
  if (isMarkdown) {
15157
15461
  let fileContent = null;
15158
15462
  try {
15159
15463
  const sz = statSize(normalized);
15160
15464
  if (sz !== null && sz >= MARKDOWN_SIZE_THRESHOLD) {
15161
- fileContent = fs8.readFileSync(normalized, "utf8");
15465
+ fileContent = fs10.readFileSync(normalized, "utf8");
15162
15466
  }
15163
15467
  } catch {
15164
15468
  }
@@ -15167,9 +15471,9 @@ function preReadHandler(event) {
15167
15471
  if (headings.length >= 3) {
15168
15472
  recordFileRead(normalized);
15169
15473
  const hintText = formatHeadingTree(headings, normalized);
15170
- const wellKnown = getWellKnownSections(basename8);
15474
+ const wellKnown = getWellKnownSections(basename9);
15171
15475
  const wellKnownText = wellKnown.length > 0 ? "\nQuick access: " + wellKnown.map((s) => 'token-goat section "' + normalized + "::" + s + '"').join(" | ") : "";
15172
- const changelogExtra = basename8.toLowerCase() === "changelog.md" ? extractChangelogVersionHint(fileContent, normalized) : "";
15476
+ const changelogExtra = basename9.toLowerCase() === "changelog.md" ? extractChangelogVersionHint(fileContent, normalized) : "";
15173
15477
  return denyOutput(hintText + wellKnownText + changelogExtra);
15174
15478
  }
15175
15479
  }
@@ -15178,19 +15482,19 @@ function preReadHandler(event) {
15178
15482
  if (isMemoryMd && wasFileReadThisSession(normalized)) {
15179
15483
  recordFileRead(normalized);
15180
15484
  recordStat("session_hint", 0, 0);
15181
- const isMainMemory = basename8.toLowerCase() === "memory.md";
15485
+ const isMainMemory = basename9.toLowerCase() === "memory.md";
15182
15486
  return denyOutput(
15183
15487
  isMainMemory ? "MEMORY.md was read this session. Its content is in the compact manifest as 'session memory'." : normalized + ' was already read this session. Memory files rarely change mid-session. Use `token-goat section "' + normalized + '::SectionHeading"` to extract one section.'
15184
15488
  );
15185
15489
  }
15186
- if (/^\.improve-state-.*\.json$/.test(basename8) && wasFileReadThisSession(normalized)) {
15490
+ if (/^\.improve-state-.*\.json$/.test(basename9) && wasFileReadThisSession(normalized)) {
15187
15491
  recordFileRead(normalized);
15188
15492
  recordStat("session_hint", 0, 0);
15189
15493
  return denyOutput(
15190
15494
  "Orchestrator state already read this session. Use `token-goat bash-output <id>` or recall it from the compact manifest."
15191
15495
  );
15192
15496
  }
15193
- if (/^\.env(\.\w+)?$/.test(basename8) && wasFileReadThisSession(normalized)) {
15497
+ if (/^\.env(\.\w+)?$/.test(basename9) && wasFileReadThisSession(normalized)) {
15194
15498
  recordFileRead(normalized);
15195
15499
  recordStat("session_hint", 0, 0);
15196
15500
  return denyOutput(
@@ -15203,7 +15507,7 @@ function preReadHandler(event) {
15203
15507
  recordFileRead(normalized);
15204
15508
  recordStat("session_hint", 0, 0);
15205
15509
  return denyOutput(
15206
- "File was truncated on last read. Use `token-goat bash-output --tail N` or `--grep PATTERN` to read a slice."
15510
+ "File was truncated on last read. " + sessionArtifactRecall(normalized)
15207
15511
  );
15208
15512
  }
15209
15513
  const artifactSessionId = getSessionId();
@@ -15212,7 +15516,7 @@ function preReadHandler(event) {
15212
15516
  try {
15213
15517
  const sz = statSize(normalized);
15214
15518
  if (sz !== null && sz <= 256 * 1024) {
15215
- const currentContent = fs8.readFileSync(normalized, "utf8");
15519
+ const currentContent = fs10.readFileSync(normalized, "utf8");
15216
15520
  const TRUNC_MARKER = "\n<snapshot truncated at ";
15217
15521
  const oldRaw = oldArtifactSnap.toString("utf8");
15218
15522
  const truncIdx = oldRaw.indexOf(TRUNC_MARKER);
@@ -15221,16 +15525,16 @@ function preReadHandler(event) {
15221
15525
  recordFileRead(normalized);
15222
15526
  recordStat("session_hint", 0, 0);
15223
15527
  return denyOutput(
15224
- basename8 + " is unchanged since last read. Use `token-goat bash-output --tail N` or `--grep PATTERN` to read a slice."
15528
+ basename9 + " is unchanged since last read. " + sessionArtifactRecall(normalized)
15225
15529
  );
15226
15530
  }
15227
- const diff = buildLineDiff(oldContent, currentContent, basename8);
15531
+ const diff = buildLineDiff(oldContent, currentContent, basename9);
15228
15532
  if (diff !== "") {
15229
15533
  recordFileRead(normalized);
15230
15534
  const savedBytes = Math.max(0, currentContent.length - diff.length);
15231
15535
  recordStat("session_hint", savedBytes, Math.round(savedBytes / 4));
15232
15536
  return denyOutput(
15233
- "Content changed since last read of " + basename8 + ". Here is what changed:\n\n```diff\n" + diff + "\n```\n\nUse `token-goat bash-output --tail N` or `--grep PATTERN` to read a slice."
15537
+ "Content changed since last read of " + basename9 + ". Here is what changed:\n\n```diff\n" + diff + "\n```\n\n" + sessionArtifactRecall(normalized)
15234
15538
  );
15235
15539
  }
15236
15540
  }
@@ -15240,18 +15544,16 @@ function preReadHandler(event) {
15240
15544
  recordFileRead(normalized);
15241
15545
  recordStat("session_hint", 0, 0);
15242
15546
  return denyOutput(
15243
- normalized + " was already read this session. Use `token-goat bash-output --tail N` or `--grep PATTERN` to read a slice."
15547
+ normalized + " was already read this session. " + sessionArtifactRecall(normalized)
15244
15548
  );
15245
15549
  }
15246
15550
  if (/[/\\]tasks[/\\][a-z0-9]+\.output$/i.test(normalized)) {
15247
15551
  recordFileRead(normalized);
15248
- return contextOutput(
15249
- "Session transcript: use `token-goat bash-output --tail N` or `--grep PATTERN` to read a slice instead of the full file."
15250
- );
15552
+ return contextOutput("Session transcript: " + sessionArtifactRecall(normalized));
15251
15553
  }
15252
15554
  }
15253
- const isDocDiffable = /\.(md|mdx|rst|txt)$/i.test(basename8);
15254
- const isSourceDiffable = loadConfig().hints.serve_diff_on_reread && DIFFABLE_SOURCE_RE.test(basename8);
15555
+ const isDocDiffable = /\.(md|mdx|rst|txt)$/i.test(basename9);
15556
+ const isSourceDiffable = loadConfig().hints.serve_diff_on_reread && DIFFABLE_SOURCE_RE.test(basename9);
15255
15557
  if ((isDocDiffable || isSourceDiffable) && wasFileReadThisSession(normalized)) {
15256
15558
  if (wasFileTruncatedThisSession(normalized)) {
15257
15559
  recordFileRead(normalized);
@@ -15266,7 +15568,7 @@ function preReadHandler(event) {
15266
15568
  try {
15267
15569
  const sz = statSize(normalized);
15268
15570
  if (sz !== null && sz <= 256 * 1024) {
15269
- const currentContent = fs8.readFileSync(normalized, "utf8");
15571
+ const currentContent = fs10.readFileSync(normalized, "utf8");
15270
15572
  const TRUNC_MARKER = "\n<snapshot truncated at ";
15271
15573
  const oldRaw = oldSnap.toString("utf8");
15272
15574
  const truncIdx = oldRaw.indexOf(TRUNC_MARKER);
@@ -15275,10 +15577,10 @@ function preReadHandler(event) {
15275
15577
  recordFileRead(normalized);
15276
15578
  recordStat("session_hint", 0, 0);
15277
15579
  return denyOutput(
15278
- basename8 + " is unchanged since last read. " + surgicalHint(normalized, basename8)
15580
+ basename9 + " is unchanged since last read. " + surgicalHint(normalized, basename9)
15279
15581
  );
15280
15582
  }
15281
- const diff = buildLineDiff(oldContent, currentContent, basename8);
15583
+ const diff = buildLineDiff(oldContent, currentContent, basename9);
15282
15584
  if (diff !== "") {
15283
15585
  if (isSourceDiffable && !isDocDiffable && diff.length > currentContent.length * 0.6) {
15284
15586
  } else {
@@ -15286,7 +15588,7 @@ function preReadHandler(event) {
15286
15588
  const savedBytes = Math.max(0, currentContent.length - diff.length);
15287
15589
  recordStat("session_hint", savedBytes, Math.round(savedBytes / 4));
15288
15590
  return denyOutput(
15289
- "Content changed since last read of " + basename8 + ". Here is what changed:\n\n```diff\n" + diff + "\n```\n\n" + surgicalHint(normalized, basename8)
15591
+ "Content changed since last read of " + basename9 + ". Here is what changed:\n\n```diff\n" + diff + "\n```\n\n" + surgicalHint(normalized, basename9)
15290
15592
  );
15291
15593
  }
15292
15594
  }
@@ -15298,7 +15600,7 @@ function preReadHandler(event) {
15298
15600
  if (wasFileReadThisSession(normalized)) {
15299
15601
  const entry = getSessionFiles().get(normalized);
15300
15602
  const reads = entry?.readCount ?? 1;
15301
- const plural = reads === 1 ? "read" : "reads";
15603
+ const plural2 = reads === 1 ? "read" : "reads";
15302
15604
  recordFileRead(normalized);
15303
15605
  const rereadBytes = statSize(normalized) ?? 0;
15304
15606
  recordStat("session_hint", rereadBytes, Math.round(rereadBytes / 4));
@@ -15307,12 +15609,12 @@ function preReadHandler(event) {
15307
15609
  'File was truncated on last read (>33K tokens). Use `token-goat skeleton "' + normalized + '"` for structure or `token-goat read "' + normalized + '::SymbolName"` for one function.'
15308
15610
  );
15309
15611
  }
15310
- if (/\.(md|mdx)$/i.test(basename8)) {
15612
+ if (/\.(md|mdx)$/i.test(basename9)) {
15311
15613
  return denyOutput(
15312
15614
  'Markdown file already read this session. Use `token-goat section "' + normalized + '::HeadingName"` to read one section.'
15313
15615
  );
15314
15616
  }
15315
- const isSourceExt = /\.(ts|tsx|js|jsx|py|go|rs|java|rb|php|swift|kt|cpp|c|h)$/i.test(basename8);
15617
+ const isSourceExt = /\.(ts|tsx|js|jsx|py|go|rs|java|rb|php|swift|kt|cpp|c|h)$/i.test(basename9);
15316
15618
  if (isSourceExt && reads >= 2) {
15317
15619
  recordStat("read_count_deny", rereadBytes, Math.round(rereadBytes / 4));
15318
15620
  return denyOutput(
@@ -15322,11 +15624,11 @@ function preReadHandler(event) {
15322
15624
  const hint = _isDocFile(normalized) ? 'Use `token-goat section "' + normalized + '::SectionName"` to read one section.' : "Use token-goat read/section/symbol to re-read surgically.";
15323
15625
  if (rereadBytes >= REREAD_DENY_BYTES || reads >= 2) {
15324
15626
  return denyOutput(
15325
- normalized + " was already read this session (" + reads + " " + plural + "). " + hint
15627
+ normalized + " was already read this session (" + reads + " " + plural2 + "). " + hint
15326
15628
  );
15327
15629
  }
15328
15630
  return contextOutput(
15329
- "Note: " + normalized + " was already read this session (" + reads + " " + plural + "). " + hint
15631
+ "Note: " + normalized + " was already read this session (" + reads + " " + plural2 + "). " + hint
15330
15632
  );
15331
15633
  }
15332
15634
  const size = statSize(normalized);
@@ -15344,7 +15646,7 @@ function preReadHandler(event) {
15344
15646
  "Note: " + normalized + " is large (" + kb + "KB). " + hint
15345
15647
  );
15346
15648
  }
15347
- const fileTypeExt = path10.extname(normalized).slice(1).toLowerCase();
15649
+ const fileTypeExt = path13.extname(normalized).slice(1).toLowerCase();
15348
15650
  const binaryExts = /* @__PURE__ */ new Set(["pdf", "docx", "xlsx", "pptx", "odt", "ods", "ott", "odp"]);
15349
15651
  const textTypeExts = /* @__PURE__ */ new Set(["html", "htm", "xhtml", "txt", "log", "out", "err", "trace", "csv", "tsv"]);
15350
15652
  const fileStatSize = size ?? statSize(normalized) ?? 0;
@@ -15353,7 +15655,7 @@ function preReadHandler(event) {
15353
15655
  let ftContent = "";
15354
15656
  if (!binaryExts.has(fileTypeExt)) {
15355
15657
  try {
15356
- ftContent = fs8.readFileSync(normalized, "utf8");
15658
+ ftContent = fs10.readFileSync(normalized, "utf8");
15357
15659
  } catch {
15358
15660
  }
15359
15661
  }
@@ -15387,13 +15689,13 @@ function postReadHandler(event) {
15387
15689
  if (respText.includes("[Truncated:") || respText.includes("Truncated: PARTIAL view")) {
15388
15690
  markFileTruncated(normalized);
15389
15691
  }
15390
- const postBasename = path10.basename(normalized);
15692
+ const postBasename = path13.basename(normalized);
15391
15693
  const diffSourcesEnabled = loadConfig().hints.serve_diff_on_reread;
15392
15694
  if (/\.(md|mdx|rst|txt)$/i.test(postBasename) || isSessionArtifactFile(normalized) || diffSourcesEnabled && DIFFABLE_SOURCE_RE.test(postBasename)) {
15393
15695
  try {
15394
15696
  const sz = statSize(normalized);
15395
15697
  if (sz !== null && sz <= 256 * 1024) {
15396
- const content = fs8.readFileSync(normalized);
15698
+ const content = fs10.readFileSync(normalized);
15397
15699
  store(getSessionId(), normalized, content);
15398
15700
  }
15399
15701
  } catch {
@@ -15405,31 +15707,31 @@ registerHook("post_tool_use", postReadHandler, { toolName: "Read" });
15405
15707
 
15406
15708
  // src/hooks_edit.ts
15407
15709
  init_define_import_meta_env();
15408
- import * as path12 from "node:path";
15710
+ import * as path15 from "node:path";
15409
15711
 
15410
15712
  // src/hooks_index.ts
15411
15713
  init_define_import_meta_env();
15412
- import * as fs9 from "node:fs";
15413
- import * as path11 from "node:path";
15714
+ import * as fs11 from "node:fs";
15715
+ import * as path14 from "node:path";
15414
15716
  function dirtyQueuePath() {
15415
- return path11.join(dataDir(), "queue", "dirty.txt");
15717
+ return path14.join(dataDir(), "queue", "dirty.txt");
15416
15718
  }
15417
15719
  function appendDirtyPath(normalizedPath) {
15418
15720
  const queuePath = dirtyQueuePath();
15419
- const dir = path11.dirname(queuePath);
15721
+ const dir = path14.dirname(queuePath);
15420
15722
  try {
15421
- fs9.mkdirSync(dir, { recursive: true });
15723
+ fs11.mkdirSync(dir, { recursive: true });
15422
15724
  } catch (e) {
15423
- if (e.code !== "EEXIST" || !fs9.existsSync(dir)) throw e;
15725
+ if (e.code !== "EEXIST" || !fs11.existsSync(dir)) throw e;
15424
15726
  }
15425
- fs9.appendFileSync(queuePath, `${normalizedPath}
15727
+ fs11.appendFileSync(queuePath, `${normalizedPath}
15426
15728
  `);
15427
15729
  }
15428
15730
  function getDirtyPaths() {
15429
15731
  const queuePath = dirtyQueuePath();
15430
15732
  let raw;
15431
15733
  try {
15432
- raw = fs9.readFileSync(queuePath, "utf8");
15734
+ raw = fs11.readFileSync(queuePath, "utf8");
15433
15735
  } catch {
15434
15736
  return [];
15435
15737
  }
@@ -15445,16 +15747,16 @@ function getDirtyPaths() {
15445
15747
  }
15446
15748
  function clearDirtyQueue() {
15447
15749
  try {
15448
- fs9.rmSync(dirtyQueuePath(), { force: true });
15750
+ fs11.rmSync(dirtyQueuePath(), { force: true });
15449
15751
  } catch {
15450
15752
  }
15451
15753
  }
15452
15754
  function preCompactIndexHandler(_event) {
15453
15755
  const paths = getDirtyPaths();
15454
15756
  if (paths.length > 0) {
15455
- const sidecar = path11.join(dataDir(), "queue", "pending.txt");
15757
+ const sidecar = path14.join(dataDir(), "queue", "pending.txt");
15456
15758
  try {
15457
- fs9.mkdirSync(path11.dirname(sidecar), { recursive: true });
15759
+ fs11.mkdirSync(path14.dirname(sidecar), { recursive: true });
15458
15760
  atomicWriteBytes(sidecar, Buffer.from(`${paths.join("\n")}
15459
15761
  `, "utf8"));
15460
15762
  clearDirtyQueue();
@@ -15472,7 +15774,7 @@ function postEditHandler(event) {
15472
15774
  const normalized = normalizePath(filePath);
15473
15775
  recordFileEdit(normalized);
15474
15776
  appendDirtyPath(normalized);
15475
- const editedBasename = path12.basename(normalized);
15777
+ const editedBasename = path15.basename(normalized);
15476
15778
  if (/\.(md|mdx|markdown|rst)$/i.test(editedBasename)) {
15477
15779
  const escapedPath = normalized.replace(/`/g, "\\`");
15478
15780
  return contextOutput(
@@ -15489,9 +15791,9 @@ init_define_import_meta_env();
15489
15791
  var MAX_ROWS = 40;
15490
15792
  function renderReadRow(entry) {
15491
15793
  const kb = Math.max(1, Math.round(entry.sizeBytes / 1024));
15492
- const plural = entry.readCount === 1 ? "read" : "reads";
15794
+ const plural2 = entry.readCount === 1 ? "read" : "reads";
15493
15795
  const edited = entry.wasEdited ? ", edited" : "";
15494
- return `- ${entry.path} (${kb}kb, ${entry.readCount} ${plural}${edited})`;
15796
+ return `- ${entry.path} (${kb}kb, ${entry.readCount} ${plural2}${edited})`;
15495
15797
  }
15496
15798
  function buildManifest() {
15497
15799
  const files = [...getSessionFiles().values()];
@@ -15620,6 +15922,50 @@ registerHook("subagent_stop", subagentStopHandler);
15620
15922
 
15621
15923
  // src/hooks_fetch.ts
15622
15924
  init_define_import_meta_env();
15925
+
15926
+ // src/web_cache.ts
15927
+ init_define_import_meta_env();
15928
+ var WEB_OUTPUT_SUBDIR = "web_outputs";
15929
+ var _byId = /* @__PURE__ */ new Map();
15930
+ var _urlIndex = /* @__PURE__ */ new Map();
15931
+ function cacheIdForUrl(url) {
15932
+ return shortFingerprint(url);
15933
+ }
15934
+ function storeWebOutput(url, content) {
15935
+ const cacheId = cacheIdForUrl(url);
15936
+ _byId.set(cacheId, content);
15937
+ _urlIndex.set(url, cacheId);
15938
+ storeBlob(WEB_OUTPUT_SUBDIR, cacheId, { url, content });
15939
+ return cacheId;
15940
+ }
15941
+ function coerceWebBlob(raw) {
15942
+ if (raw === null || typeof raw !== "object") return null;
15943
+ const o = raw;
15944
+ if (typeof o["content"] !== "string") return null;
15945
+ return { url: typeof o["url"] === "string" ? o["url"] : null, content: o["content"] };
15946
+ }
15947
+ function getWebOutput(cacheId) {
15948
+ const hit = _byId.get(cacheId);
15949
+ if (hit !== void 0) return hit;
15950
+ const blob = coerceWebBlob(loadBlob(WEB_OUTPUT_SUBDIR, cacheId));
15951
+ if (blob === null) return null;
15952
+ _byId.set(cacheId, blob.content);
15953
+ if (blob.url !== null) _urlIndex.set(blob.url, cacheId);
15954
+ return blob.content;
15955
+ }
15956
+ function getWebOutputByUrl(url) {
15957
+ const cacheId = _urlIndex.get(url);
15958
+ if (cacheId === void 0) return null;
15959
+ const content = _byId.get(cacheId);
15960
+ if (content === void 0) return null;
15961
+ return { cacheId, content };
15962
+ }
15963
+ registerReset(() => {
15964
+ _byId = /* @__PURE__ */ new Map();
15965
+ _urlIndex = /* @__PURE__ */ new Map();
15966
+ });
15967
+
15968
+ // src/hooks_fetch.ts
15623
15969
  function extractToolResponse(raw) {
15624
15970
  const toolResponse = raw["tool_response"];
15625
15971
  if (typeof toolResponse === "string") {
@@ -15670,6 +16016,8 @@ function postFetchHandler(event) {
15670
16016
  if (!body || body.length < 1024) {
15671
16017
  return passOutput();
15672
16018
  }
16019
+ const cacheId = storeWebOutput(url, body);
16020
+ recordWebFetch(url, cacheId);
15673
16021
  return passOutput();
15674
16022
  } catch {
15675
16023
  return passOutput();
@@ -15754,9 +16102,10 @@ init_define_import_meta_env();
15754
16102
 
15755
16103
  // src/bash_output_cache.ts
15756
16104
  init_define_import_meta_env();
15757
- import * as fs10 from "fs/promises";
15758
- import { resolve as resolve4 } from "path";
15759
- var _byId = /* @__PURE__ */ new Map();
16105
+ import * as fs12 from "fs/promises";
16106
+ import { resolve as resolve5 } from "path";
16107
+ var BASH_OUTPUT_SUBDIR = "bash_outputs";
16108
+ var _byId2 = /* @__PURE__ */ new Map();
15760
16109
  var _globsByKey = /* @__PURE__ */ new Map();
15761
16110
  var _grepsByKey = /* @__PURE__ */ new Map();
15762
16111
  var COMMAND_PATTERNS = {
@@ -15797,7 +16146,7 @@ async function gitStateFingerprint(cwd) {
15797
16146
  const headSha = headResult.stdout.trim();
15798
16147
  let indexMtime = "";
15799
16148
  try {
15800
- const stat3 = await fs10.stat(resolve4(cwd, ".git", "index"));
16149
+ const stat3 = await fs12.stat(resolve5(cwd, ".git", "index"));
15801
16150
  indexMtime = stat3.mtimeMs.toString();
15802
16151
  } catch {
15803
16152
  }
@@ -15807,9 +16156,9 @@ async function gitStateFingerprint(cwd) {
15807
16156
  return null;
15808
16157
  }
15809
16158
  }
15810
- async function dirStateFingerprint(path19) {
16159
+ async function dirStateFingerprint(path22) {
15811
16160
  try {
15812
- const stat3 = await fs10.stat(path19);
16161
+ const stat3 = await fs12.stat(path22);
15813
16162
  if (!stat3.isDirectory()) return null;
15814
16163
  return shortFingerprint(stat3.mtimeMs.toString());
15815
16164
  } catch {
@@ -15825,7 +16174,7 @@ async function depLockfileFingerprint(cmd, cwd) {
15825
16174
  if (!candidates) return null;
15826
16175
  for (const lockfile of candidates) {
15827
16176
  try {
15828
- const content = await fs10.readFile(resolve4(cwd, lockfile));
16177
+ const content = await fs12.readFile(resolve5(cwd, lockfile));
15829
16178
  return shortFingerprint(content);
15830
16179
  } catch {
15831
16180
  continue;
@@ -15895,78 +16244,1195 @@ async function storeBashOutput(command, output, exitCode, cwd = null) {
15895
16244
  storedAt: Date.now(),
15896
16245
  sizeBytes: Buffer.byteLength(output, "utf-8")
15897
16246
  };
15898
- _byId.set(id, entry);
16247
+ _byId2.set(id, entry);
16248
+ storeBlob(BASH_OUTPUT_SUBDIR, id, entry);
15899
16249
  return id;
15900
16250
  }
16251
+ function coerceBashEntry(raw) {
16252
+ if (raw === null || typeof raw !== "object") return null;
16253
+ const o = raw;
16254
+ 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") {
16255
+ return null;
16256
+ }
16257
+ return {
16258
+ id: o["id"],
16259
+ command: o["command"],
16260
+ output: o["output"],
16261
+ exitCode: o["exitCode"],
16262
+ storedAt: o["storedAt"],
16263
+ sizeBytes: o["sizeBytes"]
16264
+ };
16265
+ }
15901
16266
  function getBashOutput(id) {
15902
- return _byId.get(id) ?? null;
16267
+ const hit = _byId2.get(id);
16268
+ if (hit !== void 0) return hit;
16269
+ const entry = coerceBashEntry(loadBlob(BASH_OUTPUT_SUBDIR, id));
16270
+ if (entry === null) return null;
16271
+ _byId2.set(id, entry);
16272
+ return entry;
15903
16273
  }
15904
16274
  registerReset(() => {
15905
- _byId = /* @__PURE__ */ new Map();
16275
+ _byId2 = /* @__PURE__ */ new Map();
15906
16276
  _globsByKey = /* @__PURE__ */ new Map();
15907
16277
  _grepsByKey = /* @__PURE__ */ new Map();
15908
16278
  });
15909
16279
 
15910
- // src/hooks_bash.ts
15911
- function stripCdPrefix(cmd) {
15912
- const stripped = cmd.replace(/^(?:cd\s+(?:"[^"]*"|'[^']*'|\S+)\s*&&\s*)+/, "");
15913
- return stripped.trim() || cmd;
16280
+ // src/tool_filters/index.ts
16281
+ init_define_import_meta_env();
16282
+
16283
+ // src/tool_filters/helpers.ts
16284
+ init_define_import_meta_env();
16285
+
16286
+ // src/bash_compress.ts
16287
+ init_define_import_meta_env();
16288
+
16289
+ // src/filters.ts
16290
+ init_define_import_meta_env();
16291
+
16292
+ // src/bash_compress.ts
16293
+ var ANSI_ESCAPE_RE = (
16294
+ // eslint-disable-next-line no-control-regex -- intentionally matches ESC (\x1B) and BEL (\x07) control bytes
16295
+ /\x1B\[[0-?]*[ -/]*[@-~]|\x1B\].*?(?:\x07|\x1B\\)|\x1B[PX^_].*?\x1B\\|\x1B[@-Z\\-_]/gs
16296
+ );
16297
+ function stripAnsiCodes(text) {
16298
+ if (!text.includes("\x1B")) return text;
16299
+ return text.replace(ANSI_ESCAPE_RE, "");
15914
16300
  }
15915
- function stripOutputPipeline(cmd) {
15916
- let inSingle = false;
15917
- let inDouble = false;
15918
- let cut = cmd.length;
15919
- for (let i = 0; i < cmd.length; i++) {
15920
- const ch = cmd[i];
15921
- if (ch === "'" && !inDouble) {
15922
- inSingle = !inSingle;
15923
- } else if (ch === '"' && !inSingle) {
15924
- inDouble = !inDouble;
15925
- } else if (ch === "|" && !inSingle && !inDouble) {
15926
- if (cmd[i + 1] === "|") {
15927
- i++;
15928
- continue;
16301
+
16302
+ // src/tool_filters/helpers.ts
16303
+ var DEFAULT_MAX_LINES = 1e3;
16304
+ var DEFAULT_MAX_BYTES = 64 * 1024;
16305
+ var MAX_INSPECT_BYTES = 2 * 1024 * 1024;
16306
+ var DEFAULT_MAX_INPUT_BYTES = 500 * 1024;
16307
+ var FALLBACK_MAX_LINE_CHARS = 400;
16308
+ function getMaxInputBytes() {
16309
+ const raw = process.env["TOKEN_GOAT_FILTER_MAX_BYTES"];
16310
+ const v = raw ? Number.parseInt(raw, 10) : 0;
16311
+ return Number.isFinite(v) && v > 0 ? v : DEFAULT_MAX_INPUT_BYTES;
16312
+ }
16313
+ function compressionMarker(filter, pct) {
16314
+ return `
16315
+ [token-goat: ${filter} filter -${Math.round(pct)}%; disable via TOKEN_GOAT_BASH_COMPRESS]`;
16316
+ }
16317
+ var ERROR_SIGNAL_RE = /error:|Error:|ERROR|FAILED|failed|fatal:|Traceback|exception:|Exception:|AssertionError|assert |panic:/i;
16318
+ var REDIRECT_TOKEN_RE = /^(\d*)(>>?|<<?).*$|^&>$|^>&.*$/;
16319
+ var BYTES_ELIDED_MARKER_RE = /\n\.\.\. \[\d+ bytes elided by token-goat\]$/;
16320
+ var CONTROL_CHAR_RE = /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x9f]/g;
16321
+ function utf8Len(s) {
16322
+ return Buffer.byteLength(s, "utf8");
16323
+ }
16324
+ function safeDecode(text) {
16325
+ return text.indexOf("\0") === -1 ? text : text.split("\0").join("");
16326
+ }
16327
+ function sanitizeControlChars(text) {
16328
+ return text.replace(CONTROL_CHAR_RE, "");
16329
+ }
16330
+ function stripProgress(text) {
16331
+ if (!text.includes("\r")) return text;
16332
+ return text.split("\n").map((line) => line.includes("\r") ? line.slice(line.lastIndexOf("\r") + 1) : line).join("\n");
16333
+ }
16334
+ function normalise(text, opts = {}) {
16335
+ if (!text) return "";
16336
+ let t = text.replace(/\r\n/g, "\n");
16337
+ if (!opts.skipProgress) t = stripProgress(t);
16338
+ t = stripAnsiCodes(t);
16339
+ return sanitizeControlChars(t);
16340
+ }
16341
+ 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;
16342
+ var LONG_HEX_RE = /\b[0-9a-f]{32,}\b/i;
16343
+ var JWT_RE = /\b[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/;
16344
+ function hasHighEntropyToken(line) {
16345
+ if (UUID_RE.test(line) || LONG_HEX_RE.test(line) || JWT_RE.test(line)) return true;
16346
+ for (const tok of line.split(/[\s"'`,;:()[\]{}<>]+/)) {
16347
+ if (tok.length < 24) continue;
16348
+ if (/[a-z]/.test(tok) && /[A-Z]/.test(tok) && /[0-9]/.test(tok) && /^[A-Za-z0-9+/=_-]+$/.test(tok)) {
16349
+ return true;
16350
+ }
16351
+ }
16352
+ return false;
16353
+ }
16354
+ function dedupeConsecutive(lines, opts = {}) {
16355
+ const minRun = opts.minRun ?? 2;
16356
+ const fmt = opts.fmt ?? ((line, count2) => `${line} (\xD7${count2})`);
16357
+ const entropyBypass = opts.entropyBypass ?? false;
16358
+ const out2 = [];
16359
+ let prev = null;
16360
+ let count = 0;
16361
+ const flush = () => {
16362
+ if (prev === null) return;
16363
+ if (count >= minRun) out2.push(fmt(prev, count));
16364
+ else for (let i = 0; i < count; i++) out2.push(prev);
16365
+ };
16366
+ for (const line of lines) {
16367
+ if (entropyBypass && hasHighEntropyToken(line)) {
16368
+ flush();
16369
+ prev = null;
16370
+ count = 0;
16371
+ out2.push(line);
16372
+ continue;
16373
+ }
16374
+ if (line === prev) {
16375
+ count += 1;
16376
+ continue;
16377
+ }
16378
+ flush();
16379
+ prev = line;
16380
+ count = 1;
16381
+ }
16382
+ flush();
16383
+ return out2;
16384
+ }
16385
+ function truncateMiddle(lines, maxLines, opts = {}) {
16386
+ if (lines.length <= maxLines) return lines;
16387
+ const headRatio = opts.headRatio ?? 0.4;
16388
+ const markerFmt = opts.markerFmt ?? ((n) => `... [${n} lines elided by token-goat]`);
16389
+ const headKeep = Math.max(1, Math.floor(maxLines * headRatio));
16390
+ const tailKeep = Math.max(1, maxLines - headKeep);
16391
+ const elided = lines.length - headKeep - tailKeep;
16392
+ return [...lines.slice(0, headKeep), markerFmt(elided), ...lines.slice(lines.length - tailKeep)];
16393
+ }
16394
+ function truncateMiddleSmart(lines, maxLines, opts = {}) {
16395
+ if (lines.length <= maxLines) return lines;
16396
+ const headKeep = opts.headKeep ?? 10;
16397
+ const tailKeep = opts.tailKeep ?? 10;
16398
+ const errorContext = opts.errorContext ?? 2;
16399
+ const maxErrorLines = opts.maxErrorLines ?? 10;
16400
+ const markerFmt = opts.markerFmt ?? ((n) => `--- ${n} lines omitted ---`);
16401
+ const errorIndices = [];
16402
+ for (let i = 0; i < lines.length; i++) if (ERROR_SIGNAL_RE.test(lines[i])) errorIndices.push(i);
16403
+ if (errorIndices.length === 0) return truncateMiddle(lines, maxLines, { markerFmt });
16404
+ const total = lines.length;
16405
+ const effHead = Math.min(headKeep, Math.floor(total / 4));
16406
+ const effTail = Math.min(tailKeep, Math.floor(total / 4));
16407
+ const middle = /* @__PURE__ */ new Set();
16408
+ for (let k = 0; k < errorIndices.length && k < maxErrorLines; k++) {
16409
+ const ei = errorIndices[k];
16410
+ for (let ci = Math.max(0, ei - errorContext); ci < Math.min(total, ei + errorContext + 1); ci++) {
16411
+ middle.add(ci);
16412
+ }
16413
+ }
16414
+ for (let i = 0; i < effHead; i++) middle.delete(i);
16415
+ for (let i = total - effTail; i < total; i++) middle.delete(i);
16416
+ const budgetForMiddle = Math.max(0, maxLines - effHead - effTail);
16417
+ let sortedMiddle = Array.from(middle).sort((a, b) => a - b);
16418
+ if (sortedMiddle.length > budgetForMiddle) sortedMiddle = sortedMiddle.slice(0, budgetForMiddle);
16419
+ const result = [];
16420
+ const appendSection = (indices) => {
16421
+ for (let pos = 0; pos < indices.length; pos++) {
16422
+ const idx = indices[pos];
16423
+ if (pos > 0) {
16424
+ const prevIdx = indices[pos - 1];
16425
+ if (idx !== prevIdx + 1) result.push(markerFmt(idx - prevIdx - 1));
15929
16426
  }
15930
- cut = i;
15931
- break;
16427
+ result.push(lines[idx]);
15932
16428
  }
16429
+ };
16430
+ const headList = Array.from({ length: effHead }, (_, i) => i);
16431
+ const tailList = Array.from({ length: effTail }, (_, i) => total - effTail + i);
16432
+ appendSection(headList);
16433
+ if (sortedMiddle.length) {
16434
+ const gapAfterHead = sortedMiddle[0] - (headList.length ? headList[headList.length - 1] : -1) - 1;
16435
+ if (gapAfterHead > 0) result.push(markerFmt(gapAfterHead));
16436
+ appendSection(sortedMiddle);
16437
+ }
16438
+ if (tailList.length) {
16439
+ const lastKept = sortedMiddle.length ? sortedMiddle[sortedMiddle.length - 1] : headList.length ? headList[headList.length - 1] : -1;
16440
+ const gapBeforeTail = tailList[0] - lastKept - 1;
16441
+ if (gapBeforeTail > 0) result.push(markerFmt(gapBeforeTail));
16442
+ appendSection(tailList);
15933
16443
  }
15934
- let base = cmd.slice(0, cut);
15935
- let prev;
15936
- do {
15937
- prev = base;
15938
- const masked = base.replace(/"((?:[^"\\]|\\.)*)"/g, (_m, inner) => '"' + " ".repeat(inner.length) + '"').replace(/'([^']*)'/g, (_m, inner) => "'" + " ".repeat(inner.length) + "'");
15939
- const newMasked = masked.replace(/\s*(?:[0-9]*>&[0-9]+|[0-9&]*>>?\s*(?:"[^"]*"|'[^']*'|[^\s|]+))\s*$/, "");
15940
- if (newMasked.length < masked.length) {
15941
- base = base.slice(0, newMasked.length);
16444
+ return result;
16445
+ }
16446
+ function capBytes(text, maxBytes) {
16447
+ const encoded = Buffer.from(text, "utf8");
16448
+ if (encoded.length <= maxBytes) return text;
16449
+ const marker = `
16450
+ ... [${encoded.length - maxBytes} bytes elided by token-goat]`;
16451
+ const budget = maxBytes - Buffer.byteLength(marker, "utf8");
16452
+ if (budget <= 0) return marker.trim();
16453
+ let slice = encoded.subarray(0, budget);
16454
+ const nl = slice.lastIndexOf(10);
16455
+ if (nl > budget / 2) slice = slice.subarray(0, nl);
16456
+ return slice.toString("utf8") + marker;
16457
+ }
16458
+ function capTokens(text, maxTokens) {
16459
+ const clean = stripAnsiCodes(text);
16460
+ if (clean.length / 3.5 <= maxTokens) return text;
16461
+ const maxBytes = Math.floor(maxTokens * 3.5);
16462
+ let truncated = capBytes(clean, maxBytes);
16463
+ if (!truncated.includes("[token-goat: output capped at")) {
16464
+ truncated = truncated.replace(BYTES_ELIDED_MARKER_RE, "");
16465
+ truncated += `
16466
+ [token-goat: output capped at ~${maxTokens} tokens]`;
16467
+ }
16468
+ return truncated;
16469
+ }
16470
+ function trimRepeatedPrefix(lines, pattern, keep) {
16471
+ const out2 = [];
16472
+ let matched = 0;
16473
+ let dropped = 0;
16474
+ for (const line of lines) {
16475
+ if (pattern.test(line)) {
16476
+ matched += 1;
16477
+ if (matched <= keep) out2.push(line);
16478
+ else dropped += 1;
16479
+ } else {
16480
+ out2.push(line);
15942
16481
  }
15943
- } while (base !== prev);
15944
- return base.trim();
16482
+ }
16483
+ if (dropped) out2.push(`[token-goat: +${dropped} more lines matching ${pattern.source}]`);
16484
+ return out2;
15945
16485
  }
15946
- function extractCommand(event) {
15947
- const cmd = event.toolInput["command"];
15948
- return typeof cmd === "string" && cmd.trim() !== "" ? cmd.trim() : void 0;
16486
+ function maybeNote(notes, value, msg) {
16487
+ if (value) notes.push(msg);
15949
16488
  }
15950
- function isTempPath(fp) {
15951
- const norm = fp.replace(/\\/g, "/");
15952
- return /^\/tmp\//i.test(norm) || /\/var\/folders\//i.test(norm) || /AppData\/Local\/Temp\//i.test(norm) || norm.startsWith("/c/Users/") && norm.includes("/AppData/Local/Temp/");
16489
+ function squeezeBlankLines(text) {
16490
+ return text.replace(/\n\s*\n\s*\n+/g, "\n\n");
15953
16491
  }
15954
- function isOrchestratorStateFile(filePath) {
15955
- const basename8 = (filePath.includes("/") ? filePath.split("/").at(-1) : filePath.split("\\").at(-1)) ?? filePath;
15956
- return /^\.improve-state-/.test(basename8);
16492
+ function preserveStderrOnError(stdout, stderr, exitCode) {
16493
+ if (exitCode !== 0 && stderr.trim()) {
16494
+ return stdout.trim() ? `${stdout.replace(/\s+$/, "")}
16495
+ ---
16496
+ ${stderr.replace(/\s+$/, "")}` : stderr;
16497
+ }
16498
+ return null;
15957
16499
  }
15958
- function extractCatSourceFile(cmd) {
15959
- const m = /^cat\s+(\S+\.(?:java|py|ts|tsx|js|jsx|go|rb|rs|cpp|cc|cxx|c|h|hpp|kt|swift|cs|php|scala|clj|css|scss|sass|less))\s*$/.exec(cmd);
15960
- return m?.[1] ?? null;
16500
+ var PASSTHROUGH_PREFIXES = /* @__PURE__ */ new Set([
16501
+ "sudo",
16502
+ "doas",
16503
+ "time",
16504
+ "nice",
16505
+ "ionice",
16506
+ "nohup",
16507
+ "exec",
16508
+ "env",
16509
+ "stdbuf",
16510
+ "unbuffer",
16511
+ "script"
16512
+ ]);
16513
+ var TWO_TOKEN_PREFIXES = {
16514
+ python: /* @__PURE__ */ new Set(["-m"]),
16515
+ python3: /* @__PURE__ */ new Set(["-m"]),
16516
+ py: /* @__PURE__ */ new Set(["-m"]),
16517
+ uv: /* @__PURE__ */ new Set(["run", "tool"]),
16518
+ uvx: /* @__PURE__ */ new Set(),
16519
+ poetry: /* @__PURE__ */ new Set(["run"]),
16520
+ rye: /* @__PURE__ */ new Set(["run"]),
16521
+ pdm: /* @__PURE__ */ new Set(["run"]),
16522
+ pipenv: /* @__PURE__ */ new Set(["run"]),
16523
+ npx: /* @__PURE__ */ new Set(),
16524
+ pnpm: /* @__PURE__ */ new Set(["exec", "dlx"]),
16525
+ yarn: /* @__PURE__ */ new Set(["exec", "dlx"]),
16526
+ bundle: /* @__PURE__ */ new Set(["exec"]),
16527
+ tox: /* @__PURE__ */ new Set(["-e"]),
16528
+ hatch: /* @__PURE__ */ new Set(["run"])
16529
+ };
16530
+ function pathName(p) {
16531
+ const norm = p.replace(/\\/g, "/");
16532
+ const idx = norm.lastIndexOf("/");
16533
+ return idx >= 0 ? norm.slice(idx + 1) : norm;
16534
+ }
16535
+ function pathStem(p) {
16536
+ const name = pathName(p);
16537
+ const dot = name.lastIndexOf(".");
16538
+ return dot > 0 ? name.slice(0, dot) : name;
16539
+ }
16540
+ function positionalArgs(args) {
16541
+ return args.filter((a) => !a.startsWith("-"));
16542
+ }
16543
+ var SHORT_FLAGS_WITH_VALUE = /* @__PURE__ */ new Set(["-n", "-c", "-i", "-u", "-e"]);
16544
+ function stripPrefixes(argv) {
16545
+ if (argv.length === 0) return [];
16546
+ let out2 = [...argv];
16547
+ while (out2.length && out2[0].includes("=") && !out2[0].startsWith("-") && !out2[0].includes("/")) {
16548
+ const head = out2[0].split("=", 1)[0];
16549
+ if (head && (/[A-Za-z]/.test(head[0]) || head[0] === "_") && /^[A-Za-z0-9_]+$/.test(head)) out2.shift();
16550
+ else break;
16551
+ }
16552
+ while (out2.length) {
16553
+ if (!PASSTHROUGH_PREFIXES.has(pathStem(out2[0]).toLowerCase())) break;
16554
+ out2.shift();
16555
+ while (out2.length && out2[0].startsWith("-")) {
16556
+ const flag = out2.shift();
16557
+ if (SHORT_FLAGS_WITH_VALUE.has(flag) && out2.length) out2.shift();
16558
+ }
16559
+ }
16560
+ if (out2.length === 0) return out2;
16561
+ const stem = pathStem(out2[0]).toLowerCase();
16562
+ const triggers = TWO_TOKEN_PREFIXES[stem];
16563
+ if (triggers !== void 0 && out2.length >= 2) {
16564
+ const nextTok = out2[1];
16565
+ if (triggers.size === 0 || triggers.has(nextTok)) {
16566
+ const consume = triggers.size === 0 ? 1 : 2;
16567
+ if (out2.length > consume) out2 = out2.slice(consume);
16568
+ }
16569
+ }
16570
+ return out2;
15961
16571
  }
15962
- function extractCatFile(cmd) {
15963
- const m = /^cat(?:\s+(?:-[a-zA-Z]+|--[a-zA-Z-]+))*\s+(?:"([^"]+)"|'([^']+)'|(\S+))\s*$/.exec(cmd);
15964
- if (!m) return null;
16572
+ function shlexSplit(cmd) {
16573
+ const tokens = [];
16574
+ let cur = "";
16575
+ let has = false;
16576
+ let i = 0;
16577
+ const n = cmd.length;
16578
+ while (i < n) {
16579
+ const ch = cmd[i];
16580
+ if (ch === " " || ch === " " || ch === "\n" || ch === "\r") {
16581
+ if (has) {
16582
+ tokens.push(cur);
16583
+ cur = "";
16584
+ has = false;
16585
+ }
16586
+ i += 1;
16587
+ continue;
16588
+ }
16589
+ has = true;
16590
+ if (ch === "\\") {
16591
+ if (i + 1 < n) {
16592
+ cur += cmd[i + 1];
16593
+ i += 2;
16594
+ } else {
16595
+ i += 1;
16596
+ }
16597
+ continue;
16598
+ }
16599
+ if (ch === "'") {
16600
+ i += 1;
16601
+ const end = cmd.indexOf("'", i);
16602
+ if (end === -1) throw new Error("No closing quotation");
16603
+ cur += cmd.slice(i, end);
16604
+ i = end + 1;
16605
+ continue;
16606
+ }
16607
+ if (ch === '"') {
16608
+ i += 1;
16609
+ let buf = "";
16610
+ while (i < n && cmd[i] !== '"') {
16611
+ if (cmd[i] === "\\" && i + 1 < n && (cmd[i + 1] === '"' || cmd[i + 1] === "\\")) {
16612
+ buf += cmd[i + 1];
16613
+ i += 2;
16614
+ } else {
16615
+ buf += cmd[i];
16616
+ i += 1;
16617
+ }
16618
+ }
16619
+ if (i >= n) throw new Error("No closing quotation");
16620
+ cur += buf;
16621
+ i += 1;
16622
+ continue;
16623
+ }
16624
+ cur += ch;
16625
+ i += 1;
16626
+ }
16627
+ if (has) tokens.push(cur);
16628
+ return tokens;
16629
+ }
16630
+ function capLongLines(lines, maxChars = FALLBACK_MAX_LINE_CHARS) {
16631
+ return lines.map(
16632
+ (line) => line.length > maxChars ? `${line.slice(0, maxChars)} \u2026 [${line.length - maxChars} chars elided]` : line
16633
+ );
16634
+ }
16635
+ function fallbackTruncate(stdout, stderr, maxLines) {
16636
+ const half = Math.floor(maxLines / 2);
16637
+ const outLines = truncateMiddle(capLongLines(dedupeConsecutive(stdout.split("\n"))), half);
16638
+ const errLines = truncateMiddle(capLongLines(dedupeConsecutive(stderr.split("\n"))), half);
16639
+ if (stderr) return `${outLines.join("\n")}
16640
+ ---
16641
+ ${errLines.join("\n")}`;
16642
+ return outLines.join("\n");
16643
+ }
16644
+ function compressBashOutput(stdout, stderr) {
16645
+ let body = dedupeConsecutive(stdout.split("\n"), { entropyBypass: true }).join("\n");
16646
+ if (stderr.trim()) {
16647
+ const err2 = dedupeConsecutive(stderr.split("\n"), { entropyBypass: true }).join("\n");
16648
+ body = `${body.replace(/\s+$/, "")}
16649
+ ---
16650
+ ${err2.replace(/\s+$/, "")}`;
16651
+ }
16652
+ return body;
16653
+ }
16654
+ function byteLength(s) {
16655
+ return utf8Len(s);
16656
+ }
16657
+
16658
+ // src/tool_filters/base.ts
16659
+ init_define_import_meta_env();
16660
+ var CompressedOutput = class {
16661
+ constructor(text, originalBytes, compressedBytes, filterName, exitCode = 0, notes = []) {
16662
+ this.text = text;
16663
+ this.originalBytes = originalBytes;
16664
+ this.compressedBytes = compressedBytes;
16665
+ this.filterName = filterName;
16666
+ this.exitCode = exitCode;
16667
+ this.notes = notes;
16668
+ }
16669
+ /** Non-negative byte savings (`original - compressed`, clamped at 0). */
16670
+ get bytesSaved() {
16671
+ return Math.max(0, this.originalBytes - this.compressedBytes);
16672
+ }
16673
+ /** Estimated token savings (`n // 3 + 1`, matching `estimateTokens`). */
16674
+ get tokensSaved() {
16675
+ const n = this.bytesSaved;
16676
+ return n <= 0 ? 0 : Math.max(1, Math.floor(n / 3) + 1);
16677
+ }
16678
+ /** Reduction as a percentage of the original size (0 when no input). */
16679
+ get percentSaved() {
16680
+ if (this.originalBytes <= 0) return 0;
16681
+ return 100 * this.bytesSaved / this.originalBytes;
16682
+ }
16683
+ /**
16684
+ * `text` with the trailing compression-summary marker appended. Skipped
16685
+ * entirely on a no-op (savings ≤ 0) so raw output never carries a marker.
16686
+ */
16687
+ withMarker() {
16688
+ if (this.bytesSaved <= 0 || this.originalBytes <= 0) return this.text;
16689
+ return this.text + compressionMarker(this.filterName, this.percentSaved);
16690
+ }
16691
+ };
16692
+ var ToolFilter = class {
16693
+ /** Command basenames this filter handles (lowercased stems). */
16694
+ binaries = /* @__PURE__ */ new Set();
16695
+ /** Optional subcommand gate; matched against the first 3 positional args. */
16696
+ subcommands = /* @__PURE__ */ new Set();
16697
+ /** When true, pass raw error output through on non-zero exit. */
16698
+ errorPassthrough = false;
16699
+ /** Detect whether this filter applies to the raw command string. */
16700
+ detectFromCommand(cmd) {
16701
+ try {
16702
+ if (!cmd || cmd.length > 65536) return false;
16703
+ const resolved = stripPrefixes(shlexSplit(cmd));
16704
+ if (resolved.length === 0) return false;
16705
+ return this.matches(resolved);
16706
+ } catch {
16707
+ return false;
16708
+ }
16709
+ }
16710
+ /**
16711
+ * Return true when this filter should run for `argv`. Checks `binaries`
16712
+ * against the lowercased stem (and full name, for dot-in-name binaries like
16713
+ * `py.test`) of `argv[0]`; when `subcommands` is non-empty, requires one in
16714
+ * the first three positional args.
16715
+ */
16716
+ matches(argv) {
16717
+ if (argv.length === 0) return false;
16718
+ const first = argv[0];
16719
+ const stem = pathStem(first).toLowerCase();
16720
+ const name = pathName(first).toLowerCase();
16721
+ if (!this.binaries.has(stem) && !this.binaries.has(name)) return false;
16722
+ if (this.subcommands.size === 0) return true;
16723
+ return positionalArgs(argv.slice(1)).slice(0, 3).some((tok) => this.subcommands.has(tok));
16724
+ }
16725
+ /** Combine stdout/stderr with a `---` separator when both are present. */
16726
+ combineOutput(stdout, stderr) {
16727
+ if (stderr.trim() && stdout.trim()) return `${stdout.replace(/\s+$/, "")}
16728
+ ---
16729
+ ${stderr.replace(/\s+$/, "")}`;
16730
+ return stdout.trim() ? stdout.replace(/\s+$/, "") : stderr.replace(/\s+$/, "");
16731
+ }
16732
+ /** Append a `[token-goat: <joined notes>]` summary line to `kept`. */
16733
+ emitNotes(kept, notes, prefix = "token-goat: ") {
16734
+ if (notes.length) kept.push(`[${prefix}${notes.join("; ")}]`);
16735
+ }
16736
+ /** Join `kept` with newlines and squeeze runs of blank lines. */
16737
+ finalize(kept) {
16738
+ return squeezeBlankLines(kept.join("\n"));
16739
+ }
16740
+ /**
16741
+ * Hook applied to each stream right after {@link normalise}. Identity by
16742
+ * default; the git filter family overrides it to strip CRLF warnings. Keeps
16743
+ * `apply` free of per-family name checks.
16744
+ */
16745
+ postNormalise(text) {
16746
+ return text;
16747
+ }
16748
+ /**
16749
+ * Template: when {@link errorPassthrough} is set, return the raw combined
16750
+ * error output on non-zero exit before delegating to {@link compressBody}.
16751
+ * Filters that handle errors structurally (pytest, cargo) override this
16752
+ * directly and leave `errorPassthrough` false.
16753
+ */
16754
+ compress(stdout, stderr, exitCode, argv) {
16755
+ if (this.errorPassthrough) {
16756
+ const err2 = preserveStderrOnError(stdout, stderr, exitCode);
16757
+ if (err2 !== null) return err2;
16758
+ }
16759
+ return this.compressBody(stdout, stderr, exitCode, argv);
16760
+ }
16761
+ /**
16762
+ * Inner compression logic, called after the error-passthrough guard.
16763
+ * Default is a passthrough that joins the two streams — useful when the only
16764
+ * compression is the ANSI / progress strip `apply` already performed.
16765
+ */
16766
+ compressBody(stdout, stderr, _exitCode, _argv) {
16767
+ if (stderr && stdout) return `${stdout.replace(/\s+$/, "")}
16768
+ ---
16769
+ ${stderr.replace(/\s+$/, "")}`;
16770
+ return stdout || stderr;
16771
+ }
16772
+ /**
16773
+ * Top-level entry: sanitise → input cap → normalise → compress → line/byte
16774
+ * cap → wrap in {@link CompressedOutput}. Faithful port of the Python
16775
+ * `apply` 10-step pipeline. Errors from {@link compress} fall back to a
16776
+ * truncated view so the agent always sees something.
16777
+ */
16778
+ apply(stdout, stderr, exitCode, argv, opts = {}) {
16779
+ const maxLines = opts.maxLines ?? DEFAULT_MAX_LINES;
16780
+ const maxBytes = opts.maxBytes ?? DEFAULT_MAX_BYTES;
16781
+ const skipProgress = opts.skipProgress ?? false;
16782
+ let so = safeDecode(stdout);
16783
+ let se = safeDecode(stderr);
16784
+ const maxInput = getMaxInputBytes();
16785
+ const notes = [];
16786
+ const soBytes = Buffer.from(so, "utf8");
16787
+ const seBytes = Buffer.from(se, "utf8");
16788
+ if (soBytes.length > maxInput) {
16789
+ so = soBytes.subarray(0, maxInput).toString("utf8");
16790
+ notes.push(`input truncated at ${Math.floor(maxInput / 1024)}KB (TOKEN_GOAT_FILTER_MAX_BYTES)`);
16791
+ }
16792
+ if (seBytes.length > maxInput) {
16793
+ se = seBytes.subarray(0, maxInput).toString("utf8");
16794
+ if (!notes.some((n) => n.includes("input truncated"))) {
16795
+ notes.push(`stderr truncated at ${Math.floor(maxInput / 1024)}KB (TOKEN_GOAT_FILTER_MAX_BYTES)`);
16796
+ }
16797
+ }
16798
+ const originalBytes = soBytes.length + seBytes.length;
16799
+ if (!so.trim() && !se.trim()) {
16800
+ const text = notes.length ? `[${notes.join("; ")}]
16801
+ ` : "";
16802
+ return new CompressedOutput(text, originalBytes, byteLength(text), this.name, exitCode, notes);
16803
+ }
16804
+ let body;
16805
+ try {
16806
+ const normOut = this.postNormalise(normalise(so, { skipProgress }));
16807
+ const normErr = this.postNormalise(normalise(se, { skipProgress }));
16808
+ const normBytes = byteLength(normOut) + byteLength(normErr);
16809
+ if (originalBytes > 0 && normBytes <= originalBytes * 0.6) {
16810
+ body = compressBashOutput(normOut, normErr);
16811
+ notes.push("early-exit: normalisation alone sufficient");
16812
+ } else if (normBytes > MAX_INSPECT_BYTES) {
16813
+ notes.push(`input exceeded inspect budget (${Math.floor(MAX_INSPECT_BYTES / 1024)} KiB); fell back to truncation`);
16814
+ body = fallbackTruncate(normOut, normErr, maxLines);
16815
+ } else {
16816
+ body = this.compress(normOut, normErr, exitCode, argv);
16817
+ }
16818
+ } catch (exc) {
16819
+ const kind = exc instanceof Error ? exc.constructor.name : "Error";
16820
+ notes.push(`${this.name} filter raised ${kind}; truncated raw`);
16821
+ const fbOut = this.postNormalise(normalise(so, { skipProgress }));
16822
+ const fbErr = this.postNormalise(normalise(se, { skipProgress }));
16823
+ body = fallbackTruncate(fbOut, fbErr, maxLines);
16824
+ }
16825
+ const lines = body.split("\n");
16826
+ if (lines.length > maxLines) body = truncateMiddleSmart(lines, maxLines).join("\n");
16827
+ body = capBytes(body, maxBytes);
16828
+ if (notes.length) body = `[${notes.join("; ")}]
16829
+ ${body}`;
16830
+ return new CompressedOutput(body, originalBytes, byteLength(body), this.name, exitCode, notes);
16831
+ }
16832
+ };
16833
+
16834
+ // src/tool_filters/generic.ts
16835
+ init_define_import_meta_env();
16836
+ var GenericFilter = class extends ToolFilter {
16837
+ name = "generic";
16838
+ compress(stdout, stderr, _exitCode, _argv) {
16839
+ const outLines = dedupeConsecutive(stdout.split("\n"), { entropyBypass: true });
16840
+ const errLines = dedupeConsecutive(stderr.split("\n"), { entropyBypass: true });
16841
+ let result;
16842
+ if (stderr.trim()) {
16843
+ const outPart = outLines.join("\n").replace(/\s+$/, "");
16844
+ const errPart = errLines.join("\n").replace(/\s+$/, "");
16845
+ result = outPart ? `${outPart}
16846
+ ---
16847
+ ${errPart}` : errPart;
16848
+ } else {
16849
+ result = outLines.join("\n");
16850
+ }
16851
+ return capTokens(result, 2e3);
16852
+ }
16853
+ };
16854
+
16855
+ // src/tool_filters/families.ts
16856
+ init_define_import_meta_env();
16857
+ function plural(n, suffix = "s") {
16858
+ return n === 1 ? "" : suffix;
16859
+ }
16860
+ function makeNodeTestRunnerFilter(cfg) {
16861
+ return new class extends ToolFilter {
16862
+ name = cfg.name;
16863
+ binaries = new Set(cfg.binaries);
16864
+ compress(stdout, stderr, _exitCode, _argv) {
16865
+ const merged = this.combineOutput(stdout, stderr);
16866
+ const lines = merged.split("\n");
16867
+ const kept = [];
16868
+ let passFile = 0;
16869
+ let tick = 0;
16870
+ let inFail = false;
16871
+ let blockLines = 0;
16872
+ let inBlock = false;
16873
+ let inFailuresSection = false;
16874
+ let failuresDropped = 0;
16875
+ const flush = () => {
16876
+ if (blockLines) {
16877
+ kept.push(` [token-goat: collapsed ${blockLines} ${cfg.outputNoun} line${plural(blockLines)}]`);
16878
+ }
16879
+ blockLines = 0;
16880
+ inBlock = false;
16881
+ };
16882
+ for (const line of lines) {
16883
+ if (cfg.failuresSection) {
16884
+ if (line.trim() === "Failures:") {
16885
+ flush();
16886
+ inFail = false;
16887
+ inFailuresSection = true;
16888
+ failuresDropped += 1;
16889
+ continue;
16890
+ }
16891
+ if (inFailuresSection) {
16892
+ if (cfg.summaryRe.test(line)) {
16893
+ inFailuresSection = false;
16894
+ kept.push(line);
16895
+ } else {
16896
+ failuresDropped += 1;
16897
+ }
16898
+ continue;
16899
+ }
16900
+ }
16901
+ if (cfg.passFileRe.test(line) && !inFail) {
16902
+ flush();
16903
+ passFile += 1;
16904
+ continue;
16905
+ }
16906
+ if (cfg.failFileRe.test(line)) {
16907
+ flush();
16908
+ inFail = true;
16909
+ kept.push(line);
16910
+ continue;
16911
+ }
16912
+ if (cfg.summaryRe.test(line)) {
16913
+ flush();
16914
+ kept.push(line);
16915
+ continue;
16916
+ }
16917
+ if (!line.trim() && inFail) {
16918
+ inFail = false;
16919
+ kept.push(line);
16920
+ continue;
16921
+ }
16922
+ if (inFail) {
16923
+ kept.push(line);
16924
+ continue;
16925
+ }
16926
+ if (cfg.outputHdrRe.test(line)) {
16927
+ flush();
16928
+ inBlock = true;
16929
+ blockLines = 1;
16930
+ continue;
16931
+ }
16932
+ if (inBlock) {
16933
+ const stripped = line.trim();
16934
+ if (!stripped || line.length > 0 && !/^\s/.test(line)) {
16935
+ flush();
16936
+ } else {
16937
+ blockLines += 1;
16938
+ continue;
16939
+ }
16940
+ }
16941
+ if (cfg.testTickRe.test(line)) {
16942
+ tick += 1;
16943
+ continue;
16944
+ }
16945
+ kept.push(line);
16946
+ }
16947
+ flush();
16948
+ const notes = [];
16949
+ maybeNote(notes, passFile, `collapsed ${passFile} ${cfg.passFileNoun}${plural(passFile)}`);
16950
+ maybeNote(notes, tick, `collapsed ${tick} passing tick${plural(tick)}`);
16951
+ if (failuresDropped) {
16952
+ notes.push(
16953
+ `collapsed ${failuresDropped} line${plural(failuresDropped)} from duplicate 'Failures:' section (already shown inline)`
16954
+ );
16955
+ }
16956
+ this.emitNotes(kept, notes);
16957
+ return this.finalize(kept);
16958
+ }
16959
+ }();
16960
+ }
16961
+
16962
+ // src/tool_filters/test_runners.ts
16963
+ init_define_import_meta_env();
16964
+ var jestFilter = makeNodeTestRunnerFilter({
16965
+ name: "jest",
16966
+ binaries: ["jest", "mocha", "ava", "tap"],
16967
+ // File-level PASS header: `PASS src/foo.test.js`, or a bare `✓ file` at col 0.
16968
+ passFileRe: /^(?:\s*PASS\s+\S|[✓√]\s+\S)/,
16969
+ failFileRe: /^\s*(?:FAIL|✗|×|✘)\s+\S/,
16970
+ summaryRe: /^(Test Suites|Tests|Snapshots|Time|Ran all test suites):/,
16971
+ // Per-test tick: any ✓/√ line not already claimed as a file header above.
16972
+ testTickRe: /^\s*[✓√]/,
16973
+ outputHdrRe: /^\s*console\.(log|error|warn|info|debug)\s/,
16974
+ outputNoun: "console output",
16975
+ passFileNoun: "PASS file",
16976
+ failuresSection: true
16977
+ });
16978
+ var vitestFilter = makeNodeTestRunnerFilter({
16979
+ name: "vitest",
16980
+ binaries: ["vitest"],
16981
+ // File-level pass header carries a duration: `✓ src/x.test.ts (12ms)`.
16982
+ passFileRe: /^\s*✓\s+\S.*\([\d.]+\s*\w+\)/,
16983
+ failFileRe: /^\s*(?:×|FAIL|✗|✘)\s+\S/,
16984
+ summaryRe: /^(Test Files|Tests|Modules|Duration|Start at)[\s:]+\d/,
16985
+ // Per-test tick is indented ≥2 spaces: ` ✓ should pass`.
16986
+ testTickRe: /^\s{2,}✓\s/,
16987
+ outputHdrRe: /^\s*stdout\s*\|/,
16988
+ outputNoun: "stdout",
16989
+ passFileNoun: "passing file"
16990
+ });
16991
+ var TEST_RUNNER_FILTERS = [jestFilter, vitestFilter];
16992
+
16993
+ // src/tool_filters/pytest.ts
16994
+ init_define_import_meta_env();
16995
+ var XDIST_PREFIX_RE = /^\[gw\d+\]\s*(?:\[\s*\d+%\]\s*)?/;
16996
+ var DOTS_RE = /^[.FxXEsS]+\s*(\[\s*\d+%\])?\s*$/;
16997
+ var BANNER_RE = /^(?:platform\s|cachedir:\s|rootdir:\s|plugins:\s|configfile:\s|bringing up\s|cacheprovider-)/;
16998
+ var PREAMBLE_RE = /^collecting\s/;
16999
+ var HEADER_RE2 = /^=+\s*(?:test session starts|FAILURES|ERRORS|short test summary info|warnings summary|slowest \d+ durations|\d+ failed|\d+ passed|\d+ error)\b/;
17000
+ var COV_TOTAL_RE = /^TOTAL\s+\d/;
17001
+ var WARN_DOCS_RE = /^\s*--\s+Docs:\s+https?:\/\//;
17002
+ var WARN_MSG_RE = /^\s+\S.*:\d+:\s+\S.*Warning\b/;
17003
+ var SLOW_DURATION_RE = /^\d+\.\d+s\s+(?:call|setup|teardown)\s+\S/;
17004
+ var FAIL_LINE_RE = /^(FAILED|ERROR|PASSED|SKIPPED|XFAIL|XPASS)\s+\S/;
17005
+ var VERBOSE_LINE_RE = /^\S.+::\S+[ \t]+(PASSED|FAILED|ERROR|SKIPPED|XFAIL|XPASS)(?:[ \t]|$)/;
17006
+ var COLLECT_RE = /^collected \d+ items?/;
17007
+ var COV_SEP_RE = /^[-=]+\s*$/;
17008
+ var COV_ROW_RE = /^\S.*\s+\d+\s+\d+\s+\d+%?\s*$/;
17009
+ var MAX_SLOW_KEPT = 5;
17010
+ var PytestFilter = class extends ToolFilter {
17011
+ name = "pytest";
17012
+ binaries = /* @__PURE__ */ new Set(["pytest", "py.test"]);
17013
+ compress(stdout, stderr, _exitCode, _argv) {
17014
+ const text = this.combineOutput(stdout, stderr);
17015
+ const lines = text.split("\n");
17016
+ let kept = [];
17017
+ let passedCount = 0;
17018
+ let inFailures = false;
17019
+ let inErrors = false;
17020
+ let inSlowSection = false;
17021
+ let inWarningsSection = false;
17022
+ let slowKept = 0;
17023
+ let slowDropped = 0;
17024
+ let inCovTable = false;
17025
+ let covTableRowsDropped = 0;
17026
+ const warnMsgSeen = /* @__PURE__ */ new Map();
17027
+ let warningsDropped = 0;
17028
+ for (let line of lines) {
17029
+ if (XDIST_PREFIX_RE.test(line)) line = line.replace(XDIST_PREFIX_RE, "");
17030
+ if (DOTS_RE.test(line)) continue;
17031
+ if (BANNER_RE.test(line)) continue;
17032
+ if (PREAMBLE_RE.test(line)) continue;
17033
+ if (HEADER_RE2.test(line)) {
17034
+ inFailures = line.includes("FAILURES");
17035
+ inErrors = line.includes("ERRORS") || line.includes("short test summary");
17036
+ inSlowSection = line.includes("slowest") && line.includes("durations");
17037
+ inWarningsSection = line.includes("warnings summary");
17038
+ inCovTable = false;
17039
+ if (line.includes("test session starts")) continue;
17040
+ kept.push(line);
17041
+ continue;
17042
+ }
17043
+ if (line.startsWith("Name") && line.includes("Stmts") && line.includes("Miss")) {
17044
+ inCovTable = true;
17045
+ kept.push(line);
17046
+ continue;
17047
+ }
17048
+ if (inCovTable) {
17049
+ if (COV_TOTAL_RE.test(line)) {
17050
+ if (covTableRowsDropped) {
17051
+ kept.push(`[token-goat: collapsed ${covTableRowsDropped} coverage table rows]`);
17052
+ covTableRowsDropped = 0;
17053
+ }
17054
+ kept.push(line);
17055
+ inCovTable = false;
17056
+ continue;
17057
+ }
17058
+ if (COV_SEP_RE.test(line)) {
17059
+ kept.push(line);
17060
+ continue;
17061
+ }
17062
+ if (COV_ROW_RE.test(line)) {
17063
+ covTableRowsDropped += 1;
17064
+ continue;
17065
+ }
17066
+ inCovTable = false;
17067
+ }
17068
+ if (inWarningsSection) {
17069
+ if (WARN_DOCS_RE.test(line)) {
17070
+ warningsDropped += 1;
17071
+ continue;
17072
+ }
17073
+ if (WARN_MSG_RE.test(line)) {
17074
+ const colonIdx = line.lastIndexOf("Warning");
17075
+ const normKey = colonIdx >= 0 ? line.slice(colonIdx).trim() : line.trim();
17076
+ const count = warnMsgSeen.get(normKey) ?? 0;
17077
+ warnMsgSeen.set(normKey, count + 1);
17078
+ if (count === 0) kept.push(line);
17079
+ else warningsDropped += 1;
17080
+ continue;
17081
+ }
17082
+ }
17083
+ if (inSlowSection) {
17084
+ if (SLOW_DURATION_RE.test(line)) {
17085
+ if (slowKept < MAX_SLOW_KEPT) {
17086
+ kept.push(line);
17087
+ slowKept += 1;
17088
+ } else {
17089
+ slowDropped += 1;
17090
+ }
17091
+ continue;
17092
+ }
17093
+ if (!line.trim() || HEADER_RE2.test(line)) {
17094
+ if (slowDropped) {
17095
+ kept.push(`[token-goat: collapsed ${slowDropped} slow-test duration lines]`);
17096
+ slowDropped = 0;
17097
+ }
17098
+ inSlowSection = false;
17099
+ if (!line.trim()) continue;
17100
+ if (HEADER_RE2.test(line)) {
17101
+ inFailures = line.includes("FAILURES");
17102
+ inErrors = line.includes("ERRORS") || line.includes("short test summary");
17103
+ inWarningsSection = line.includes("warnings summary");
17104
+ kept.push(line);
17105
+ continue;
17106
+ }
17107
+ } else {
17108
+ kept.push(line);
17109
+ continue;
17110
+ }
17111
+ }
17112
+ if (!inFailures && !inErrors && FAIL_LINE_RE.test(line)) {
17113
+ const tag = line.split(/\s+/, 1)[0];
17114
+ if (tag === "PASSED") {
17115
+ passedCount += 1;
17116
+ continue;
17117
+ }
17118
+ kept.push(line);
17119
+ continue;
17120
+ }
17121
+ if (!inFailures && !inErrors) {
17122
+ const m = VERBOSE_LINE_RE.exec(line);
17123
+ if (m) {
17124
+ if (m[1] === "PASSED") {
17125
+ passedCount += 1;
17126
+ continue;
17127
+ }
17128
+ kept.push(line);
17129
+ continue;
17130
+ }
17131
+ }
17132
+ kept.push(line);
17133
+ }
17134
+ if (slowDropped) kept.push(`[token-goat: collapsed ${slowDropped} slow-test duration lines]`);
17135
+ if (covTableRowsDropped)
17136
+ kept.push(`[token-goat: collapsed ${covTableRowsDropped} coverage table rows]`);
17137
+ kept = trimRepeatedPrefix(kept, COLLECT_RE, 3);
17138
+ if (passedCount) kept.push(`[token-goat: collapsed ${passedCount} PASSED lines]`);
17139
+ if (warningsDropped)
17140
+ kept.push(`[token-goat: collapsed ${warningsDropped} duplicate/docs warning lines]`);
17141
+ return this.finalize(kept);
17142
+ }
17143
+ };
17144
+ var pytestFilter = new PytestFilter();
17145
+
17146
+ // src/tool_filters/go_test.ts
17147
+ init_define_import_meta_env();
17148
+ var TEST_RUN_RE = /^=== (RUN|PAUSE|CONT|NAME)\s/;
17149
+ var RACE_FENCE_RE = /^={10,}\s*$/;
17150
+ var RACE_WARNING_RE = /^WARNING: DATA RACE/;
17151
+ var TEST_PASS_RE = /^\s*--- PASS:\s/;
17152
+ var TEST_FAIL_RE = /^\s*--- FAIL:\s/;
17153
+ var TEST_RPC_RE = /^=== (RUN|PAUSE|CONT)\s/;
17154
+ var SKIP_RE = /^\s*--- SKIP:\s/;
17155
+ var GOROUTINE_HEADER_RE = /^(?:Goroutine \d+|Previous|Current)\s/;
17156
+ var OK_PKG_RE = /^ok\s+\S+\s+\d/;
17157
+ var FAIL_PKG_RE = /^FAIL\t\S+/;
17158
+ var MAX_RACE_GOROUTINE_FRAMES = 5;
17159
+ var GoTestFilter = class extends ToolFilter {
17160
+ name = "go-test";
17161
+ binaries = /* @__PURE__ */ new Set(["go"]);
17162
+ // Fire only for `go test` (test as the FIRST positional), not `go build`/`run`.
17163
+ matches(argv) {
17164
+ if (argv.length === 0) return false;
17165
+ const stem = argv[0].replace(/\\/g, "/").split("/").pop().toLowerCase().replace(/\.exe$/, "");
17166
+ if (stem !== "go") return false;
17167
+ return positionalArgs(argv.slice(1)).slice(0, 1)[0] === "test";
17168
+ }
17169
+ compress(stdout, stderr, _exitCode, argv) {
17170
+ if (argv.includes("-json")) return this.combineOutput(stdout, stderr);
17171
+ const merged = this.combineOutput(stdout, stderr);
17172
+ const lines = merged.split("\n");
17173
+ const kept = [];
17174
+ let passCount = 0;
17175
+ let skipCount = 0;
17176
+ let inFailBlock = false;
17177
+ let droppedRun = 0;
17178
+ let droppedDownload = 0;
17179
+ let inRaceBlock = false;
17180
+ let raceBlockLines = [];
17181
+ let racePendingFence = false;
17182
+ let raceCount = 0;
17183
+ const flushRaceBlock = () => {
17184
+ let inGoroutine = false;
17185
+ let goroutineFrameCount = 0;
17186
+ let goroutineFramesDropped = 0;
17187
+ for (const rline of raceBlockLines) {
17188
+ if (GOROUTINE_HEADER_RE.test(rline)) {
17189
+ if (goroutineFramesDropped) {
17190
+ kept.push(` [token-goat: +${goroutineFramesDropped} goroutine frames omitted]`);
17191
+ }
17192
+ inGoroutine = true;
17193
+ goroutineFrameCount = 0;
17194
+ goroutineFramesDropped = 0;
17195
+ kept.push(rline);
17196
+ continue;
17197
+ }
17198
+ if (inGoroutine) {
17199
+ if ((rline.startsWith(" ") || rline.startsWith(" ")) && rline.trim()) {
17200
+ goroutineFrameCount += 1;
17201
+ if (goroutineFrameCount <= MAX_RACE_GOROUTINE_FRAMES) kept.push(rline);
17202
+ else goroutineFramesDropped += 1;
17203
+ continue;
17204
+ }
17205
+ if (goroutineFramesDropped) {
17206
+ kept.push(` [token-goat: +${goroutineFramesDropped} goroutine frames omitted]`);
17207
+ }
17208
+ inGoroutine = false;
17209
+ goroutineFrameCount = 0;
17210
+ goroutineFramesDropped = 0;
17211
+ }
17212
+ kept.push(rline);
17213
+ }
17214
+ if (goroutineFramesDropped) {
17215
+ kept.push(` [token-goat: +${goroutineFramesDropped} goroutine frames omitted]`);
17216
+ }
17217
+ };
17218
+ for (const line of lines) {
17219
+ if (line.startsWith("go: downloading")) {
17220
+ droppedDownload += 1;
17221
+ continue;
17222
+ }
17223
+ if (RACE_FENCE_RE.test(line)) {
17224
+ if (inRaceBlock) {
17225
+ raceBlockLines.push(line);
17226
+ flushRaceBlock();
17227
+ raceBlockLines = [];
17228
+ inRaceBlock = false;
17229
+ racePendingFence = false;
17230
+ } else if (!racePendingFence) {
17231
+ racePendingFence = true;
17232
+ raceBlockLines = [line];
17233
+ } else {
17234
+ kept.push(...raceBlockLines, line);
17235
+ raceBlockLines = [];
17236
+ racePendingFence = false;
17237
+ }
17238
+ continue;
17239
+ }
17240
+ if (racePendingFence) {
17241
+ raceBlockLines.push(line);
17242
+ if (RACE_WARNING_RE.test(line)) {
17243
+ inRaceBlock = true;
17244
+ raceCount += 1;
17245
+ racePendingFence = false;
17246
+ } else if (!line.trim()) {
17247
+ kept.push(...raceBlockLines);
17248
+ raceBlockLines = [];
17249
+ racePendingFence = false;
17250
+ }
17251
+ continue;
17252
+ }
17253
+ if (inRaceBlock) {
17254
+ raceBlockLines.push(line);
17255
+ continue;
17256
+ }
17257
+ if (TEST_RPC_RE.test(line)) {
17258
+ droppedRun += 1;
17259
+ continue;
17260
+ }
17261
+ if (TEST_FAIL_RE.test(line)) {
17262
+ inFailBlock = true;
17263
+ kept.push(line);
17264
+ continue;
17265
+ }
17266
+ if (TEST_PASS_RE.test(line)) {
17267
+ inFailBlock = false;
17268
+ passCount += 1;
17269
+ continue;
17270
+ }
17271
+ if (SKIP_RE.test(line)) {
17272
+ skipCount += 1;
17273
+ continue;
17274
+ }
17275
+ if (TEST_RUN_RE.test(line)) {
17276
+ if (inFailBlock) {
17277
+ inFailBlock = false;
17278
+ } else {
17279
+ droppedRun += 1;
17280
+ continue;
17281
+ }
17282
+ }
17283
+ if (inFailBlock && (line.startsWith(" ") || line.startsWith(" ") || !line.trim())) {
17284
+ kept.push(line);
17285
+ continue;
17286
+ }
17287
+ inFailBlock = false;
17288
+ kept.push(line);
17289
+ }
17290
+ if (raceBlockLines.length) flushRaceBlock();
17291
+ const goOkCount = kept.filter((l) => OK_PKG_RE.test(l)).length;
17292
+ const goFailCount = kept.filter((l) => FAIL_PKG_RE.test(l)).length;
17293
+ if (goOkCount + goFailCount >= 2) {
17294
+ kept.push(`[${goOkCount} packages passed, ${goFailCount} packages failed]`);
17295
+ }
17296
+ const notes = [];
17297
+ maybeNote(
17298
+ notes,
17299
+ raceCount,
17300
+ `kept ${raceCount} DATA RACE block(s) verbatim (goroutine stacks collapsed)`
17301
+ );
17302
+ maybeNote(notes, passCount, `collapsed ${passCount} PASS testcases`);
17303
+ maybeNote(notes, skipCount, `collapsed ${skipCount} SKIP testcases`);
17304
+ maybeNote(notes, droppedRun, `dropped ${droppedRun} === RUN/PAUSE/CONT lines`);
17305
+ maybeNote(notes, droppedDownload, `dropped ${droppedDownload} 'go: downloading' lines`);
17306
+ this.emitNotes(kept, notes);
17307
+ return this.finalize(kept);
17308
+ }
17309
+ };
17310
+ var goTestFilter = new GoTestFilter();
17311
+
17312
+ // src/tool_filters/dispatch.ts
17313
+ init_define_import_meta_env();
17314
+ var TOOL_FILTERS = [
17315
+ // Batch A — test runners. Node family (jest/mocha/ava/tap, vitest) first, then
17316
+ // the bespoke runners (pytest, go test) that need their own structural logic.
17317
+ // No overlap with later batches; safe at the head — except `go-test`, which
17318
+ // must precede any future `go build`/`go run` filter (both match `go`), so it
17319
+ // is registered here ahead of the build batch.
17320
+ ...TEST_RUNNER_FILTERS,
17321
+ pytestFilter,
17322
+ goTestFilter
17323
+ // Batches append here: package-managers · linters · vcs · build · containers ·
17324
+ // cloud/iac · ci · ai-clis · shell/file · lang.
17325
+ ];
17326
+ var PROFILE_CAPS = { aggressive: 50, balanced: 200, minimal: 500 };
17327
+ function selectFilter(argv) {
17328
+ if (argv.length === 0) return null;
17329
+ let resolved = stripPrefixes(argv);
17330
+ if (resolved.length === 0) {
17331
+ resolved = argv.slice(0, 1);
17332
+ if (resolved.length === 0) return null;
17333
+ }
17334
+ for (const f of TOOL_FILTERS) {
17335
+ try {
17336
+ if (f.matches(resolved)) return f;
17337
+ } catch {
17338
+ }
17339
+ }
17340
+ return null;
17341
+ }
17342
+ function hasRedirect(argv) {
17343
+ return argv.some((tok) => REDIRECT_TOKEN_RE.test(tok));
17344
+ }
17345
+ function detectFromCommand(command) {
17346
+ if (!command || command.length > 65536) return null;
17347
+ if (["&&", "||", "$(", "`"].some((op) => command.includes(op))) return null;
17348
+ if (command.includes("|") || command.includes(";")) return null;
17349
+ let argv;
17350
+ try {
17351
+ argv = shlexSplit(command);
17352
+ } catch {
17353
+ return null;
17354
+ }
17355
+ if (hasRedirect(argv)) return null;
17356
+ const filter = selectFilter(argv);
17357
+ if (filter === null) return null;
17358
+ return { filter, argv: stripPrefixes(argv) };
17359
+ }
17360
+ function compressOutput(filter, stdout, stderr, exitCode, argv, opts = {}) {
17361
+ const profile = opts.compressionProfile ?? "balanced";
17362
+ const maxLines = opts.maxLines ?? 1e3;
17363
+ let effectiveMaxLines = PROFILE_CAPS[profile] ?? 200;
17364
+ if (maxLines < effectiveMaxLines) effectiveMaxLines = maxLines;
17365
+ const skipProgress = profile === "minimal";
17366
+ const applyOpts = { maxLines: effectiveMaxLines, skipProgress };
17367
+ if (opts.maxBytes !== void 0) applyOpts.maxBytes = opts.maxBytes;
17368
+ return filter.apply(stdout, stderr, exitCode, argv, applyOpts);
17369
+ }
17370
+ function filterByName(name) {
17371
+ if (name === "generic") return new GenericFilter();
17372
+ for (const f of TOOL_FILTERS) if (f.name === name) return f;
17373
+ return null;
17374
+ }
17375
+
17376
+ // src/hooks_bash.ts
17377
+ function stripCdPrefix(cmd) {
17378
+ const stripped = cmd.replace(/^(?:cd\s+(?:"[^"]*"|'[^']*'|\S+)\s*&&\s*)+/, "");
17379
+ return stripped.trim() || cmd;
17380
+ }
17381
+ function stripOutputPipeline(cmd) {
17382
+ let inSingle = false;
17383
+ let inDouble = false;
17384
+ let cut = cmd.length;
17385
+ for (let i = 0; i < cmd.length; i++) {
17386
+ const ch = cmd[i];
17387
+ if (ch === "'" && !inDouble) {
17388
+ inSingle = !inSingle;
17389
+ } else if (ch === '"' && !inSingle) {
17390
+ inDouble = !inDouble;
17391
+ } else if (ch === "|" && !inSingle && !inDouble) {
17392
+ if (cmd[i + 1] === "|") {
17393
+ i++;
17394
+ continue;
17395
+ }
17396
+ cut = i;
17397
+ break;
17398
+ }
17399
+ }
17400
+ let base = cmd.slice(0, cut);
17401
+ let prev;
17402
+ do {
17403
+ prev = base;
17404
+ const masked = base.replace(/"((?:[^"\\]|\\.)*)"/g, (_m, inner) => '"' + " ".repeat(inner.length) + '"').replace(/'([^']*)'/g, (_m, inner) => "'" + " ".repeat(inner.length) + "'");
17405
+ const newMasked = masked.replace(/\s*(?:[0-9]*>&[0-9]+|[0-9&]*>>?\s*(?:"[^"]*"|'[^']*'|[^\s|]+))\s*$/, "");
17406
+ if (newMasked.length < masked.length) {
17407
+ base = base.slice(0, newMasked.length);
17408
+ }
17409
+ } while (base !== prev);
17410
+ return base.trim();
17411
+ }
17412
+ function extractCommand(event) {
17413
+ const cmd = event.toolInput["command"];
17414
+ return typeof cmd === "string" && cmd.trim() !== "" ? cmd.trim() : void 0;
17415
+ }
17416
+ function isTempPath(fp) {
17417
+ const norm = fp.replace(/\\/g, "/");
17418
+ return /^\/tmp\//i.test(norm) || /\/var\/folders\//i.test(norm) || /AppData\/Local\/Temp\//i.test(norm) || norm.startsWith("/c/Users/") && norm.includes("/AppData/Local/Temp/");
17419
+ }
17420
+ function isOrchestratorStateFile(filePath) {
17421
+ const basename9 = (filePath.includes("/") ? filePath.split("/").at(-1) : filePath.split("\\").at(-1)) ?? filePath;
17422
+ return /^\.improve-state-/.test(basename9);
17423
+ }
17424
+ function extractCatSourceFile(cmd) {
17425
+ const m = /^cat\s+(\S+\.(?:java|py|ts|tsx|js|jsx|go|rb|rs|cpp|cc|cxx|c|h|hpp|kt|swift|cs|php|scala|clj|css|scss|sass|less))\s*$/.exec(cmd);
17426
+ return m?.[1] ?? null;
17427
+ }
17428
+ function extractCatFile(cmd) {
17429
+ const m = /^cat(?:\s+(?:-[a-zA-Z]+|--[a-zA-Z-]+))*\s+(?:"([^"]+)"|'([^']+)'|(\S+))\s*$/.exec(cmd);
17430
+ if (!m) return null;
15965
17431
  const filePath = m[1] ?? m[2] ?? m[3];
15966
17432
  if (filePath === void 0) return null;
15967
17433
  if (isTempPath(filePath)) return null;
15968
- const basename8 = (filePath.includes("/") ? filePath.split("/").at(-1) : filePath.split("\\").at(-1)) ?? filePath;
15969
- const isEnvFile = /^\.env(\.\w+)?$/i.test(basename8);
17434
+ const basename9 = (filePath.includes("/") ? filePath.split("/").at(-1) : filePath.split("\\").at(-1)) ?? filePath;
17435
+ const isEnvFile = /^\.env(\.\w+)?$/i.test(basename9);
15970
17436
  const hasKnownExt = /\.(?:java|py|ts|tsx|js|jsx|go|rb|rs|cpp|cc|cxx|c|h|hpp|kt|swift|cs|php|scala|clj|css|scss|sass|less|md|mdx|rst|txt|json|yaml|yml|toml|xml|conf|cfg|ini|properties|sql|env)$/i.test(filePath);
15971
17437
  if (!hasKnownExt && !isEnvFile) return null;
15972
17438
  const isSql = /\.sql$/i.test(filePath);
@@ -16007,8 +17473,8 @@ function extractWslCatFile(cmd) {
16007
17473
  if (!drive || !pathRest) return null;
16008
17474
  const filePath = drive + ":/" + pathRest;
16009
17475
  if (isTempPath(filePath)) return null;
16010
- const basename8 = (filePath.includes("/") ? filePath.split("/").at(-1) : filePath.split("\\").at(-1)) ?? filePath;
16011
- const isEnvFile = /^\.env(\.\w+)?$/i.test(basename8);
17476
+ const basename9 = (filePath.includes("/") ? filePath.split("/").at(-1) : filePath.split("\\").at(-1)) ?? filePath;
17477
+ const isEnvFile = /^\.env(\.\w+)?$/i.test(basename9);
16012
17478
  const hasKnownExt = /\.(?:java|py|ts|tsx|js|jsx|go|rb|rs|cpp|cc|cxx|c|h|hpp|kt|swift|cs|php|scala|clj|css|scss|sass|less|md|mdx|rst|txt|json|yaml|yml|toml|xml|conf|cfg|ini|properties|sql|env)$/i.test(filePath);
16013
17479
  if (!hasKnownExt && !isEnvFile) return null;
16014
17480
  const isSql = /\.sql$/i.test(filePath);
@@ -16124,7 +17590,7 @@ function extractTasksOutput(cmd) {
16124
17590
  const fp = catM[1] ?? catM[2] ?? catM[3];
16125
17591
  if (fp) {
16126
17592
  const m = taskOutputRe.exec(fp);
16127
- if (m) return { id: m[1] };
17593
+ if (m) return { id: m[1], path: fp };
16128
17594
  }
16129
17595
  }
16130
17596
  if (!/-f\b/.test(cmd) && !/-n\s*\+/.test(cmd)) {
@@ -16136,7 +17602,7 @@ function extractTasksOutput(cmd) {
16136
17602
  if (m) {
16137
17603
  const nStr = tailM[1] ?? tailM[2];
16138
17604
  const n = nStr !== void 0 ? parseInt(nStr, 10) : void 0;
16139
- return n !== void 0 ? { id: m[1], n } : { id: m[1] };
17605
+ return n !== void 0 ? { id: m[1], path: fp, n } : { id: m[1], path: fp };
16140
17606
  }
16141
17607
  }
16142
17608
  }
@@ -16145,7 +17611,7 @@ function extractTasksOutput(cmd) {
16145
17611
  const fp = byteTailM[1] ?? byteTailM[2] ?? byteTailM[3];
16146
17612
  if (fp) {
16147
17613
  const m = taskOutputRe.exec(fp);
16148
- if (m) return { id: m[1] };
17614
+ if (m) return { id: m[1], path: fp };
16149
17615
  }
16150
17616
  }
16151
17617
  }
@@ -16236,6 +17702,53 @@ function buildRecallHint(cmd, outputId) {
16236
17702
  }
16237
17703
  return "Output from a prior `" + cmdPreview + "` run is cached. Use `token-goat bash-output " + outputId + "` (or `--tail 50`, `--grep ERROR`) to re-inspect it without re-running.";
16238
17704
  }
17705
+ function shellQuoteSingle(s) {
17706
+ return `'${s.replace(/'/g, `'\\''`)}'`;
17707
+ }
17708
+ function isCompressibleSingleCommand(cmd) {
17709
+ if (!cmd || cmd.length > 65536) return false;
17710
+ if (["&&", "||", "$(", "`"].some((op) => cmd.includes(op))) return false;
17711
+ if (cmd.includes("|") || cmd.includes(";")) return false;
17712
+ if (/[<>]/.test(cmd)) return false;
17713
+ return true;
17714
+ }
17715
+ function maybeCompressRewrite(event, rawCmd, cmd) {
17716
+ if (process.env["TOKEN_GOAT_BASH_COMPRESS"] === "0") return null;
17717
+ let cfg;
17718
+ try {
17719
+ cfg = loadConfig().bash_compress;
17720
+ } catch {
17721
+ return null;
17722
+ }
17723
+ if (!cfg.enabled) return null;
17724
+ const detected = detectFromCommand(cmd);
17725
+ let filterName;
17726
+ if (detected !== null) {
17727
+ filterName = detected.filter.name;
17728
+ } else if (isCompressibleSingleCommand(cmd)) {
17729
+ filterName = "generic";
17730
+ } else {
17731
+ return null;
17732
+ }
17733
+ if (cfg.disabled_filters.includes(filterName)) return null;
17734
+ const wrapped = `token-goat compress -f ${filterName} -c ${shellQuoteSingle(rawCmd)}`;
17735
+ return { hookType: "rewriteInput", updatedInput: { ...event.toolInput, command: wrapped } };
17736
+ }
17737
+ function unwrapCompressCommand(executed) {
17738
+ const t = executed.trim();
17739
+ if (!/^token-goat\s+compress\b/.test(t)) return null;
17740
+ let argv;
17741
+ try {
17742
+ argv = shlexSplit(t);
17743
+ } catch {
17744
+ return null;
17745
+ }
17746
+ for (let i = 0; i + 1 < argv.length; i++) {
17747
+ const tok = argv[i];
17748
+ if (tok === "-c" || tok === "--cmd") return argv[i + 1] ?? null;
17749
+ }
17750
+ return null;
17751
+ }
16239
17752
  function preBashHandler(event) {
16240
17753
  const rawCmd = extractCommand(event);
16241
17754
  if (rawCmd === void 0) return passOutput();
@@ -16243,10 +17756,11 @@ function preBashHandler(event) {
16243
17756
  const cdStripped = cmd !== rawCmd;
16244
17757
  const taskOutput = extractTasksOutput(cmd);
16245
17758
  if (taskOutput !== null) {
16246
- const { id } = taskOutput;
17759
+ const { id, path: outPath, n } = taskOutput;
16247
17760
  recordStat("session_hint", 0, 0);
17761
+ const tail = n ?? 50;
16248
17762
  return denyOutput(
16249
- "Use `token-goat bash-output " + id + "` to recall this task output (already cached). Append `--tail <n>` or `--grep PATTERN` to slice it."
17763
+ "Task output " + id + ' is on disk. Use `token-goat bash-output --file "' + outPath + '" --tail ' + tail + "` (or `--grep PATTERN`) to read a slice instead of the whole file."
16250
17764
  );
16251
17765
  }
16252
17766
  const findResult = extractFindCommand(cmd);
@@ -16375,9 +17889,9 @@ function preBashHandler(event) {
16375
17889
  if (monitoringHint !== null) {
16376
17890
  const monCmdHash = shortFingerprint(stripOutputPipeline(cmd));
16377
17891
  const monOutputId = getBashOutputId(monCmdHash);
16378
- if (monOutputId !== null) {
16379
- const monEntry = getBashOutput(monOutputId);
16380
- const monBytes = monEntry?.sizeBytes ?? 0;
17892
+ const monEntry = monOutputId !== null ? getBashOutput(monOutputId) : null;
17893
+ if (monOutputId !== null && monEntry !== null) {
17894
+ const monBytes = monEntry.sizeBytes;
16381
17895
  const catFile = extractCatSourceFile(cmd);
16382
17896
  if (catFile !== null) {
16383
17897
  recordStat("bash_compress:recall", monBytes, Math.round(monBytes / 4));
@@ -16406,9 +17920,9 @@ function preBashHandler(event) {
16406
17920
  const curlCacheKey = extractCurlUrl(cmd) ?? cmd;
16407
17921
  const curlHash = shortFingerprint(curlCacheKey);
16408
17922
  const curlOutputId = getBashOutputId(curlHash);
16409
- if (curlOutputId !== null) {
16410
- const curlEntry = getBashOutput(curlOutputId);
16411
- const curlBytes = curlEntry?.sizeBytes ?? 0;
17923
+ const curlEntry = curlOutputId !== null ? getBashOutput(curlOutputId) : null;
17924
+ if (curlOutputId !== null && curlEntry !== null) {
17925
+ const curlBytes = curlEntry.sizeBytes;
16412
17926
  recordStat("bash_compress:recall", curlBytes, Math.round(curlBytes / 4));
16413
17927
  const curlPreview = cmd.length > 60 ? cmd.slice(0, 57) + "..." : cmd;
16414
17928
  return contextOutput(
@@ -16416,14 +17930,16 @@ function preBashHandler(event) {
16416
17930
  );
16417
17931
  }
16418
17932
  }
16419
- if (!isBuildCommand(cmd)) return passOutput();
17933
+ if (!isBuildCommand(cmd) && detectFromCommand(cmd) === null) return passOutput();
16420
17934
  const cmdHash = shortFingerprint(stripOutputPipeline(cmd));
16421
17935
  const outputId = getBashOutputId(cmdHash);
16422
- if (outputId === null) return passOutput();
16423
- const entry = getBashOutput(outputId);
16424
- const bytes = entry?.sizeBytes ?? 0;
16425
- recordStat("bash_compress:recall", bytes, Math.round(bytes / 4));
16426
- return contextOutput(buildRecallHint(cmd, outputId));
17936
+ const entry = outputId !== null ? getBashOutput(outputId) : null;
17937
+ if (outputId !== null && entry !== null) {
17938
+ const bytes = entry.sizeBytes;
17939
+ recordStat("bash_compress:recall", bytes, Math.round(bytes / 4));
17940
+ return contextOutput(buildRecallHint(cmd, outputId));
17941
+ }
17942
+ return maybeCompressRewrite(event, rawCmd, cmd) ?? passOutput();
16427
17943
  }
16428
17944
  registerHook("pre_tool_use", preBashHandler, { toolName: "Bash" });
16429
17945
  var MIN_CACHE_BYTES = 512;
@@ -16438,15 +17954,57 @@ function extractBashOutput(raw) {
16438
17954
  }
16439
17955
  return "";
16440
17956
  }
17957
+ function extractExitCode(raw) {
17958
+ const resp = raw["tool_response"];
17959
+ if (resp !== null && typeof resp === "object") {
17960
+ const r = resp;
17961
+ for (const key of ["exit_code", "exitCode", "returncode", "code"]) {
17962
+ if (typeof r[key] === "number") return r[key];
17963
+ }
17964
+ }
17965
+ return null;
17966
+ }
17967
+ var GH_SECURITY_PATHS = ["/security_advisories", "/advisories", "security_events"];
17968
+ var GH_SCOPE_PHRASES = ["Must have push access", "Resource not accessible by integration", "Must be an admin"];
17969
+ function buildGhApiHint(cmd, stdout, exitCode) {
17970
+ if (stdout === "" || !cmd.startsWith("gh api")) return null;
17971
+ const hints = [];
17972
+ const isSecurityPath = GH_SECURITY_PATHS.some((p) => cmd.includes(p));
17973
+ const hasScopePhrase = GH_SCOPE_PHRASES.some((p) => stdout.includes(p));
17974
+ const failedSecurityCall = exitCode !== null && exitCode !== 0 && isSecurityPath;
17975
+ if (hasScopePhrase || failedSecurityCall) {
17976
+ hints.push("[token-goat] GitHub API scope issue: try gh auth refresh -s security_events");
17977
+ }
17978
+ const trimmed = stdout.trimStart();
17979
+ if (trimmed.startsWith("{")) {
17980
+ try {
17981
+ const parsed = JSON.parse(stdout);
17982
+ if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
17983
+ const keyCount = Object.keys(parsed).length;
17984
+ if (keyCount >= 15) {
17985
+ hints.push(`[token-goat] Large API response (${keyCount} keys). Filter with --jq '.key1,.key2' to reduce tokens.`);
17986
+ }
17987
+ }
17988
+ } catch {
17989
+ }
17990
+ }
17991
+ return hints.length > 0 ? hints.join(" ") : null;
17992
+ }
16441
17993
  async function postBashHandler(event) {
16442
17994
  try {
16443
- const rawCmd = extractCommand(event);
16444
- if (rawCmd === void 0) return passOutput();
17995
+ const rawCmdRaw = extractCommand(event);
17996
+ if (rawCmdRaw === void 0) return passOutput();
17997
+ const rawCmd = unwrapCompressCommand(rawCmdRaw) ?? rawCmdRaw;
16445
17998
  const cmd = stripCdPrefix(rawCmd);
16446
17999
  const curlDl = extractCurlDownload(cmd);
16447
18000
  if (curlDl !== null) {
16448
18001
  recordCurlDownload(curlDl.url, curlDl.outputPath);
16449
18002
  }
18003
+ const ghHint = buildGhApiHint(cmd, extractBashOutput(event.raw), extractExitCode(event.raw));
18004
+ if (ghHint !== null) {
18005
+ recordStat("session_hint", 0, 0);
18006
+ return contextOutput(ghHint);
18007
+ }
16450
18008
  const isMonitoring = getMonitoringRecallHint(cmd) !== null;
16451
18009
  if (!isMonitoring && !isBuildCommand(cmd) && !isCurlGetCommand(cmd)) return passOutput();
16452
18010
  const output = extractBashOutput(event.raw);
@@ -16464,8 +18022,8 @@ registerHook("post_tool_use", postBashHandler, { toolName: "Bash" });
16464
18022
 
16465
18023
  // src/image_shrink.ts
16466
18024
  init_define_import_meta_env();
16467
- import * as fs11 from "node:fs";
16468
- import * as path13 from "node:path";
18025
+ import * as fs13 from "node:fs";
18026
+ import * as path16 from "node:path";
16469
18027
  var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
16470
18028
  ".png",
16471
18029
  ".jpg",
@@ -16492,7 +18050,7 @@ async function loadSharp() {
16492
18050
  return _sharpCache;
16493
18051
  }
16494
18052
  function isImagePath(p) {
16495
- return IMAGE_EXTENSIONS.has(path13.extname(p).toLowerCase());
18053
+ return IMAGE_EXTENSIONS.has(path16.extname(p).toLowerCase());
16496
18054
  }
16497
18055
  async function shrinkImage(input, opts) {
16498
18056
  const maxDimension = opts?.maxDimension ?? DEFAULT_MAX_DIMENSION;
@@ -16524,7 +18082,7 @@ async function shrinkImage(input, opts) {
16524
18082
  }
16525
18083
  function statSize2(absPath) {
16526
18084
  try {
16527
- const st = fs11.statSync(absPath);
18085
+ const st = fs13.statSync(absPath);
16528
18086
  return st.isFile() ? st.size : null;
16529
18087
  } catch {
16530
18088
  return null;
@@ -16538,7 +18096,7 @@ async function preReadImageHandler(event) {
16538
18096
  if (size === null || size < DEFAULT_SIZE_THRESHOLD_BYTES) return passOutput();
16539
18097
  let input;
16540
18098
  try {
16541
- input = fs11.readFileSync(filePath);
18099
+ input = fs13.readFileSync(filePath);
16542
18100
  } catch {
16543
18101
  return passOutput();
16544
18102
  }
@@ -16547,7 +18105,7 @@ async function preReadImageHandler(event) {
16547
18105
  const saved = result.originalBytes - result.shrunkBytes;
16548
18106
  const pct = Math.round(saved / result.originalBytes * 100);
16549
18107
  const dataUrl = `data:image/${result.format};base64,${result.data.toString("base64")}`;
16550
- const summary = `token-goat shrank ${path13.basename(filePath)}: ${Math.round(result.originalBytes / 1024)}kb -> ${Math.round(result.shrunkBytes / 1024)}kb (${pct}% smaller, ${result.width}x${result.height} ${result.format}).`;
18108
+ const summary = `token-goat shrank ${path16.basename(filePath)}: ${Math.round(result.originalBytes / 1024)}kb -> ${Math.round(result.shrunkBytes / 1024)}kb (${pct}% smaller, ${result.width}x${result.height} ${result.format}).`;
16551
18109
  return contextOutput(`${summary}
16552
18110
  ${dataUrl}`);
16553
18111
  }
@@ -16556,7 +18114,7 @@ registerHook("pre_tool_use", preReadImageHandler, { toolName: "Read" });
16556
18114
  // src/relay.ts
16557
18115
  var DEFAULT_STDIN_TIMEOUT_MS = 5e3;
16558
18116
  function readStdinJson(timeoutMs = DEFAULT_STDIN_TIMEOUT_MS) {
16559
- return new Promise((resolve7, reject) => {
18117
+ return new Promise((resolve8, reject) => {
16560
18118
  const chunks = [];
16561
18119
  let settled = false;
16562
18120
  const finish = (fn) => {
@@ -16582,7 +18140,7 @@ function readStdinJson(timeoutMs = DEFAULT_STDIN_TIMEOUT_MS) {
16582
18140
  return;
16583
18141
  }
16584
18142
  try {
16585
- resolve7(JSON.parse(text));
18143
+ resolve8(JSON.parse(text));
16586
18144
  } catch (err2) {
16587
18145
  reject(err2 instanceof Error ? err2 : new Error(String(err2)));
16588
18146
  }
@@ -16617,7 +18175,15 @@ async function relay(eventName) {
16617
18175
  }
16618
18176
  const payload = await readStdinJson();
16619
18177
  const event = buildEvent(eventName, payload);
18178
+ try {
18179
+ loadSessionState(event.sessionId);
18180
+ } catch {
18181
+ }
16620
18182
  const output = await runHook(event);
18183
+ try {
18184
+ saveSessionState(event.sessionId);
18185
+ } catch {
18186
+ }
16621
18187
  process.stdout.write(serializeOutput(output));
16622
18188
  } catch {
16623
18189
  process.stdout.write("{}");
@@ -16626,9 +18192,9 @@ async function relay(eventName) {
16626
18192
 
16627
18193
  // src/install.ts
16628
18194
  init_define_import_meta_env();
16629
- import * as fs12 from "node:fs";
16630
- import * as os3 from "node:os";
16631
- import * as path14 from "node:path";
18195
+ import * as fs14 from "node:fs";
18196
+ import * as os5 from "node:os";
18197
+ import * as path17 from "node:path";
16632
18198
  var HOOK_EVENT_MAP = [
16633
18199
  ["PreToolUse", "pre_tool_use"],
16634
18200
  ["PostToolUse", "post_tool_use"],
@@ -16639,13 +18205,13 @@ function hookCommand(eventArg) {
16639
18205
  return `token-goat hook ${eventArg}`;
16640
18206
  }
16641
18207
  function settingsPath(scope) {
16642
- const base = scope === "user" ? path14.join(os3.homedir(), ".claude") : path14.join(process.cwd(), ".claude");
16643
- return path14.join(base, "settings.json");
18208
+ const base = scope === "user" ? path17.join(os5.homedir(), ".claude") : path17.join(process.cwd(), ".claude");
18209
+ return path17.join(base, "settings.json");
16644
18210
  }
16645
18211
  function readSettings(p) {
16646
18212
  let raw;
16647
18213
  try {
16648
- raw = fs12.readFileSync(p, "utf8");
18214
+ raw = fs14.readFileSync(p, "utf8");
16649
18215
  } catch {
16650
18216
  return {};
16651
18217
  }
@@ -16685,7 +18251,7 @@ function installHooks(scope = "user") {
16685
18251
  return { scope, settingsPath: p, alreadyInstalled: true };
16686
18252
  }
16687
18253
  settings.hooks = hooks;
16688
- fs12.mkdirSync(path14.dirname(p), { recursive: true });
18254
+ fs14.mkdirSync(path17.dirname(p), { recursive: true });
16689
18255
  atomicWriteText(p, `${JSON.stringify(settings, null, 2)}
16690
18256
  `);
16691
18257
  return { scope, settingsPath: p, alreadyInstalled: false };
@@ -16724,7 +18290,7 @@ function uninstallHooks(scope = "user") {
16724
18290
  } else {
16725
18291
  settings.hooks = hooks;
16726
18292
  }
16727
- fs12.mkdirSync(path14.dirname(p), { recursive: true });
18293
+ fs14.mkdirSync(path17.dirname(p), { recursive: true });
16728
18294
  atomicWriteText(p, `${JSON.stringify(settings, null, 2)}
16729
18295
  `);
16730
18296
  return true;
@@ -16733,21 +18299,21 @@ function uninstallHooks(scope = "user") {
16733
18299
  // src/worker.ts
16734
18300
  init_define_import_meta_env();
16735
18301
  import { spawn } from "node:child_process";
16736
- import * as fs13 from "node:fs";
16737
- import * as path15 from "node:path";
18302
+ import * as fs15 from "node:fs";
18303
+ import * as path18 from "node:path";
16738
18304
  import { Worker, isMainThread, parentPort, workerData } from "node:worker_threads";
16739
18305
  import { fileURLToPath } from "node:url";
16740
18306
  var DEFAULT_POLL_INTERVAL_MS = 2e3;
16741
18307
  function dirtyQueuePathFor(dir) {
16742
- return path15.join(dir, "queue", "dirty.txt");
18308
+ return path18.join(dir, "queue", "dirty.txt");
16743
18309
  }
16744
18310
  function workerPidPath(dir = dataDir()) {
16745
- return path15.join(dir, "worker.pid");
18311
+ return path18.join(dir, "worker.pid");
16746
18312
  }
16747
18313
  function getDirtyPathsFor(dir) {
16748
18314
  let raw;
16749
18315
  try {
16750
- raw = fs13.readFileSync(dirtyQueuePathFor(dir), "utf8");
18316
+ raw = fs15.readFileSync(dirtyQueuePathFor(dir), "utf8");
16751
18317
  } catch {
16752
18318
  return [];
16753
18319
  }
@@ -16755,15 +18321,19 @@ function getDirtyPathsFor(dir) {
16755
18321
  const out2 = [];
16756
18322
  for (const line of raw.split("\n")) {
16757
18323
  const trimmed = line.trim();
16758
- if (trimmed === "" || seen.has(trimmed)) continue;
16759
- seen.add(trimmed);
18324
+ if (trimmed === "") continue;
18325
+ const normalized = normalizePath(trimmed);
18326
+ const caseInsensitiveFs = process.platform === "win32" || process.platform === "darwin";
18327
+ const dedupeKey = caseInsensitiveFs ? normalized.toLowerCase() : normalized;
18328
+ if (seen.has(dedupeKey)) continue;
18329
+ seen.add(dedupeKey);
16760
18330
  out2.push(trimmed);
16761
18331
  }
16762
18332
  return out2;
16763
18333
  }
16764
18334
  function clearDirtyQueueFor(dir) {
16765
18335
  try {
16766
- fs13.rmSync(dirtyQueuePathFor(dir), { force: true });
18336
+ fs15.rmSync(dirtyQueuePathFor(dir), { force: true });
16767
18337
  } catch {
16768
18338
  }
16769
18339
  }
@@ -16778,7 +18348,7 @@ function makeIndexer(dbPath) {
16778
18348
  function processDirtyBatch(paths, index = makeIndexer(globalDbPath())) {
16779
18349
  let indexed = 0;
16780
18350
  for (const p of paths) {
16781
- if (!p || !fs13.existsSync(p)) continue;
18351
+ if (!p || !fs15.existsSync(p)) continue;
16782
18352
  const sha = fingerprintFile(p);
16783
18353
  if (sha === null) continue;
16784
18354
  index(p, sha);
@@ -16789,7 +18359,7 @@ function processDirtyBatch(paths, index = makeIndexer(globalDbPath())) {
16789
18359
  function drainOnce(dir, index) {
16790
18360
  const paths = getDirtyPathsFor(dir);
16791
18361
  if (paths.length === 0) return 0;
16792
- const indexed = processDirtyBatch(paths, index ?? makeIndexer(path15.join(dir, "global.db")));
18362
+ const indexed = processDirtyBatch(paths, index ?? makeIndexer(path18.join(dir, "global.db")));
16793
18363
  clearDirtyQueueFor(dir);
16794
18364
  return indexed;
16795
18365
  }
@@ -16803,7 +18373,7 @@ function pidAlive(pid) {
16803
18373
  }
16804
18374
  function readPidFile(dir) {
16805
18375
  try {
16806
- const raw = fs13.readFileSync(workerPidPath(dir), "utf8").trim();
18376
+ const raw = fs15.readFileSync(workerPidPath(dir), "utf8").trim();
16807
18377
  if (!/^\d+$/.test(raw)) return null;
16808
18378
  return parseInt(raw, 10);
16809
18379
  } catch {
@@ -16826,7 +18396,7 @@ function stopWorker(dir = dataDir()) {
16826
18396
  }
16827
18397
  }
16828
18398
  try {
16829
- fs13.rmSync(workerPidPath(dir), { force: true });
18399
+ fs15.rmSync(workerPidPath(dir), { force: true });
16830
18400
  } catch {
16831
18401
  }
16832
18402
  return alive;
@@ -16835,9 +18405,9 @@ function startDetachedWorker(opts) {
16835
18405
  const pollIntervalMs = opts?.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
16836
18406
  const dir = opts?.dataDir ?? dataDir();
16837
18407
  try {
16838
- fs13.mkdirSync(dir, { recursive: true });
18408
+ fs15.mkdirSync(dir, { recursive: true });
16839
18409
  } catch (e) {
16840
- if (e.code !== "EEXIST" || !fs13.existsSync(dir)) throw e;
18410
+ if (e.code !== "EEXIST" || !fs15.existsSync(dir)) throw e;
16841
18411
  }
16842
18412
  const child = spawn(
16843
18413
  process.execPath,
@@ -16857,7 +18427,7 @@ function startDetachedWorker(opts) {
16857
18427
  if (pid === void 0) {
16858
18428
  throw new Error("startDetachedWorker: spawn produced no pid");
16859
18429
  }
16860
- fs13.writeFileSync(workerPidPath(dir), `${pid}
18430
+ fs15.writeFileSync(workerPidPath(dir), `${pid}
16861
18431
  `);
16862
18432
  child.unref();
16863
18433
  return pid;
@@ -16869,7 +18439,7 @@ async function runWorkerLoop(dir, pollIntervalMs, shouldStop = () => false) {
16869
18439
  } catch {
16870
18440
  }
16871
18441
  if (shouldStop()) break;
16872
- await new Promise((resolve7) => setTimeout(resolve7, pollIntervalMs));
18442
+ await new Promise((resolve8) => setTimeout(resolve8, pollIntervalMs));
16873
18443
  }
16874
18444
  }
16875
18445
  function workerEntry() {
@@ -16898,42 +18468,131 @@ function workerEntry() {
16898
18468
  }
16899
18469
  workerEntry();
16900
18470
 
16901
- // src/web_cache.ts
18471
+ // src/bash_runner.ts
16902
18472
  init_define_import_meta_env();
16903
- var _byId2 = /* @__PURE__ */ new Map();
16904
- var _urlIndex = /* @__PURE__ */ new Map();
16905
- function cacheIdForUrl(url) {
16906
- return shortFingerprint(url);
18473
+ import { spawnSync as spawnSync2 } from "node:child_process";
18474
+ import * as os6 from "node:os";
18475
+ var DEFAULT_TIMEOUT_SECONDS = 600;
18476
+ var MAX_CAPTURE_BYTES = 32 * 1024 * 1024;
18477
+ var MIN_RECORD_STAT_BYTES = 32;
18478
+ function resolveFilter(command, filterName) {
18479
+ if (filterName) {
18480
+ const named = filterByName(filterName);
18481
+ if (named !== null) return named;
18482
+ }
18483
+ let argv;
18484
+ try {
18485
+ argv = shlexSplit(command);
18486
+ } catch {
18487
+ return null;
18488
+ }
18489
+ return selectFilter(argv);
16907
18490
  }
16908
- function storeWebOutput(url, content) {
16909
- const cacheId = cacheIdForUrl(url);
16910
- _byId2.set(cacheId, content);
16911
- _urlIndex.set(url, cacheId);
16912
- return cacheId;
18491
+ function signalExitCode(signal) {
18492
+ const num = os6.constants.signals[signal];
18493
+ return 128 + (num ?? 0);
16913
18494
  }
16914
- function getWebOutput(cacheId) {
16915
- return _byId2.get(cacheId) ?? null;
18495
+ function resolveProfile(explicit) {
18496
+ if (explicit) return explicit;
18497
+ try {
18498
+ const p = loadConfig().compression.profile;
18499
+ return p && p !== "auto" ? p : "balanced";
18500
+ } catch {
18501
+ return "balanced";
18502
+ }
16916
18503
  }
16917
- function getWebOutputByUrl(url) {
16918
- const cacheId = _urlIndex.get(url);
16919
- if (cacheId === void 0) return null;
16920
- const content = _byId2.get(cacheId);
16921
- if (content === void 0) return null;
16922
- return { cacheId, content };
18504
+ function run(command, opts = {}) {
18505
+ const timeout = opts.timeout ?? DEFAULT_TIMEOUT_SECONDS;
18506
+ const filter = resolveFilter(command, opts.filterName);
18507
+ if (filter === null) {
18508
+ return passthrough(command, timeout, opts.cwd, opts.env);
18509
+ }
18510
+ return wrapAndCompress(command, filter, timeout, resolveProfile(opts.compressionProfile), opts);
18511
+ }
18512
+ function runRaw(command, timeout = DEFAULT_TIMEOUT_SECONDS) {
18513
+ return passthrough(command, timeout, void 0, void 0);
18514
+ }
18515
+ function passthrough(command, timeout, cwd, env) {
18516
+ const result = spawnSync2(command, {
18517
+ shell: true,
18518
+ stdio: "inherit",
18519
+ timeout: timeout * 1e3,
18520
+ cwd,
18521
+ env
18522
+ });
18523
+ if (isTimeout(result.error)) return 124;
18524
+ if (result.status !== null) return result.status;
18525
+ if (result.signal) return signalExitCode(result.signal);
18526
+ return 0;
18527
+ }
18528
+ function isTimeout(error) {
18529
+ return !!error && error.code === "ETIMEDOUT";
18530
+ }
18531
+ function decode(buf) {
18532
+ if (buf == null) return "";
18533
+ return typeof buf === "string" ? buf : buf.toString("utf8");
18534
+ }
18535
+ function wrapAndCompress(command, filter, timeout, profile, opts) {
18536
+ const writeStdout = opts.writeStdout ?? ((s) => process.stdout.write(s));
18537
+ const start = Date.now();
18538
+ const result = spawnSync2(command, {
18539
+ shell: true,
18540
+ stdio: ["ignore", "pipe", "pipe"],
18541
+ timeout: timeout * 1e3,
18542
+ maxBuffer: MAX_CAPTURE_BYTES,
18543
+ cwd: opts.cwd,
18544
+ env: opts.env
18545
+ });
18546
+ const timedOut = isTimeout(result.error);
18547
+ const overflowed = !!result.error && result.error.code === "ENOBUFS";
18548
+ let stdoutText = decode(result.stdout);
18549
+ let stderrText = decode(result.stderr);
18550
+ if (overflowed) {
18551
+ stdoutText += `
18552
+ [token-goat: capture capped at ${MAX_CAPTURE_BYTES / (1024 * 1024)} MiB]`;
18553
+ }
18554
+ let exitCode;
18555
+ if (timedOut) {
18556
+ exitCode = 124;
18557
+ stderrText = (stderrText ? stderrText + "\n" : "") + `[token-goat: command exceeded ${timeout}s timeout and was killed]`;
18558
+ } else if (result.status !== null) {
18559
+ exitCode = result.status;
18560
+ } else if (result.signal) {
18561
+ exitCode = signalExitCode(result.signal);
18562
+ } else {
18563
+ exitCode = 0;
18564
+ }
18565
+ let argv;
18566
+ try {
18567
+ argv = shlexSplit(command);
18568
+ } catch {
18569
+ argv = [command];
18570
+ }
18571
+ const compressed = compressOutput(filter, stdoutText, stderrText, exitCode, argv, {
18572
+ compressionProfile: profile
18573
+ });
18574
+ let text = compressed.text;
18575
+ const maxTokens = opts.maxTokens ?? 0;
18576
+ if (maxTokens > 0) text = capTokens(text, maxTokens);
18577
+ const marker = compressed.withMarker().slice(compressed.text.length);
18578
+ const body = text + marker;
18579
+ writeStdout(body.endsWith("\n") ? body : body + "\n");
18580
+ recordSavings(compressed, Date.now() - start);
18581
+ return exitCode;
18582
+ }
18583
+ function recordSavings(result, _elapsedMs) {
18584
+ if (result.bytesSaved < MIN_RECORD_STAT_BYTES) return;
18585
+ recordStat(`bash_compress:${result.filterName}`, result.bytesSaved, result.tokensSaved);
16923
18586
  }
16924
- registerReset(() => {
16925
- _byId2 = /* @__PURE__ */ new Map();
16926
- _urlIndex = /* @__PURE__ */ new Map();
16927
- });
16928
18587
 
16929
18588
  // src/read_commands.ts
16930
18589
  init_define_import_meta_env();
16931
- import * as fs14 from "node:fs";
16932
- import * as path16 from "node:path";
18590
+ import * as fs16 from "node:fs";
18591
+ import * as path19 from "node:path";
16933
18592
 
16934
18593
  // src/section_reader.ts
16935
18594
  init_define_import_meta_env();
16936
- import { readFileSync as readFileSync10 } from "node:fs";
18595
+ import { readFileSync as readFileSync12 } from "node:fs";
16937
18596
  function parseHeadingSpec(spec) {
16938
18597
  const m = /^(.*?)#(\d+)$/.exec(spec);
16939
18598
  if (m !== null && m[1] !== void 0 && m[2] !== void 0) {
@@ -17027,7 +18686,7 @@ function sectionEndIndex(headers, headerPos, totalLines) {
17027
18686
  function readSection(filePath, headingSpec) {
17028
18687
  let text;
17029
18688
  try {
17030
- text = readFileSync10(filePath, "utf-8");
18689
+ text = readFileSync12(filePath, "utf-8");
17031
18690
  } catch {
17032
18691
  return null;
17033
18692
  }
@@ -17069,7 +18728,7 @@ function readSection(filePath, headingSpec) {
17069
18728
  function listAllSections(filePath) {
17070
18729
  let text;
17071
18730
  try {
17072
- text = readFileSync10(filePath, "utf-8");
18731
+ text = readFileSync12(filePath, "utf-8");
17073
18732
  } catch {
17074
18733
  return [];
17075
18734
  }
@@ -17083,7 +18742,7 @@ var DIDYOUMEAN_LIMIT = 5;
17083
18742
  var GREP_MAX_LINES = 200;
17084
18743
  function fileExists(p) {
17085
18744
  try {
17086
- fs14.statSync(p);
18745
+ fs16.statSync(p);
17087
18746
  return true;
17088
18747
  } catch {
17089
18748
  return false;
@@ -17091,7 +18750,7 @@ function fileExists(p) {
17091
18750
  }
17092
18751
  function readFileText(p) {
17093
18752
  try {
17094
- return fs14.readFileSync(p, "utf-8");
18753
+ return fs16.readFileSync(p, "utf-8");
17095
18754
  } catch {
17096
18755
  return null;
17097
18756
  }
@@ -17385,7 +19044,7 @@ function runGrep(opts) {
17385
19044
  const hits = [];
17386
19045
  function searchFile(filePath) {
17387
19046
  try {
17388
- const text = fs14.readFileSync(filePath, "utf-8");
19047
+ const text = fs16.readFileSync(filePath, "utf-8");
17389
19048
  const lines = text.split("\n");
17390
19049
  lines.forEach((lineText, idx) => {
17391
19050
  if (regex.test(lineText)) {
@@ -17397,10 +19056,10 @@ function runGrep(opts) {
17397
19056
  }
17398
19057
  function searchDir(dir) {
17399
19058
  try {
17400
- for (const entry of fs14.readdirSync(dir)) {
19059
+ for (const entry of fs16.readdirSync(dir)) {
17401
19060
  if (entry.startsWith(".")) continue;
17402
- const full = path16.join(dir, entry);
17403
- const stat4 = fs14.statSync(full);
19061
+ const full = path19.join(dir, entry);
19062
+ const stat4 = fs16.statSync(full);
17404
19063
  if (stat4.isDirectory()) {
17405
19064
  if (opts.recursive !== false) searchDir(full);
17406
19065
  } else {
@@ -17414,7 +19073,7 @@ function runGrep(opts) {
17414
19073
  emitErr(`Path not found: ${searchPath}`);
17415
19074
  return 1;
17416
19075
  }
17417
- const stat3 = fs14.statSync(searchPath);
19076
+ const stat3 = fs16.statSync(searchPath);
17418
19077
  if (stat3.isDirectory()) {
17419
19078
  searchDir(searchPath);
17420
19079
  } else {
@@ -17443,7 +19102,7 @@ function runConfigGet(opts) {
17443
19102
  emitErr(`Could not read: ${opts.file}`);
17444
19103
  return 1;
17445
19104
  }
17446
- const ext = path16.extname(opts.file).toLowerCase();
19105
+ const ext = path19.extname(opts.file).toLowerCase();
17447
19106
  if (ext === ".json") {
17448
19107
  try {
17449
19108
  let obj = JSON.parse(text);
@@ -17465,10 +19124,22 @@ function runConfigGet(opts) {
17465
19124
  return 1;
17466
19125
  }
17467
19126
  }
17468
- const leafKey = opts.key.split(".").at(-1) ?? opts.key;
19127
+ const keyParts = opts.key.split(".");
19128
+ const leafKey = keyParts.at(-1) ?? opts.key;
19129
+ const sectionPath = keyParts.length > 1 ? keyParts.slice(0, -1).join(".") : null;
17469
19130
  const lines = text.split("\n");
19131
+ let currentSection = "";
17470
19132
  for (const line of lines) {
17471
19133
  const trimmed = line.trim();
19134
+ if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
19135
+ currentSection = trimmed.slice(1, -1);
19136
+ continue;
19137
+ }
19138
+ if (sectionPath !== null) {
19139
+ if (currentSection !== sectionPath) {
19140
+ continue;
19141
+ }
19142
+ }
17472
19143
  if (trimmed.startsWith(`${leafKey} =`) || trimmed.startsWith(`${leafKey}=`)) {
17473
19144
  const eqIdx = trimmed.indexOf("=");
17474
19145
  emit(trimmed.slice(eqIdx + 1).trim().replace(/^["']|["']$/g, ""));
@@ -17524,7 +19195,7 @@ function runExports(opts) {
17524
19195
  names.push(s.name);
17525
19196
  }
17526
19197
  }
17527
- const ext = path16.extname(opts.file).toLowerCase();
19198
+ const ext = path19.extname(opts.file).toLowerCase();
17528
19199
  const text = readFileText(opts.file);
17529
19200
  if (text !== null) {
17530
19201
  if (ext === ".go") {
@@ -17636,7 +19307,7 @@ function runImports(opts) {
17636
19307
  emitErr(`Could not read: ${opts.file}`);
17637
19308
  return 1;
17638
19309
  }
17639
- const imports = extractImports(text, path16.extname(opts.file));
19310
+ const imports = extractImports(text, path19.extname(opts.file));
17640
19311
  if (imports.length === 0) {
17641
19312
  emit(`No imports found in '${opts.file}'`);
17642
19313
  return 0;
@@ -17653,8 +19324,8 @@ function runImports(opts) {
17653
19324
 
17654
19325
  // src/skill_cache.ts
17655
19326
  init_define_import_meta_env();
17656
- import * as fs15 from "fs/promises";
17657
- import { resolve as resolve5 } from "path";
19327
+ import * as fs17 from "fs/promises";
19328
+ import { resolve as resolve6 } from "path";
17658
19329
  var COMPACT_END_MARKER = "<!-- COMPACT_END -->";
17659
19330
  var _skillOutputsDirOverride = null;
17660
19331
  registerReset(() => {
@@ -17662,11 +19333,11 @@ registerReset(() => {
17662
19333
  });
17663
19334
  function skillOutputsDir() {
17664
19335
  if (_skillOutputsDirOverride) return _skillOutputsDirOverride;
17665
- return resolve5(dataDir(), "skills");
19336
+ return resolve6(dataDir(), "skills");
17666
19337
  }
17667
19338
  async function ensureSkillsDir() {
17668
19339
  try {
17669
- await fs15.mkdir(skillOutputsDir(), { recursive: true });
19340
+ await fs17.mkdir(skillOutputsDir(), { recursive: true });
17670
19341
  } catch {
17671
19342
  }
17672
19343
  }
@@ -17685,6 +19356,9 @@ function safeSkillName(skillName) {
17685
19356
  if (!/^[A-Za-z0-9_:-]+$/.test(skillName)) return null;
17686
19357
  return skillName.replace(/[^a-zA-Z0-9_:-]/g, "_");
17687
19358
  }
19359
+ function sanitizeSkillId(name) {
19360
+ return name.replace(/:/g, "_");
19361
+ }
17688
19362
  function extractCompactFromMarker(body) {
17689
19363
  if (!body || !body.includes(COMPACT_END_MARKER)) {
17690
19364
  return null;
@@ -17708,14 +19382,14 @@ function extractCompactFromMarker(body) {
17708
19382
  async function listOutputs() {
17709
19383
  try {
17710
19384
  const dir = skillOutputsDir();
17711
- const entries = await fs15.readdir(dir, { withFileTypes: true });
19385
+ const entries = await fs17.readdir(dir, { withFileTypes: true });
17712
19386
  const metas = [];
17713
19387
  for (const entry of entries) {
17714
19388
  if (!entry.isFile() || !entry.name.endsWith(".meta")) {
17715
19389
  continue;
17716
19390
  }
17717
19391
  try {
17718
- const content = await fs15.readFile(resolve5(dir, entry.name), "utf-8");
19392
+ const content = await fs17.readFile(resolve6(dir, entry.name), "utf-8");
17719
19393
  const meta = JSON.parse(content);
17720
19394
  metas.push(meta);
17721
19395
  } catch {
@@ -17733,14 +19407,14 @@ async function storeCompact(sessionId, skillName, compactText, sourceSha) {
17733
19407
  const name = safeSkillName(skillName);
17734
19408
  if (!name) return;
17735
19409
  const safeSession = safeSessionFragment(sessionId);
17736
- const fileId = `${safeSession}-${name}-compact`;
19410
+ const fileId = `${safeSession}-${sanitizeSkillId(name)}-compact`;
17737
19411
  const dir = skillOutputsDir();
17738
19412
  let text = compactText;
17739
19413
  if (sourceSha) {
17740
19414
  text = `<!-- source_sha: ${sourceSha.slice(0, 12)} -->
17741
19415
  ${text}`;
17742
19416
  }
17743
- await atomicWriteText(resolve5(dir, fileId), text);
19417
+ await atomicWriteText(resolve6(dir, fileId), text);
17744
19418
  } catch {
17745
19419
  }
17746
19420
  }
@@ -17757,16 +19431,16 @@ async function listSkills(sessionId) {
17757
19431
  seen.add(meta.skillName);
17758
19432
  const dir = skillOutputsDir();
17759
19433
  const safeSession = safeSessionFragment(meta.outputId.split("-")[0]);
17760
- const compactFileId = `${safeSession}-${meta.skillName.replace(":", "_")}-compact`;
19434
+ const compactFileId = `${safeSession}-${sanitizeSkillId(meta.skillName)}-compact`;
17761
19435
  let compactLen = 0;
17762
19436
  try {
17763
- const stat3 = await fs15.stat(resolve5(dir, compactFileId));
19437
+ const stat3 = await fs17.stat(resolve6(dir, compactFileId));
17764
19438
  compactLen = stat3.size;
17765
19439
  } catch {
17766
19440
  compactLen = 0;
17767
19441
  }
17768
19442
  const hasMarker = extractCompactFromMarker(
17769
- await fs15.readFile(resolve5(dir, `${meta.outputId}.txt`), "utf-8").catch(() => "")
19443
+ await fs17.readFile(resolve6(dir, `${meta.outputId}.txt`), "utf-8").catch(() => "")
17770
19444
  ) !== null;
17771
19445
  results.push({
17772
19446
  name: meta.skillName,
@@ -17798,9 +19472,9 @@ async function getSkillFilePath(skillName) {
17798
19472
 
17799
19473
  // src/cli_doctor.ts
17800
19474
  init_define_import_meta_env();
17801
- import * as fs16 from "fs";
17802
- import * as path17 from "path";
17803
- import { execSync, spawnSync as spawnSync2 } from "child_process";
19475
+ import * as fs18 from "fs";
19476
+ import * as path20 from "path";
19477
+ import { execSync, spawnSync as spawnSync3 } from "child_process";
17804
19478
  function checkWorkerRunning() {
17805
19479
  try {
17806
19480
  const output = execSync("tasklist", { encoding: "utf-8" });
@@ -17810,8 +19484,8 @@ function checkWorkerRunning() {
17810
19484
  }
17811
19485
  }
17812
19486
  function checkDbExists(dataDir2) {
17813
- const dbPath = path17.join(dataDir2, "index.db");
17814
- if (!fs16.existsSync(dbPath)) {
19487
+ const dbPath = path20.join(dataDir2, "index.db");
19488
+ if (!fs18.existsSync(dbPath)) {
17815
19489
  return {
17816
19490
  name: "Database",
17817
19491
  status: "warn",
@@ -17821,7 +19495,7 @@ function checkDbExists(dataDir2) {
17821
19495
  return {
17822
19496
  name: "Database",
17823
19497
  status: "ok",
17824
- message: `index.db exists (${Math.round(fs16.statSync(dbPath).size / 1024)} KB)`
19498
+ message: `index.db exists (${Math.round(fs18.statSync(dbPath).size / 1024)} KB)`
17825
19499
  };
17826
19500
  }
17827
19501
  function checkInstall() {
@@ -17841,7 +19515,7 @@ function checkInstall() {
17841
19515
  }
17842
19516
  }
17843
19517
  function checkConfigValid(configPath2) {
17844
- if (!fs16.existsSync(configPath2)) {
19518
+ if (!fs18.existsSync(configPath2)) {
17845
19519
  return {
17846
19520
  name: "Config",
17847
19521
  status: "warn",
@@ -17849,7 +19523,7 @@ function checkConfigValid(configPath2) {
17849
19523
  };
17850
19524
  }
17851
19525
  try {
17852
- const content = fs16.readFileSync(configPath2, "utf-8");
19526
+ const content = fs18.readFileSync(configPath2, "utf-8");
17853
19527
  JSON.parse(content);
17854
19528
  return {
17855
19529
  name: "Config",
@@ -17866,7 +19540,7 @@ function checkConfigValid(configPath2) {
17866
19540
  }
17867
19541
  function checkDiskSpace(dataDir2) {
17868
19542
  try {
17869
- const result = spawnSync2("df", ["-h", dataDir2], { encoding: "utf-8" });
19543
+ const result = spawnSync3("df", ["-h", dataDir2], { encoding: "utf-8" });
17870
19544
  const stdout = typeof result.stdout === "string" ? result.stdout : "";
17871
19545
  if (result.error !== void 0 || result.status !== 0 || !stdout) {
17872
19546
  return { name: "Disk Space", status: "warn", message: "could not determine" };
@@ -17887,9 +19561,9 @@ function runDoctor(dataDir2, configPath2) {
17887
19561
  results.push(checkInstall());
17888
19562
  results.push(checkWorkerRunning() ? { name: "Worker", status: "ok", message: "running" } : { name: "Worker", status: "warn", message: "not running" });
17889
19563
  const homeDir = process.env["HOME"] || process.env["USERPROFILE"] || "~";
17890
- const actualDataDir = dataDir2 || path17.join(homeDir, ".token-goat");
19564
+ const actualDataDir = dataDir2 || path20.join(homeDir, ".token-goat");
17891
19565
  results.push(checkDbExists(actualDataDir));
17892
- const actualConfigPath = configPath2 || path17.join(actualDataDir, "config.json");
19566
+ const actualConfigPath = configPath2 || path20.join(actualDataDir, "config.json");
17893
19567
  results.push(checkConfigValid(actualConfigPath));
17894
19568
  results.push(checkDiskSpace(actualDataDir));
17895
19569
  return results;
@@ -18042,11 +19716,16 @@ function cmdSemantic(query, opts) {
18042
19716
  ${previewLines(s.body, 3)}`);
18043
19717
  out(blocks.join("\n\n"));
18044
19718
  }
18045
- function cmdIndex(pathArg) {
19719
+ function cmdIndex(pathArg, opts = {}) {
18046
19720
  const root = pathArg ?? process.cwd();
18047
- const files = getTrackedFiles(root);
19721
+ let files = getTrackedFiles(root);
18048
19722
  if (files.length === 0) {
18049
- throw new CliError(`no tracked files found under '${root}' (is it a git repo?)`);
19723
+ if (opts.walk !== true) {
19724
+ throw new CliError(
19725
+ `no tracked files found under '${root}' (is it a git repo?). Pass --walk to index a non-git folder.`
19726
+ );
19727
+ }
19728
+ files = collectWalkIndexFiles(root);
18050
19729
  }
18051
19730
  let indexed = 0;
18052
19731
  for (const f of files) {
@@ -18149,11 +19828,11 @@ function cmdBashOutput(id, opts) {
18149
19828
  }
18150
19829
  let content;
18151
19830
  try {
18152
- const st = fs17.statSync(opts.file);
19831
+ const st = fs19.statSync(opts.file);
18153
19832
  if (st.isFIFO() || st.isSocket()) {
18154
19833
  throw new CliError(`--file '${opts.file}' is a special file (FIFO or socket) \u2014 only regular files are supported`);
18155
19834
  }
18156
- content = fs17.readFileSync(opts.file, "utf-8");
19835
+ content = fs19.readFileSync(opts.file, "utf-8");
18157
19836
  } catch (e) {
18158
19837
  if (e instanceof CliError) throw e;
18159
19838
  throw new CliError(`cannot read file: ${opts.file}`);
@@ -18188,12 +19867,34 @@ function runExit(fn) {
18188
19867
  process.exitCode = 1;
18189
19868
  }
18190
19869
  }
19870
+ function cmdCompress(opts) {
19871
+ try {
19872
+ if (opts.compress === false) {
19873
+ process.exitCode = runRaw(opts.cmd, parseTimeout(opts.timeout));
19874
+ return;
19875
+ }
19876
+ const maxTokens = opts.maxTokens ? parseInt(opts.maxTokens, 10) || 0 : 0;
19877
+ process.exitCode = run(opts.cmd, {
19878
+ filterName: opts.filter,
19879
+ timeout: parseTimeout(opts.timeout),
19880
+ maxTokens,
19881
+ ...opts.profile !== void 0 ? { compressionProfile: opts.profile } : {}
19882
+ });
19883
+ } catch (e) {
19884
+ err(`token-goat: ${extractErrorMessage(e)}`);
19885
+ process.exitCode = 1;
19886
+ }
19887
+ }
19888
+ function parseTimeout(raw) {
19889
+ const sec = raw ? parseInt(raw, 10) : 0;
19890
+ return Number.isFinite(sec) && sec > 0 ? sec : DEFAULT_TIMEOUT_SECONDS;
19891
+ }
18191
19892
  async function cmdSkillBody(name, opts) {
18192
19893
  const filePath = await getSkillFilePath(name);
18193
19894
  if (filePath === null) {
18194
19895
  throw new CliError(`skill '${name}' not found`);
18195
19896
  }
18196
- const body = fs17.readFileSync(filePath, "utf-8");
19897
+ const body = fs19.readFileSync(filePath, "utf-8");
18197
19898
  if (opts.compact === true) {
18198
19899
  const lines = body.split("\n");
18199
19900
  const end = lines.findIndex((l) => l.includes("COMPACT_END"));
@@ -18211,7 +19912,7 @@ async function cmdSkillCompact(name) {
18211
19912
  if (filePath === null) {
18212
19913
  throw new CliError(`skill '${name}' not found`);
18213
19914
  }
18214
- const body = fs17.readFileSync(filePath, "utf-8");
19915
+ const body = fs19.readFileSync(filePath, "utf-8");
18215
19916
  const sessionFiles = getSessionFiles();
18216
19917
  const sessionId = Array.from(sessionFiles.keys())[0] ?? "default";
18217
19918
  await storeCompact(sessionId, name, body);
@@ -18252,7 +19953,7 @@ async function cmdSkillSize(opts) {
18252
19953
  }
18253
19954
  function atomicWriteBuffer(dest, data) {
18254
19955
  try {
18255
- if (fs17.statSync(dest).isDirectory()) {
19956
+ if (fs19.statSync(dest).isDirectory()) {
18256
19957
  const e = Object.assign(new Error(`EISDIR: illegal operation on a directory, open '${dest}'`), { code: "EISDIR", path: dest });
18257
19958
  throw e;
18258
19959
  }
@@ -18260,16 +19961,16 @@ function atomicWriteBuffer(dest, data) {
18260
19961
  if (e.code !== "ENOENT") throw e;
18261
19962
  }
18262
19963
  const rnd = Math.random().toString(36).slice(2, 8);
18263
- const tmp = path18.join(path18.dirname(path18.resolve(dest)), `.tmp.${process.pid}.${rnd}`);
19964
+ const tmp = path21.join(path21.dirname(path21.resolve(dest)), `.tmp.${process.pid}.${rnd}`);
18264
19965
  try {
18265
- fs17.writeFileSync(tmp, data, { mode: 384 });
19966
+ fs19.writeFileSync(tmp, data, { mode: 384 });
18266
19967
  try {
18267
- fs17.renameSync(tmp, dest);
19968
+ fs19.renameSync(tmp, dest);
18268
19969
  } catch (e) {
18269
19970
  if (e.code === "EXDEV") {
18270
- fs17.copyFileSync(tmp, dest);
19971
+ fs19.copyFileSync(tmp, dest);
18271
19972
  try {
18272
- fs17.unlinkSync(tmp);
19973
+ fs19.unlinkSync(tmp);
18273
19974
  } catch (ue) {
18274
19975
  process.stderr.write(`token-goat write-file: warning: could not remove temp file ${tmp}: ${ue.message}
18275
19976
  `);
@@ -18280,7 +19981,7 @@ function atomicWriteBuffer(dest, data) {
18280
19981
  }
18281
19982
  } catch (e) {
18282
19983
  try {
18283
- fs17.unlinkSync(tmp);
19984
+ fs19.unlinkSync(tmp);
18284
19985
  } catch {
18285
19986
  }
18286
19987
  throw e;
@@ -18290,9 +19991,9 @@ function mapFsError(e, src, dest) {
18290
19991
  const fe = e;
18291
19992
  if (fe.code === "ENOENT") {
18292
19993
  const errPath = fe.path ?? "";
18293
- const isSource = src !== void 0 && path18.resolve(errPath) === path18.resolve(src);
19994
+ const isSource = src !== void 0 && path21.resolve(errPath) === path21.resolve(src);
18294
19995
  if (isSource) throw new CliError(`source file not found: ${src}`);
18295
- const destDir = dest ? path18.dirname(path18.resolve(dest)) : path18.dirname(path18.resolve(errPath || "."));
19996
+ const destDir = dest ? path21.dirname(path21.resolve(dest)) : path21.dirname(path21.resolve(errPath || "."));
18296
19997
  throw new CliError(`destination directory does not exist: ${destDir}`);
18297
19998
  }
18298
19999
  if (fe.code === "ENOTDIR") {
@@ -18300,7 +20001,7 @@ function mapFsError(e, src, dest) {
18300
20001
  }
18301
20002
  if (fe.code === "EISDIR") {
18302
20003
  const errPath = fe.path ?? "";
18303
- const isSource = src !== void 0 && (errPath === "" || path18.resolve(errPath) === path18.resolve(src));
20004
+ const isSource = src !== void 0 && (errPath === "" || path21.resolve(errPath) === path21.resolve(src));
18304
20005
  if (isSource) throw new CliError(`source is a directory, not a file: ${src}`);
18305
20006
  throw new CliError(`destination is a directory, not a file: ${dest ?? (errPath || "(unknown)")}`);
18306
20007
  }
@@ -18366,7 +20067,7 @@ function cmdWriteFile(dest, opts) {
18366
20067
  throw new CliError("destination path contains a null byte");
18367
20068
  }
18368
20069
  if (isWindows()) {
18369
- const base = path18.basename(dest);
20070
+ const base = path21.basename(dest);
18370
20071
  const stem = base.replace(/\.[^.]*$/, "").toUpperCase();
18371
20072
  if (WIN_RESERVED.has(stem)) {
18372
20073
  throw new CliError(`destination '${base}' is a reserved Windows device name`);
@@ -18389,7 +20090,7 @@ function cmdWriteFile(dest, opts) {
18389
20090
  throw new CliError("--from /dev/stdin requires piped input; use piped stdin mode or --b64 for interactive use");
18390
20091
  }
18391
20092
  try {
18392
- const st = fs17.statSync(opts.from);
20093
+ const st = fs19.statSync(opts.from);
18393
20094
  if (st.isFIFO() || st.isSocket()) {
18394
20095
  throw new CliError(`--from '${opts.from}' is a special file (FIFO or socket) \u2014 only regular files are supported`);
18395
20096
  }
@@ -18401,7 +20102,7 @@ function cmdWriteFile(dest, opts) {
18401
20102
  if (st.size > maxFromBytes) {
18402
20103
  throw new CliError(`--from source exceeds size limit (${Math.round(st.size / 1024 / 1024)} MB); set TOKEN_GOAT_MAX_STDIN_MB to override`);
18403
20104
  }
18404
- atomicWriteBuffer(dest, fs17.readFileSync(opts.from));
20105
+ atomicWriteBuffer(dest, fs19.readFileSync(opts.from));
18405
20106
  } catch (e) {
18406
20107
  if (e instanceof CliError) throw e;
18407
20108
  mapFsError(e, opts.from, dest);
@@ -18443,7 +20144,7 @@ function cmdWriteFile(dest, opts) {
18443
20144
  throw new CliError(`TOKEN_GOAT_MAX_STDIN_MB must be a positive integer; got '${process.env["TOKEN_GOAT_MAX_STDIN_MB"] ?? ""}'`);
18444
20145
  }
18445
20146
  const maxBytes = maxMB * 1024 * 1024;
18446
- return new Promise((resolve7, reject) => {
20147
+ return new Promise((resolve8, reject) => {
18447
20148
  const chunks = [];
18448
20149
  let totalBytes = 0;
18449
20150
  let settled = false;
@@ -18466,7 +20167,7 @@ function cmdWriteFile(dest, opts) {
18466
20167
  cleanup();
18467
20168
  try {
18468
20169
  atomicWriteBuffer(dest, Buffer.concat(chunks));
18469
- resolve7();
20170
+ resolve8();
18470
20171
  } catch (e) {
18471
20172
  try {
18472
20173
  mapFsError(e, void 0, dest);
@@ -18569,7 +20270,7 @@ function buildProgram() {
18569
20270
  })
18570
20271
  )
18571
20272
  );
18572
- program2.command("index [path]").description("parse all git-tracked files and (re)build the symbol index").action(guard(cmdIndex));
20273
+ program2.command("index [path]").description("parse all git-tracked files and (re)build the symbol index").option("--walk", "if not a git repo, index a bounded directory walk instead (skips .env / generated / oversized trees)").action(guard(cmdIndex));
18573
20274
  program2.command("map").description("project overview").option("-c, --compact", "compact, low-token summary").action(guard(cmdMap));
18574
20275
  program2.command("hook <event>").description("hook relay entrypoint (reads JSON on stdin)").action(guard(cmdHook));
18575
20276
  program2.command("install").description("install hooks into Claude Code settings").option("-p, --project", "install into project scope instead of user scope").action(guard(cmdInstall));
@@ -18624,6 +20325,7 @@ function buildProgram() {
18624
20325
  program2.command("config-get <file> <key>").description("read one value from a config file (TOML/JSON/YAML/INI)").action((file, key) => runExit(() => runConfigGet({ file, key })));
18625
20326
  program2.command("write-file <dest>").description("write exact bytes to a file \u2014 handles backticks, quotes, $vars, CRLF without escaping\n\nModes: --b64 PAYLOAD (base64), --from SOURCE (copy file), or piped stdin").option("--from <source>", "copy bytes from this source file instead of stdin/base64").option("--b64 <payload>", "decode base64 payload and write to dest").action(guard(cmdWriteFile));
18626
20327
  program2.command("gdrive-sections <file-id>").description("fetch and list sections from a public Google Doc").option("--heading <name>", "get content of one named section").action(guard(cmdGdriveSections));
20328
+ program2.command("compress").description("run a shell command and emit a compressed view of its output").requiredOption("-c, --cmd <command>", "the shell command to run, as one string").option("-f, --filter <name>", "filter name (auto-detected from the command when omitted)").option("--timeout <seconds>", "wall-clock timeout in seconds (0 = built-in default)").option("--no-compress", "stream output raw without compression (debug the wrapper)").option("--profile <name>", "compression profile: aggressive | balanced | minimal").option("--max-tokens <n>", "post-compress token cap (0 = no cap)").action(cmdCompress);
18627
20329
  program2.command("version").description("print the token-goat version").action(
18628
20330
  guard(() => {
18629
20331
  out(VERSION);
@@ -18631,7 +20333,7 @@ function buildProgram() {
18631
20333
  );
18632
20334
  return program2;
18633
20335
  }
18634
- async function run(argv = process.argv) {
20336
+ async function run2(argv = process.argv) {
18635
20337
  const program2 = buildProgram();
18636
20338
  program2.exitOverride();
18637
20339
  try {
@@ -18653,7 +20355,7 @@ async function run(argv = process.argv) {
18653
20355
  }
18654
20356
 
18655
20357
  // src/main.ts
18656
- void run();
20358
+ void run2();
18657
20359
  /*! Bundled license information:
18658
20360
 
18659
20361
  smol-toml/dist/date.js: