token-goat 2.0.3 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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 path15 = __require("node:path");
985
- var fs15 = __require("node:fs");
984
+ var path17 = __require("node:path");
985
+ var fs16 = __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 = path15.resolve(baseDir, baseName);
1918
- if (fs15.existsSync(localBin)) return localBin;
1919
- if (sourceExt.includes(path15.extname(baseName))) return void 0;
1917
+ const localBin = path17.resolve(baseDir, baseName);
1918
+ if (fs16.existsSync(localBin)) return localBin;
1919
+ if (sourceExt.includes(path17.extname(baseName))) return void 0;
1920
1920
  const foundExt = sourceExt.find(
1921
- (ext) => fs15.existsSync(`${localBin}${ext}`)
1921
+ (ext) => fs16.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 = fs15.realpathSync(this._scriptPath);
1933
+ resolvedScriptPath = fs16.realpathSync(this._scriptPath);
1934
1934
  } catch (err2) {
1935
1935
  resolvedScriptPath = this._scriptPath;
1936
1936
  }
1937
- executableDir = path15.resolve(
1938
- path15.dirname(resolvedScriptPath),
1937
+ executableDir = path17.resolve(
1938
+ path17.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 = path15.basename(
1945
+ const legacyName = path17.basename(
1946
1946
  this._scriptPath,
1947
- path15.extname(this._scriptPath)
1947
+ path17.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(path15.extname(executableFile));
1958
+ launchWithNode = sourceExt.includes(path17.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 = path15.basename(filename, path15.extname(filename));
2798
+ this._name = path17.basename(filename, path17.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(path16) {
2813
- if (path16 === void 0) return this._executableDir;
2814
- this._executableDir = path16;
2812
+ executableDir(path18) {
2813
+ if (path18 === void 0) return this._executableDir;
2814
+ this._executableDir = path18;
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 fs15 = __require("fs");
3161
+ var fs16 = __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 readFileSync11 = (path15) => {
3166
- const fd = fs15.openSync(path15, "r");
3165
+ var readFileSync12 = (path17) => {
3166
+ const fd = fs16.openSync(path17, "r");
3167
3167
  const buffer = Buffer.alloc(MAX_LENGTH);
3168
- const bytesRead = fs15.readSync(fd, buffer, 0, MAX_LENGTH, 0);
3169
- fs15.close(fd, () => {
3168
+ const bytesRead = fs16.readSync(fd, buffer, 0, MAX_LENGTH, 0);
3169
+ fs16.close(fd, () => {
3170
3170
  });
3171
3171
  return buffer.subarray(0, bytesRead);
3172
3172
  };
3173
- var readFile3 = (path15) => new Promise((resolve6, reject) => {
3174
- fs15.open(path15, "r", (err2, fd) => {
3173
+ var readFile3 = (path17) => new Promise((resolve6, reject) => {
3174
+ fs16.open(path17, "r", (err2, fd) => {
3175
3175
  if (err2) {
3176
3176
  reject(err2);
3177
3177
  } else {
3178
3178
  const buffer = Buffer.alloc(MAX_LENGTH);
3179
- fs15.read(fd, buffer, 0, MAX_LENGTH, 0, (_, bytesRead) => {
3179
+ fs16.read(fd, buffer, 0, MAX_LENGTH, 0, (_, bytesRead) => {
3180
3180
  resolve6(buffer.subarray(0, bytesRead));
3181
- fs15.close(fd, () => {
3181
+ fs16.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: readFileSync11,
3190
+ readFileSync: readFileSync12,
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: readFileSync11 } = require_filesystem();
3241
+ var { LDD_PATH, SELF_PATH, readFile: readFile3, readFileSync: readFileSync12 } = require_filesystem();
3242
3242
  var { interpreterPath } = require_elf();
3243
3243
  var cachedFamilyInterpreter;
3244
3244
  var cachedFamilyFilesystem;
@@ -3292,11 +3292,11 @@ var require_detect_libc = __commonJS({
3292
3292
  }
3293
3293
  return null;
3294
3294
  };
3295
- var familyFromInterpreterPath = (path15) => {
3296
- if (path15) {
3297
- if (path15.includes("/ld-musl-")) {
3295
+ var familyFromInterpreterPath = (path17) => {
3296
+ if (path17) {
3297
+ if (path17.includes("/ld-musl-")) {
3298
3298
  return MUSL;
3299
- } else if (path15.includes("/ld-linux-")) {
3299
+ } else if (path17.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 = readFileSync11(LDD_PATH);
3333
+ const lddContent = readFileSync12(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 path15 = interpreterPath(selfContent);
3347
- cachedFamilyInterpreter = familyFromInterpreterPath(path15);
3346
+ const path17 = interpreterPath(selfContent);
3347
+ cachedFamilyInterpreter = familyFromInterpreterPath(path17);
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 = readFileSync11(SELF_PATH);
3359
- const path15 = interpreterPath(selfContent);
3360
- cachedFamilyInterpreter = familyFromInterpreterPath(path15);
3358
+ const selfContent = readFileSync12(SELF_PATH);
3359
+ const path17 = interpreterPath(selfContent);
3360
+ cachedFamilyInterpreter = familyFromInterpreterPath(path17);
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 = readFileSync11(LDD_PATH);
3422
+ const lddContent = readFileSync12(LDD_PATH);
3423
3423
  const versionMatch = lddContent.match(RE_GLIBC_VERSION);
3424
3424
  if (versionMatch) {
3425
3425
  cachedVersionFilesystem = versionMatch[1];
@@ -4947,8 +4947,8 @@ 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: spawnSync2 } = __require("node:child_process");
4951
- var { createHash: createHash2 } = __require("node:crypto");
4950
+ var { spawnSync: spawnSync3 } = __require("node:child_process");
4951
+ var { createHash: createHash3 } = __require("node:crypto");
4952
4952
  var semverCoerce = require_coerce();
4953
4953
  var semverGreaterThanOrEqualTo = require_gte();
4954
4954
  var semverSatisfies = require_satisfies();
@@ -5032,12 +5032,12 @@ var require_libvips = __commonJS({
5032
5032
  };
5033
5033
  var isRosetta = () => {
5034
5034
  if (process.platform === "darwin" && process.arch === "x64") {
5035
- const translated = spawnSync2("sysctl sysctl.proc_translated", spawnSyncOptions).stdout;
5035
+ const translated = spawnSync3("sysctl sysctl.proc_translated", spawnSyncOptions).stdout;
5036
5036
  return (translated || "").trim() === "sysctl.proc_translated: 1";
5037
5037
  }
5038
5038
  return false;
5039
5039
  };
5040
- var sha512 = (s) => createHash2("sha512").update(s).digest("hex");
5040
+ var sha512 = (s) => createHash3("sha512").update(s).digest("hex");
5041
5041
  var yarnLocator = () => {
5042
5042
  try {
5043
5043
  const identHash = sha512(`imgsharp-libvips-${buildPlatformArch()}`);
@@ -5047,13 +5047,13 @@ var require_libvips = __commonJS({
5047
5047
  }
5048
5048
  return "";
5049
5049
  };
5050
- var spawnRebuild = () => spawnSync2(`node-gyp rebuild --directory=src ${isEmscripten() ? "--nodedir=emscripten" : ""}`, {
5050
+ var spawnRebuild = () => spawnSync3(`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 = spawnSync2("pkg-config --modversion vips-cpp", {
5056
+ const globalLibvipsVersion2 = spawnSync3("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 = spawnSync2(
5070
+ const brewPkgConfigPath = spawnSync3(
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 path15 of paths) {
5141
+ for (const path17 of paths) {
5142
5142
  try {
5143
- sharp = __require(path15);
5143
+ sharp = __require(path17);
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, isWindows] = ["linux", "darwin", "win32"].map((os3) => runtimePlatform.startsWith(os3));
5152
+ const [isLinux, isMacOs, isWindows2] = ["linux", "darwin", "win32"].map((os4) => runtimePlatform.startsWith(os4));
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 [os3, cpu] = runtimePlatform.split("-");
5170
- const libc = os3.endsWith("musl") ? " --libc=musl" : "";
5169
+ const [os4, cpu] = runtimePlatform.split("-");
5170
+ const libc = os4.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=${os3.replace("musl", "")}${libc} --cpu=${cpu} sharp`
5177
+ ` npm install --os=${os4.replace("musl", "")}${libc} --cpu=${cpu} sharp`
5178
5178
  );
5179
5179
  } else {
5180
5180
  help.push(
@@ -5212,7 +5212,7 @@ var require_sharp = __commonJS({
5212
5212
  if (errors.some((err2) => err2.code === "ERR_DLOPEN_DISABLED")) {
5213
5213
  help.push("- Run Node.js without using the --no-addons flag");
5214
5214
  }
5215
- if (isWindows && /The specified procedure could not be found/.test(messages)) {
5215
+ if (isWindows2 && /The specified procedure could not be found/.test(messages)) {
5216
5216
  help.push(
5217
5217
  "- Using the canvas package on Windows?",
5218
5218
  " See https://sharp.pixelplumbing.com/install#canvas-and-windows",
@@ -6552,15 +6552,15 @@ var require_route = __commonJS({
6552
6552
  };
6553
6553
  }
6554
6554
  function wrapConversion(toModel, graph) {
6555
- const path15 = [graph[toModel].parent, toModel];
6555
+ const path17 = [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
- path15.unshift(graph[cur].parent);
6559
+ path17.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 = path15;
6563
+ fn.conversion = path17;
6564
6564
  return fn;
6565
6565
  }
6566
6566
  module.exports = function(fromModel) {
@@ -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 path15 = __require("node:path");
8467
+ var path17 = __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) && path15.resolve(this.options.input.file) === path15.resolve(fileOut)) {
8498
+ } else if (is.string(this.options.input.file) && path17.resolve(this.options.input.file) === path17.resolve(fileOut)) {
8499
8499
  err2 = new Error("Cannot use same file for input and output");
8500
- } else if (jp2Regex.test(path15.extname(fileOut)) && !this.constructor.format.jp2k.output.file) {
8500
+ } else if (jp2Regex.test(path17.extname(fileOut)) && !this.constructor.format.jp2k.output.file) {
8501
8501
  err2 = errJp2Save();
8502
8502
  }
8503
8503
  if (err2) {
@@ -9475,13 +9475,13 @@ var {
9475
9475
  } = import_index.default;
9476
9476
 
9477
9477
  // src/cli.ts
9478
- import * as fs14 from "fs";
9479
- import * as path14 from "path";
9478
+ import * as fs15 from "fs";
9479
+ import * as path16 from "path";
9480
9480
 
9481
9481
  // src/baseline.ts
9482
9482
  init_define_import_meta_env();
9483
9483
  import * as fs2 from "node:fs";
9484
- import * as path4 from "node:path";
9484
+ import * as path5 from "node:path";
9485
9485
 
9486
9486
  // src/constants.ts
9487
9487
  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.0.3";
9496
+ return "2.2.0";
9497
9497
  }
9498
9498
  const require2 = createRequire(import.meta.url);
9499
9499
  const pkg = require2("../package.json");
@@ -9543,9 +9543,42 @@ function configPath() {
9543
9543
  init_define_import_meta_env();
9544
9544
  import * as fs from "node:fs";
9545
9545
  import { createRequire as createRequire2 } from "node:module";
9546
- import * as path2 from "node:path";
9546
+ import * as path3 from "node:path";
9547
9547
  import Database from "better-sqlite3";
9548
9548
 
9549
+ // src/paths.ts
9550
+ init_define_import_meta_env();
9551
+ import * as path2 from "node:path";
9552
+ var WSL_PATH_RE = /^\/mnt\/([a-zA-Z])\/(.*)$/s;
9553
+ function normalizePath(p) {
9554
+ let s = p;
9555
+ if (s.includes("\\")) {
9556
+ s = s.replace(/\\/g, "/");
9557
+ }
9558
+ const m = WSL_PATH_RE.exec(s);
9559
+ if (m) {
9560
+ const driveLetter = m[1].toLowerCase();
9561
+ const rest = m[2];
9562
+ const restStripped = rest.replace(/^\/+/, "");
9563
+ s = `${driveLetter}:/${restStripped}`;
9564
+ }
9565
+ if (s.length >= 2 && s[1] === ":") {
9566
+ const c = s[0];
9567
+ if (/^[A-Z]$/.test(c)) {
9568
+ s = c.toLowerCase() + s.slice(1);
9569
+ }
9570
+ }
9571
+ return s;
9572
+ }
9573
+ function safeJoin(base, ...parts) {
9574
+ for (const part of parts) {
9575
+ if (part.includes(":")) {
9576
+ throw new Error(`safeJoin: path component contains colon: ${JSON.stringify(part)}`);
9577
+ }
9578
+ }
9579
+ return path2.join(base, ...parts);
9580
+ }
9581
+
9549
9582
  // src/reset.ts
9550
9583
  init_define_import_meta_env();
9551
9584
  var _resets = [];
@@ -9589,6 +9622,16 @@ CREATE TABLE IF NOT EXISTS refs (
9589
9622
  );
9590
9623
  CREATE INDEX IF NOT EXISTS idx_refs_name ON refs(name);
9591
9624
  CREATE INDEX IF NOT EXISTS idx_refs_file ON refs(file_path);
9625
+
9626
+ CREATE TABLE IF NOT EXISTS chunks (
9627
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
9628
+ file_path TEXT,
9629
+ start_line INTEGER,
9630
+ end_line INTEGER,
9631
+ text TEXT,
9632
+ kind TEXT
9633
+ );
9634
+ CREATE INDEX IF NOT EXISTS idx_chunks_file ON chunks(file_path);
9592
9635
  `;
9593
9636
  var FTS_SQL = `
9594
9637
  CREATE VIRTUAL TABLE IF NOT EXISTS symbols_fts USING fts5(
@@ -9636,15 +9679,15 @@ function initConnection(conn) {
9636
9679
  }
9637
9680
  }
9638
9681
  function resolveDbPath(dbPath) {
9639
- if (path2.isAbsolute(dbPath)) return dbPath;
9640
- if (dbPath.includes("/") || dbPath.includes("\\")) return path2.resolve(dbPath);
9641
- return path2.join(dataDir(), dbPath);
9682
+ if (path3.isAbsolute(dbPath)) return dbPath;
9683
+ if (dbPath.includes("/") || dbPath.includes("\\")) return path3.resolve(dbPath);
9684
+ return safeJoin(dataDir(), dbPath);
9642
9685
  }
9643
9686
  function getDb(dbPath) {
9644
9687
  const resolved = resolveDbPath(dbPath);
9645
9688
  const existing = _connections.get(resolved);
9646
9689
  if (existing !== void 0) return existing;
9647
- const dir = path2.dirname(resolved);
9690
+ const dir = path3.dirname(resolved);
9648
9691
  try {
9649
9692
  fs.mkdirSync(dir, { recursive: true });
9650
9693
  } catch (e) {
@@ -9668,7 +9711,7 @@ registerReset(closeAllDbs);
9668
9711
 
9669
9712
  // src/parser_types.ts
9670
9713
  init_define_import_meta_env();
9671
- import * as path3 from "node:path";
9714
+ import * as path4 from "node:path";
9672
9715
  var EXTENSION_LANGUAGE = /* @__PURE__ */ new Map([
9673
9716
  [".py", "python"],
9674
9717
  [".pyi", "python"],
@@ -9738,10 +9781,10 @@ var FILENAME_LANGUAGE = /* @__PURE__ */ new Map([
9738
9781
  [".envrc", "env_file"]
9739
9782
  ]);
9740
9783
  function detectLanguage(filePath) {
9741
- const base = path3.basename(filePath).toLowerCase();
9784
+ const base = path4.basename(filePath).toLowerCase();
9742
9785
  const byName = FILENAME_LANGUAGE.get(base);
9743
9786
  if (byName !== void 0) return byName;
9744
- const ext = path3.extname(base).toLowerCase();
9787
+ const ext = path4.extname(base).toLowerCase();
9745
9788
  return EXTENSION_LANGUAGE.get(ext) ?? "unknown";
9746
9789
  }
9747
9790
 
@@ -9782,7 +9825,7 @@ function walkProject(rootDir) {
9782
9825
  continue;
9783
9826
  }
9784
9827
  for (const entry of entries) {
9785
- const full = path4.join(dir, entry.name);
9828
+ const full = path5.join(dir, entry.name);
9786
9829
  if (entry.isDirectory()) {
9787
9830
  if (SKIP_DIRS.has(entry.name)) continue;
9788
9831
  if (entry.name.startsWith(".") && entry.name !== ".") {
@@ -9825,7 +9868,7 @@ function fetchTopSymbols(limit, dbPath) {
9825
9868
  }
9826
9869
  }
9827
9870
  function buildProjectMap(rootDir = process.cwd(), opts = {}) {
9828
- const root = path4.resolve(rootDir);
9871
+ const root = path5.resolve(rootDir);
9829
9872
  const { files, languages } = walkProject(root);
9830
9873
  const symbolLimit = opts.compact ? 10 : 30;
9831
9874
  const topSymbols = fetchTopSymbols(symbolLimit, globalDbPath());
@@ -9837,7 +9880,7 @@ function buildProjectMap(rootDir = process.cwd(), opts = {}) {
9837
9880
  mtime = 0;
9838
9881
  }
9839
9882
  return { f, mtime };
9840
- }).sort((a, b) => b.mtime - a.mtime).slice(0, opts.compact ? 5 : 15).map((x) => path4.relative(root, x.f));
9883
+ }).sort((a, b) => b.mtime - a.mtime).slice(0, opts.compact ? 5 : 15).map((x) => path5.relative(root, x.f));
9841
9884
  return {
9842
9885
  rootDir: root,
9843
9886
  fileCount: files.length,
@@ -9848,7 +9891,7 @@ function buildProjectMap(rootDir = process.cwd(), opts = {}) {
9848
9891
  }
9849
9892
  function formatProjectMap(map, compact = false) {
9850
9893
  const lines = [];
9851
- const rel = path4.basename(map.rootDir);
9894
+ const rel = path5.basename(map.rootDir);
9852
9895
  lines.push(`# Project map: ${rel}`);
9853
9896
  lines.push(`Files: ${map.fileCount}`);
9854
9897
  const langPairs = Object.entries(map.languages).sort((a, b) => b[1] - a[1]);
@@ -9861,7 +9904,7 @@ function formatProjectMap(map, compact = false) {
9861
9904
  if (compact) {
9862
9905
  lines.push(`- ${s.name} (${s.kind})`);
9863
9906
  } else {
9864
- const loc = `${path4.basename(s.filePath)}:${s.lineStart}-${s.lineEnd}`;
9907
+ const loc = `${path5.basename(s.filePath)}:${s.lineStart}-${s.lineEnd}`;
9865
9908
  lines.push(`- ${s.name} (${s.kind}) \u2014 ${loc}`);
9866
9909
  }
9867
9910
  }
@@ -9878,42 +9921,19 @@ function formatProjectMap(map, compact = false) {
9878
9921
 
9879
9922
  // src/repomap.ts
9880
9923
  init_define_import_meta_env();
9881
- import * as path5 from "path";
9924
+ import * as path6 from "path";
9882
9925
 
9883
9926
  // src/util.ts
9884
9927
  init_define_import_meta_env();
9885
9928
  import { spawnSync } from "node:child_process";
9886
9929
  import { closeSync, openSync, renameSync, unlinkSync, writeSync } from "node:fs";
9887
-
9888
- // src/paths.ts
9889
- init_define_import_meta_env();
9890
- var WSL_PATH_RE = /^\/mnt\/([a-zA-Z])\/(.*)$/s;
9891
- function normalizePath(p) {
9892
- let s = p;
9893
- if (s.includes("\\")) {
9894
- s = s.replace(/\\/g, "/");
9895
- }
9896
- const m = WSL_PATH_RE.exec(s);
9897
- if (m) {
9898
- const driveLetter = m[1].toLowerCase();
9899
- const rest = m[2];
9900
- const restStripped = rest.replace(/^\/+/, "");
9901
- s = `${driveLetter}:/${restStripped}`;
9902
- }
9903
- if (s.length >= 2 && s[1] === ":") {
9904
- const c = s[0];
9905
- if (/^[A-Z]$/.test(c)) {
9906
- s = c.toLowerCase() + s.slice(1);
9907
- }
9908
- }
9909
- return s;
9910
- }
9911
-
9912
- // src/util.ts
9913
9930
  function sleepSync(ms) {
9914
9931
  if (ms <= 0) return;
9915
9932
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
9916
9933
  }
9934
+ function isWindows() {
9935
+ return process.platform === "win32";
9936
+ }
9917
9937
  function runGit(args, opts = {}) {
9918
9938
  const fullArgs = ["-c", "core.fsmonitor=", ...args];
9919
9939
  const result = spawnSync("git", fullArgs, {
@@ -9979,6 +9999,21 @@ function atomicWriteText(filePath, content) {
9979
9999
  function atomicWriteBytes(filePath, content) {
9980
10000
  atomicWriteCore(filePath, content);
9981
10001
  }
10002
+ function ensureNewline(text) {
10003
+ return text.endsWith("\n") ? text : text + "\n";
10004
+ }
10005
+ function extractErrorMessage(err2, fallback = "") {
10006
+ return err2 instanceof Error ? err2.message : fallback || String(err2);
10007
+ }
10008
+ function isCodeFenceDelimiter(line) {
10009
+ const s = line.trim();
10010
+ return s.startsWith("```") || s.startsWith("~~~");
10011
+ }
10012
+ function normalizePathForwardSlash(p, toLowerCase) {
10013
+ let result = normalizePath(p).replace(/\\/g, "/");
10014
+ if (toLowerCase) result = result.toLowerCase();
10015
+ return result;
10016
+ }
9982
10017
 
9983
10018
  // src/index_reader.ts
9984
10019
  init_define_import_meta_env();
@@ -10124,7 +10159,7 @@ function isNoisePath(inputPath) {
10124
10159
  if (!inputPath) {
10125
10160
  return false;
10126
10161
  }
10127
- const p = normalizePath(inputPath).toLowerCase().replace(/\\/g, "/");
10162
+ const p = normalizePathForwardSlash(inputPath, true);
10128
10163
  for (const segment of NOISE_SEGMENTS) {
10129
10164
  if (p.includes(segment)) {
10130
10165
  return true;
@@ -10162,7 +10197,7 @@ function getTrackedFiles(cwd = process.cwd()) {
10162
10197
  if (result.exitCode !== 0 || !result.stdout) {
10163
10198
  return [];
10164
10199
  }
10165
- return result.stdout.split("\n").filter((line) => line.trim().length > 0).map((rel) => path5.join(cwd, rel));
10200
+ return result.stdout.split("\n").filter((line) => line.trim().length > 0).map((rel) => path6.join(cwd, rel));
10166
10201
  } catch {
10167
10202
  return [];
10168
10203
  }
@@ -10218,7 +10253,7 @@ function formatMap(entries, opts = {}) {
10218
10253
  lines.push("");
10219
10254
  lines.push("## Files");
10220
10255
  for (const e of shown) {
10221
- const rel = path5.relative(cwd, e.filePath);
10256
+ const rel = path6.relative(cwd, e.filePath);
10222
10257
  if (compact) {
10223
10258
  lines.push(`- ${rel} (${e.language})`);
10224
10259
  } else {
@@ -10236,11 +10271,13 @@ function formatMap(entries, opts = {}) {
10236
10271
 
10237
10272
  // src/session.ts
10238
10273
  init_define_import_meta_env();
10274
+ import { randomBytes, randomUUID } from "node:crypto";
10239
10275
  import * as fs3 from "node:fs";
10240
10276
  var _files = /* @__PURE__ */ new Map();
10241
10277
  var _hintsShown = /* @__PURE__ */ new Set();
10242
10278
  var _webFetches = /* @__PURE__ */ new Map();
10243
10279
  var _bashOutputs = /* @__PURE__ */ new Map();
10280
+ var _curlDownloads = /* @__PURE__ */ new Map();
10244
10281
  var _sessionId = null;
10245
10282
  function fileSize(absPath) {
10246
10283
  try {
@@ -10265,10 +10302,9 @@ function recordFileRead(filePath) {
10265
10302
  return;
10266
10303
  }
10267
10304
  _files.set(key, {
10268
- path: key,
10305
+ ...prev,
10269
10306
  readCount: prev.readCount + 1,
10270
10307
  lastReadAt: now,
10271
- wasEdited: prev.wasEdited,
10272
10308
  sizeBytes: size
10273
10309
  });
10274
10310
  }
@@ -10309,11 +10345,51 @@ function recordBashOutput(commandHash2, outputId, _sizeBytes) {
10309
10345
  function getBashOutputId(commandHash2) {
10310
10346
  return _bashOutputs.get(commandHash2) ?? null;
10311
10347
  }
10348
+ function recordCurlDownload(url, savedPath) {
10349
+ _curlDownloads.set(url, savedPath);
10350
+ }
10351
+ function getCurlDownloadPath(url) {
10352
+ return _curlDownloads.get(url) ?? null;
10353
+ }
10354
+ function markFileTruncated(filePath) {
10355
+ const key = normalizePath(filePath);
10356
+ const prev = _files.get(key);
10357
+ if (prev === void 0) {
10358
+ _files.set(key, {
10359
+ path: key,
10360
+ readCount: 1,
10361
+ lastReadAt: Date.now(),
10362
+ wasEdited: false,
10363
+ sizeBytes: fileSize(key),
10364
+ wasTruncated: true
10365
+ });
10366
+ return;
10367
+ }
10368
+ _files.set(key, { ...prev, wasTruncated: true });
10369
+ }
10370
+ function wasFileTruncatedThisSession(filePath) {
10371
+ const entry = _files.get(normalizePath(filePath));
10372
+ return entry?.wasTruncated === true;
10373
+ }
10374
+ function generateSessionId() {
10375
+ try {
10376
+ return randomUUID();
10377
+ } catch {
10378
+ return randomBytes(16).toString("hex");
10379
+ }
10380
+ }
10381
+ function getSessionId() {
10382
+ if (_sessionId !== null) return _sessionId;
10383
+ const fromEnv = process.env["CLAUDE_SESSION_ID"];
10384
+ _sessionId = fromEnv !== void 0 && fromEnv !== "" ? fromEnv : generateSessionId();
10385
+ return _sessionId;
10386
+ }
10312
10387
  registerReset(() => {
10313
10388
  _files = /* @__PURE__ */ new Map();
10314
10389
  _hintsShown = /* @__PURE__ */ new Set();
10315
10390
  _webFetches = /* @__PURE__ */ new Map();
10316
10391
  _bashOutputs = /* @__PURE__ */ new Map();
10392
+ _curlDownloads = /* @__PURE__ */ new Map();
10317
10393
  _sessionId = null;
10318
10394
  });
10319
10395
 
@@ -10373,8 +10449,8 @@ var HOOK_EVENTS = [
10373
10449
 
10374
10450
  // src/hooks_read.ts
10375
10451
  init_define_import_meta_env();
10376
- import * as fs4 from "node:fs";
10377
- import * as path7 from "node:path";
10452
+ import * as fs5 from "node:fs";
10453
+ import * as path9 from "node:path";
10378
10454
 
10379
10455
  // src/hooks_common.ts
10380
10456
  init_define_import_meta_env();
@@ -10398,6 +10474,166 @@ function contextOutput(context) {
10398
10474
  return { hookType: "context", context };
10399
10475
  }
10400
10476
 
10477
+ // src/snapshots.ts
10478
+ init_define_import_meta_env();
10479
+ import * as fs4 from "node:fs";
10480
+ import * as path7 from "node:path";
10481
+ import * as crypto from "node:crypto";
10482
+ import * as os2 from "node:os";
10483
+ var MAX_SNAPSHOTS_PER_SESSION = 150;
10484
+ var MAX_SNAPSHOT_BYTES = 256 * 1024;
10485
+ var SNAPSHOT_TRUNCATE_BYTES = 50 * 1024;
10486
+ var KIND_READ = "read";
10487
+ var KIND_PREDICTIVE = "predictive";
10488
+ var VALID_KINDS = /* @__PURE__ */ new Set([KIND_READ, KIND_PREDICTIVE]);
10489
+ var _TRUNCATED_MARKER = Buffer.from("\n<snapshot truncated at ");
10490
+ var SESSION_DIR_RE = /[^a-zA-Z0-9_-]/g;
10491
+ function sessionDir(sessionId) {
10492
+ if (!sessionId) return null;
10493
+ const safe = sessionId.replace(SESSION_DIR_RE, "_").slice(0, 64) || "anon";
10494
+ const base = path7.join(os2.homedir(), ".token-goat", "session_snapshots");
10495
+ const candidate = path7.join(base, safe);
10496
+ try {
10497
+ const rel = path7.relative(base, candidate);
10498
+ if (rel.startsWith("..")) return null;
10499
+ } catch {
10500
+ return null;
10501
+ }
10502
+ return candidate;
10503
+ }
10504
+ function pathKey(filePath) {
10505
+ return crypto.createHash("sha256").update(filePath, "utf8").digest("hex").slice(0, 32);
10506
+ }
10507
+ function snapshot_path(sessionId, filePath) {
10508
+ const d = sessionDir(sessionId);
10509
+ if (!d) return null;
10510
+ return path7.join(d, `${pathKey(filePath)}.bin`);
10511
+ }
10512
+ function kindSidecarPath(snapshotPath) {
10513
+ return snapshotPath + ".kind";
10514
+ }
10515
+ function writeSnapshotKind(sidecarPath, kind) {
10516
+ try {
10517
+ const safeKind = VALID_KINDS.has(kind) ? kind : KIND_READ;
10518
+ const dir = path7.dirname(sidecarPath);
10519
+ if (!fs4.existsSync(dir)) {
10520
+ fs4.mkdirSync(dir, { recursive: true });
10521
+ }
10522
+ fs4.writeFileSync(sidecarPath, safeKind, "utf8");
10523
+ return true;
10524
+ } catch {
10525
+ return false;
10526
+ }
10527
+ }
10528
+ function evictOldest(d, maxCount) {
10529
+ try {
10530
+ const entries = [];
10531
+ const files = fs4.readdirSync(d);
10532
+ for (const file of files) {
10533
+ const fullPath = path7.join(d, file);
10534
+ if (!file.endsWith(".bin")) continue;
10535
+ try {
10536
+ const stat3 = fs4.statSync(fullPath);
10537
+ entries.push([fullPath, stat3.mtimeMs]);
10538
+ } catch {
10539
+ continue;
10540
+ }
10541
+ }
10542
+ if (entries.length <= maxCount) return 0;
10543
+ entries.sort((a, b) => a[1] - b[1]);
10544
+ let removed = 0;
10545
+ const over = entries.length - maxCount;
10546
+ for (const [p] of entries.slice(0, over)) {
10547
+ try {
10548
+ fs4.unlinkSync(p);
10549
+ removed++;
10550
+ try {
10551
+ fs4.unlinkSync(kindSidecarPath(p));
10552
+ } catch {
10553
+ }
10554
+ } catch {
10555
+ continue;
10556
+ }
10557
+ }
10558
+ return removed;
10559
+ } catch {
10560
+ return 0;
10561
+ }
10562
+ }
10563
+ function store(sessionId, filePath, content, opts = {}) {
10564
+ const kind = opts.kind ?? KIND_READ;
10565
+ const origLen = content.length;
10566
+ if (origLen > MAX_SNAPSHOT_BYTES) {
10567
+ return null;
10568
+ }
10569
+ let stored = content;
10570
+ if (origLen > SNAPSHOT_TRUNCATE_BYTES) {
10571
+ const marker = Buffer.from(`
10572
+ <snapshot truncated at ${origLen} bytes>
10573
+ `);
10574
+ stored = Buffer.concat([content.slice(0, SNAPSHOT_TRUNCATE_BYTES), marker]);
10575
+ }
10576
+ const p = snapshot_path(sessionId, filePath);
10577
+ if (!p) return null;
10578
+ const sha = crypto.createHash("sha256").update(stored).digest("hex");
10579
+ try {
10580
+ if (fs4.existsSync(p)) {
10581
+ try {
10582
+ const existing = fs4.readFileSync(p);
10583
+ if (Buffer.from(existing).equals(stored)) {
10584
+ return {
10585
+ path: p,
10586
+ content_sha: sha,
10587
+ size_bytes: stored.length
10588
+ };
10589
+ }
10590
+ } catch {
10591
+ }
10592
+ }
10593
+ const dir = path7.dirname(p);
10594
+ if (!fs4.existsSync(dir)) {
10595
+ fs4.mkdirSync(dir, { recursive: true });
10596
+ }
10597
+ evictOldest(dir, MAX_SNAPSHOTS_PER_SESSION - 1);
10598
+ const tempPath = p + ".tmp";
10599
+ fs4.writeFileSync(tempPath, stored);
10600
+ fs4.renameSync(tempPath, p);
10601
+ const sidecar = kindSidecarPath(p);
10602
+ writeSnapshotKind(sidecar, kind);
10603
+ return {
10604
+ path: p,
10605
+ content_sha: sha,
10606
+ size_bytes: stored.length
10607
+ };
10608
+ } catch {
10609
+ return null;
10610
+ }
10611
+ }
10612
+ function load(sessionId, filePath, opts = {}) {
10613
+ const p = snapshot_path(sessionId, filePath);
10614
+ if (!p || !fs4.existsSync(p)) return null;
10615
+ try {
10616
+ const stat3 = fs4.statSync(p);
10617
+ if (stat3.size > MAX_SNAPSHOT_BYTES) {
10618
+ return null;
10619
+ }
10620
+ } catch {
10621
+ return null;
10622
+ }
10623
+ try {
10624
+ const data = fs4.readFileSync(p);
10625
+ if (opts.expected_sha) {
10626
+ const actualSha = crypto.createHash("sha256").update(data).digest("hex");
10627
+ if (actualSha.toLowerCase() !== opts.expected_sha.toLowerCase()) {
10628
+ return null;
10629
+ }
10630
+ }
10631
+ return data;
10632
+ } catch {
10633
+ return null;
10634
+ }
10635
+ }
10636
+
10401
10637
  // src/hints.ts
10402
10638
  init_define_import_meta_env();
10403
10639
  var HINT_PRIORITY_MEDIUM = 3;
@@ -10430,11 +10666,11 @@ function buildPackageManifestHint(options) {
10430
10666
  return null;
10431
10667
  }
10432
10668
  }
10433
- function _sanitizeHintPath(path15) {
10434
- if (typeof path15 !== "string") {
10669
+ function _sanitizeHintPath(path17) {
10670
+ if (typeof path17 !== "string") {
10435
10671
  return "???";
10436
10672
  }
10437
- return path15.replace(/[\x00]/g, "").slice(0, 200);
10673
+ return path17.replace(/[\x00]/g, "").slice(0, 200);
10438
10674
  }
10439
10675
 
10440
10676
  // src/hints/lang_patterns.ts
@@ -10567,6 +10803,10 @@ var BUILD_COMMAND_PATTERNS = [
10567
10803
  // Turbo
10568
10804
  /^\s*turbo\s+(build|dev)\b/i
10569
10805
  ];
10806
+ var LOCK_FILE_COUNT = LOCK_FILE_NAMES.size;
10807
+ var MANIFEST_FILE_COUNT = MANIFEST_FILE_NAMES.size + MANIFEST_EXTENSIONS.size + MANIFEST_BASENAME_PATTERNS.length;
10808
+ var BUILD_DIR_COUNT = BUILD_DIR_NAMES.size;
10809
+ var GENERATED_EXT_COUNT = ALWAYS_GENERATED_EXTS.size + CONDITIONALLY_GENERATED_EXTS.size;
10570
10810
  function isLockFile(basename7) {
10571
10811
  return LOCK_FILE_NAMES.has(basename7.toLowerCase());
10572
10812
  }
@@ -10870,7 +11110,7 @@ function dispatchFileTypeHandler(filePath, content, contentLengthHint) {
10870
11110
 
10871
11111
  // src/stats.ts
10872
11112
  init_define_import_meta_env();
10873
- import * as path6 from "node:path";
11113
+ import * as path8 from "node:path";
10874
11114
 
10875
11115
  // src/render/stats_renderer.ts
10876
11116
  init_define_import_meta_env();
@@ -11650,7 +11890,7 @@ CREATE INDEX IF NOT EXISTS idx_stats_kind ON stats(kind);
11650
11890
  var _globalSchemaApplied = /* @__PURE__ */ new Set();
11651
11891
  registerReset(() => _globalSchemaApplied.clear());
11652
11892
  function getGlobalDb() {
11653
- const dbPath = path6.join(dataDir(), "global.db");
11893
+ const dbPath = path8.join(dataDir(), "global.db");
11654
11894
  const db = getDb(dbPath);
11655
11895
  if (!_globalSchemaApplied.has(dbPath)) {
11656
11896
  db.exec(GLOBAL_SCHEMA_SQL);
@@ -11684,7 +11924,9 @@ function summarize(windowDays = 30, testDb) {
11684
11924
  const bytesSaved = row.bytes_saved ?? 0;
11685
11925
  const tokensSaved = row.tokens_saved ?? 0;
11686
11926
  const kind = row.kind;
11687
- const ts = row.ts;
11927
+ const tsRaw = row.ts;
11928
+ if (tsRaw === void 0) continue;
11929
+ const ts = tsRaw;
11688
11930
  totalEvents += 1;
11689
11931
  totalBytes += bytesSaved;
11690
11932
  totalTokens += tokensSaved;
@@ -11845,22 +12087,61 @@ function isTsConfigFile(basename7) {
11845
12087
  return /^tsconfig(\..+)?\.json$/i.test(lower) || lower === "jsconfig.json";
11846
12088
  }
11847
12089
  var LARGE_FILE_BYTES = 100 * 1024;
11848
- function isNodeModulesPath(path15) {
11849
- const isWindows = process.platform === "win32";
11850
- const check = isWindows ? path15.toLowerCase() : path15;
12090
+ var REREAD_DENY_BYTES = 50 * 1024;
12091
+ var LARGE_FILE_DENY_BYTES = 500 * 1024;
12092
+ function isNodeModulesPath(p) {
12093
+ const check = isWindows() ? p.toLowerCase() : p;
11851
12094
  return check.includes("/node_modules/") || check.includes("\\node_modules\\");
11852
12095
  }
11853
12096
  function _isDocFile(filePath) {
11854
12097
  const lower = filePath.toLowerCase();
11855
12098
  return lower.endsWith(".md") || lower.endsWith(".mdx") || lower.endsWith(".markdown") || lower.endsWith(".rst");
11856
12099
  }
12100
+ function isSessionArtifactFile(filePath) {
12101
+ if (/[/\\]tasks[/\\][a-z0-9]+\.output$/i.test(filePath)) return true;
12102
+ if (/[/\\]tool-results[/\\][a-z0-9]+\.txt$/i.test(filePath)) return true;
12103
+ return false;
12104
+ }
11857
12105
  function statSize(absPath) {
11858
12106
  try {
11859
- return fs4.statSync(absPath).size;
12107
+ return fs5.statSync(absPath).size;
11860
12108
  } catch {
11861
12109
  return null;
11862
12110
  }
11863
12111
  }
12112
+ function buildLineDiff(oldContent, newContent, label) {
12113
+ const oldLines = oldContent.split("\n");
12114
+ const newLines = newContent.split("\n");
12115
+ let prefix = 0;
12116
+ while (prefix < oldLines.length && prefix < newLines.length && oldLines[prefix] === newLines[prefix]) {
12117
+ prefix++;
12118
+ }
12119
+ let oldSuffix = oldLines.length;
12120
+ let newSuffix = newLines.length;
12121
+ while (oldSuffix > prefix && newSuffix > prefix && oldLines[oldSuffix - 1] === newLines[newSuffix - 1]) {
12122
+ oldSuffix--;
12123
+ newSuffix--;
12124
+ }
12125
+ if (prefix === oldLines.length && prefix === newLines.length) return "";
12126
+ const changedOld = oldLines.slice(prefix, oldSuffix);
12127
+ const changedNew = newLines.slice(prefix, newSuffix);
12128
+ const MAX_LINES = 50;
12129
+ const out2 = [
12130
+ `--- ${label} (prev)`,
12131
+ `+++ ${label} (current)`,
12132
+ `@@ -${prefix + 1},${changedOld.length} +${prefix + 1},${changedNew.length} @@`
12133
+ ];
12134
+ const removedLines = changedOld.map((l) => `-${l}`);
12135
+ const addedLines = changedNew.map((l) => `+${l}`);
12136
+ const allChanges = [...removedLines, ...addedLines];
12137
+ if (allChanges.length <= MAX_LINES) {
12138
+ out2.push(...allChanges);
12139
+ } else {
12140
+ out2.push(...allChanges.slice(0, MAX_LINES));
12141
+ out2.push(`... (${allChanges.length - MAX_LINES} more changed lines)`);
12142
+ }
12143
+ return out2.join("\n");
12144
+ }
11864
12145
  function preReadHandler(event) {
11865
12146
  const filePath = getFilePath(event);
11866
12147
  if (filePath === void 0) return passOutput();
@@ -11870,7 +12151,7 @@ function preReadHandler(event) {
11870
12151
  "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"
11871
12152
  );
11872
12153
  }
11873
- const basename7 = path7.basename(normalized);
12154
+ const basename7 = path9.basename(normalized);
11874
12155
  if (isLockFile(basename7)) {
11875
12156
  return denyOutput(
11876
12157
  '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.'
@@ -11909,7 +12190,7 @@ function preReadHandler(event) {
11909
12190
  try {
11910
12191
  const sz = statSize(normalized);
11911
12192
  if (sz !== null && sz >= MARKDOWN_SIZE_THRESHOLD) {
11912
- fileContent = fs4.readFileSync(normalized, "utf8");
12193
+ fileContent = fs5.readFileSync(normalized, "utf8");
11913
12194
  }
11914
12195
  } catch {
11915
12196
  }
@@ -11925,14 +12206,152 @@ function preReadHandler(event) {
11925
12206
  }
11926
12207
  }
11927
12208
  }
12209
+ const isMemoryMd = normalized.toLowerCase().includes("memory/memory.md") || /[/\\]memory[/\\][^/\\]+\.md$/i.test(normalized);
12210
+ if (isMemoryMd && wasFileReadThisSession(normalized)) {
12211
+ recordFileRead(normalized);
12212
+ recordStat("session_hint", 0, 0);
12213
+ const isMainMemory = basename7.toLowerCase() === "memory.md";
12214
+ return denyOutput(
12215
+ 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.'
12216
+ );
12217
+ }
12218
+ if (/^\.improve-state-.*\.json$/.test(basename7) && wasFileReadThisSession(normalized)) {
12219
+ recordFileRead(normalized);
12220
+ recordStat("session_hint", 0, 0);
12221
+ return denyOutput(
12222
+ "Orchestrator state already read this session. Use `token-goat bash-output <id>` or recall it from the compact manifest."
12223
+ );
12224
+ }
12225
+ if (/^\.env(\.\w+)?$/.test(basename7) && wasFileReadThisSession(normalized)) {
12226
+ recordFileRead(normalized);
12227
+ recordStat("session_hint", 0, 0);
12228
+ return denyOutput(
12229
+ normalized + " was already read this session. Environment files rarely change mid-session. Use `token-goat config-get " + normalized + " KEY_NAME` to extract a specific variable."
12230
+ );
12231
+ }
12232
+ if (isSessionArtifactFile(normalized)) {
12233
+ if (wasFileReadThisSession(normalized)) {
12234
+ if (wasFileTruncatedThisSession(normalized)) {
12235
+ recordFileRead(normalized);
12236
+ recordStat("session_hint", 0, 0);
12237
+ return denyOutput(
12238
+ "File was truncated on last read. Use `token-goat bash-output --tail N` or `--grep PATTERN` to read a slice."
12239
+ );
12240
+ }
12241
+ const artifactSessionId = getSessionId();
12242
+ const oldArtifactSnap = load(artifactSessionId, normalized);
12243
+ if (oldArtifactSnap !== null) {
12244
+ try {
12245
+ const sz = statSize(normalized);
12246
+ if (sz !== null && sz <= 256 * 1024) {
12247
+ const currentContent = fs5.readFileSync(normalized, "utf8");
12248
+ const TRUNC_MARKER = "\n<snapshot truncated at ";
12249
+ const oldRaw = oldArtifactSnap.toString("utf8");
12250
+ const truncIdx = oldRaw.indexOf(TRUNC_MARKER);
12251
+ const oldContent = truncIdx >= 0 ? oldRaw.slice(0, truncIdx) : oldRaw;
12252
+ if (oldContent === currentContent) {
12253
+ recordFileRead(normalized);
12254
+ recordStat("session_hint", 0, 0);
12255
+ return denyOutput(
12256
+ basename7 + " is unchanged since last read. Use `token-goat bash-output --tail N` or `--grep PATTERN` to read a slice."
12257
+ );
12258
+ }
12259
+ const diff = buildLineDiff(oldContent, currentContent, basename7);
12260
+ if (diff !== "") {
12261
+ recordFileRead(normalized);
12262
+ const savedBytes = Math.max(0, currentContent.length - diff.length);
12263
+ recordStat("session_hint", savedBytes, Math.round(savedBytes / 4));
12264
+ return denyOutput(
12265
+ "Content changed since last read of " + basename7 + ". 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."
12266
+ );
12267
+ }
12268
+ }
12269
+ } catch {
12270
+ }
12271
+ }
12272
+ recordFileRead(normalized);
12273
+ recordStat("session_hint", 0, 0);
12274
+ return denyOutput(
12275
+ normalized + " was already read this session. Use `token-goat bash-output --tail N` or `--grep PATTERN` to read a slice."
12276
+ );
12277
+ }
12278
+ if (/[/\\]tasks[/\\][a-z0-9]+\.output$/i.test(normalized)) {
12279
+ recordFileRead(normalized);
12280
+ return contextOutput(
12281
+ "Session transcript: use `token-goat bash-output --tail N` or `--grep PATTERN` to read a slice instead of the full file."
12282
+ );
12283
+ }
12284
+ }
12285
+ if (/\.(md|mdx|rst|txt)$/i.test(basename7) && wasFileReadThisSession(normalized)) {
12286
+ if (wasFileTruncatedThisSession(normalized)) {
12287
+ recordFileRead(normalized);
12288
+ recordStat("session_hint", 0, 0);
12289
+ return denyOutput(
12290
+ 'File was truncated on last read (>33K tokens). Use `token-goat skeleton "' + normalized + '"` for structure or `token-goat read "' + normalized + '::SymbolName"` for one function.'
12291
+ );
12292
+ }
12293
+ const sessionId = getSessionId();
12294
+ const oldSnap = load(sessionId, normalized);
12295
+ if (oldSnap !== null) {
12296
+ try {
12297
+ const sz = statSize(normalized);
12298
+ if (sz !== null && sz <= 256 * 1024) {
12299
+ const currentContent = fs5.readFileSync(normalized, "utf8");
12300
+ const TRUNC_MARKER = "\n<snapshot truncated at ";
12301
+ const oldRaw = oldSnap.toString("utf8");
12302
+ const truncIdx = oldRaw.indexOf(TRUNC_MARKER);
12303
+ const oldContent = truncIdx >= 0 ? oldRaw.slice(0, truncIdx) : oldRaw;
12304
+ if (oldContent === currentContent) {
12305
+ recordFileRead(normalized);
12306
+ recordStat("session_hint", 0, 0);
12307
+ return denyOutput(
12308
+ basename7 + ' is unchanged since last read. Use `token-goat section "' + normalized + '::HeadingName"` to extract a part.'
12309
+ );
12310
+ }
12311
+ const diff = buildLineDiff(oldContent, currentContent, basename7);
12312
+ if (diff !== "") {
12313
+ recordFileRead(normalized);
12314
+ const savedBytes = Math.max(0, currentContent.length - diff.length);
12315
+ recordStat("session_hint", savedBytes, Math.round(savedBytes / 4));
12316
+ return denyOutput(
12317
+ "Content changed since last read of " + basename7 + ". Here is what changed:\n\n```diff\n" + diff + '\n```\n\nUse `token-goat section "' + normalized + '::HeadingName"` to read a specific section.'
12318
+ );
12319
+ }
12320
+ }
12321
+ } catch {
12322
+ }
12323
+ }
12324
+ }
11928
12325
  if (wasFileReadThisSession(normalized)) {
11929
12326
  const entry = getSessionFiles().get(normalized);
11930
12327
  const reads = entry?.readCount ?? 1;
11931
12328
  const plural = reads === 1 ? "read" : "reads";
11932
12329
  recordFileRead(normalized);
11933
- const hint = _isDocFile(normalized) ? 'Use `token-goat section "' + normalized + '::SectionName"` to read one section.' : "Use token-goat read/section/symbol to re-read surgically.";
11934
12330
  const rereadBytes = statSize(normalized) ?? 0;
11935
12331
  recordStat("session_hint", rereadBytes, Math.round(rereadBytes / 4));
12332
+ if (wasFileTruncatedThisSession(normalized)) {
12333
+ return denyOutput(
12334
+ 'File was truncated on last read (>33K tokens). Use `token-goat skeleton "' + normalized + '"` for structure or `token-goat read "' + normalized + '::SymbolName"` for one function.'
12335
+ );
12336
+ }
12337
+ if (/\.(md|mdx)$/i.test(basename7)) {
12338
+ return denyOutput(
12339
+ 'Markdown file already read this session. Use `token-goat section "' + normalized + '::HeadingName"` to read one section.'
12340
+ );
12341
+ }
12342
+ const isSourceExt = /\.(ts|tsx|js|jsx|py|go|rs|java|rb|php|swift|kt|cpp|c|h)$/i.test(basename7);
12343
+ if (isSourceExt && reads >= 2) {
12344
+ recordStat("read_count_deny", rereadBytes, Math.round(rereadBytes / 4));
12345
+ return denyOutput(
12346
+ "Read this file " + reads + ' times already \u2014 use `token-goat read "' + normalized + '::Symbol"`, `token-goat skeleton ' + normalized + "`, or `token-goat outline " + normalized + "` to pull just the part you need."
12347
+ );
12348
+ }
12349
+ const hint = _isDocFile(normalized) ? 'Use `token-goat section "' + normalized + '::SectionName"` to read one section.' : "Use token-goat read/section/symbol to re-read surgically.";
12350
+ if (rereadBytes >= REREAD_DENY_BYTES || reads >= 2) {
12351
+ return denyOutput(
12352
+ normalized + " was already read this session (" + reads + " " + plural + "). " + hint
12353
+ );
12354
+ }
11936
12355
  return contextOutput(
11937
12356
  "Note: " + normalized + " was already read this session (" + reads + " " + plural + "). " + hint
11938
12357
  );
@@ -11943,11 +12362,16 @@ function preReadHandler(event) {
11943
12362
  recordFileRead(normalized);
11944
12363
  const hint = _isDocFile(normalized) ? 'Use `token-goat section "' + normalized + '::SectionName"` to read one section.' : "Consider token-goat skeleton or token-goat section.";
11945
12364
  recordStat("session_hint", size, Math.round(size / 4));
12365
+ if (size >= LARGE_FILE_DENY_BYTES) {
12366
+ return denyOutput(
12367
+ normalized + " is very large (" + kb + "KB). " + hint + " Use Read with offset/limit to sample specific sections."
12368
+ );
12369
+ }
11946
12370
  return contextOutput(
11947
- "Note: " + normalized + " is large (" + kb + "kb). " + hint
12371
+ "Note: " + normalized + " is large (" + kb + "KB). " + hint
11948
12372
  );
11949
12373
  }
11950
- const fileTypeExt = path7.extname(normalized).slice(1).toLowerCase();
12374
+ const fileTypeExt = path9.extname(normalized).slice(1).toLowerCase();
11951
12375
  const binaryExts = /* @__PURE__ */ new Set(["pdf", "docx", "xlsx", "pptx", "odt", "ods", "ott", "odp"]);
11952
12376
  const textTypeExts = /* @__PURE__ */ new Set(["html", "htm", "xhtml", "txt", "log", "out", "err", "trace", "csv", "tsv"]);
11953
12377
  const fileStatSize = size ?? statSize(normalized) ?? 0;
@@ -11956,7 +12380,7 @@ function preReadHandler(event) {
11956
12380
  let ftContent = "";
11957
12381
  if (!binaryExts.has(fileTypeExt)) {
11958
12382
  try {
11959
- ftContent = fs4.readFileSync(normalized, "utf8");
12383
+ ftContent = fs5.readFileSync(normalized, "utf8");
11960
12384
  } catch {
11961
12385
  }
11962
12386
  }
@@ -11971,29 +12395,67 @@ function preReadHandler(event) {
11971
12395
  }
11972
12396
  registerHook("pre_tool_use", preReadHandler, { toolName: "Read" });
11973
12397
  registerHook("pre_tool_use", preReadHandler, { toolName: "Grep" });
12398
+ function extractReadOutput(raw) {
12399
+ const resp = raw["tool_response"];
12400
+ if (typeof resp === "string") return resp;
12401
+ if (resp !== null && typeof resp === "object") {
12402
+ const r = resp;
12403
+ for (const key of ["output", "content", "text", "body"]) {
12404
+ if (typeof r[key] === "string") return r[key];
12405
+ }
12406
+ }
12407
+ return "";
12408
+ }
12409
+ function postReadHandler(event) {
12410
+ const filePath = getFilePath(event);
12411
+ if (filePath === void 0) return passOutput();
12412
+ const normalized = normalizePath(filePath);
12413
+ const respText = extractReadOutput(event.raw);
12414
+ if (respText.includes("[Truncated:") || respText.includes("Truncated: PARTIAL view")) {
12415
+ markFileTruncated(normalized);
12416
+ }
12417
+ const postBasename = path9.basename(normalized);
12418
+ if (/\.(md|mdx|rst|txt)$/i.test(postBasename) || isSessionArtifactFile(normalized)) {
12419
+ try {
12420
+ const sz = statSize(normalized);
12421
+ if (sz !== null && sz <= 256 * 1024) {
12422
+ const content = fs5.readFileSync(normalized);
12423
+ store(getSessionId(), normalized, content);
12424
+ }
12425
+ } catch {
12426
+ }
12427
+ }
12428
+ return passOutput();
12429
+ }
12430
+ registerHook("post_tool_use", postReadHandler, { toolName: "Read" });
11974
12431
 
11975
12432
  // src/hooks_edit.ts
11976
12433
  init_define_import_meta_env();
11977
- import * as path9 from "node:path";
12434
+ import * as path11 from "node:path";
11978
12435
 
11979
12436
  // src/hooks_index.ts
11980
12437
  init_define_import_meta_env();
11981
- import * as fs5 from "node:fs";
11982
- import * as path8 from "node:path";
12438
+ import * as fs6 from "node:fs";
12439
+ import * as path10 from "node:path";
11983
12440
  function dirtyQueuePath() {
11984
- return path8.join(dataDir(), "queue", "dirty.txt");
12441
+ return path10.join(dataDir(), "queue", "dirty.txt");
11985
12442
  }
11986
12443
  function appendDirtyPath(normalizedPath) {
11987
12444
  const queuePath = dirtyQueuePath();
11988
- fs5.mkdirSync(path8.dirname(queuePath), { recursive: true });
11989
- fs5.appendFileSync(queuePath, `${normalizedPath}
12445
+ const dir = path10.dirname(queuePath);
12446
+ try {
12447
+ fs6.mkdirSync(dir, { recursive: true });
12448
+ } catch (e) {
12449
+ if (e.code !== "EEXIST" || !fs6.existsSync(dir)) throw e;
12450
+ }
12451
+ fs6.appendFileSync(queuePath, `${normalizedPath}
11990
12452
  `);
11991
12453
  }
11992
12454
  function getDirtyPaths() {
11993
12455
  const queuePath = dirtyQueuePath();
11994
12456
  let raw;
11995
12457
  try {
11996
- raw = fs5.readFileSync(queuePath, "utf8");
12458
+ raw = fs6.readFileSync(queuePath, "utf8");
11997
12459
  } catch {
11998
12460
  return [];
11999
12461
  }
@@ -12009,21 +12471,21 @@ function getDirtyPaths() {
12009
12471
  }
12010
12472
  function clearDirtyQueue() {
12011
12473
  try {
12012
- fs5.rmSync(dirtyQueuePath(), { force: true });
12474
+ fs6.rmSync(dirtyQueuePath(), { force: true });
12013
12475
  } catch {
12014
12476
  }
12015
12477
  }
12016
12478
  function preCompactIndexHandler(_event) {
12017
12479
  const paths = getDirtyPaths();
12018
12480
  if (paths.length > 0) {
12019
- const sidecar = path8.join(dataDir(), "queue", "pending.txt");
12481
+ const sidecar = path10.join(dataDir(), "queue", "pending.txt");
12020
12482
  try {
12021
- fs5.mkdirSync(path8.dirname(sidecar), { recursive: true });
12483
+ fs6.mkdirSync(path10.dirname(sidecar), { recursive: true });
12022
12484
  atomicWriteBytes(sidecar, Buffer.from(`${paths.join("\n")}
12023
12485
  `, "utf8"));
12486
+ clearDirtyQueue();
12024
12487
  } catch {
12025
12488
  }
12026
- clearDirtyQueue();
12027
12489
  }
12028
12490
  return passOutput();
12029
12491
  }
@@ -12036,10 +12498,11 @@ function postEditHandler(event) {
12036
12498
  const normalized = normalizePath(filePath);
12037
12499
  recordFileEdit(normalized);
12038
12500
  appendDirtyPath(normalized);
12039
- const editedBasename = path9.basename(normalized);
12501
+ const editedBasename = path11.basename(normalized);
12040
12502
  if (/\.(md|mdx|markdown|rst)$/i.test(editedBasename)) {
12503
+ const escapedPath = normalized.replace(/`/g, "\\`");
12041
12504
  return contextOutput(
12042
- editedBasename + ' was edited. Use `token-goat section "' + normalized + '::HeadingName"` to re-read a specific section rather than the full file.'
12505
+ editedBasename + ' was edited. Use `token-goat section "' + escapedPath + '::HeadingName"` to re-read a specific section rather than the full file.'
12043
12506
  );
12044
12507
  }
12045
12508
  return passOutput();
@@ -12317,17 +12780,20 @@ init_define_import_meta_env();
12317
12780
 
12318
12781
  // src/fingerprint.ts
12319
12782
  init_define_import_meta_env();
12320
- import { createHash } from "node:crypto";
12321
- import * as fs6 from "node:fs";
12783
+ import { createHash as createHash2 } from "node:crypto";
12784
+ import * as fs7 from "node:fs";
12322
12785
  function fingerprintContent(content) {
12323
- const hash = createHash("sha256");
12786
+ const hash = createHash2("sha256");
12324
12787
  hash.update(typeof content === "string" ? Buffer.from(content, "utf-8") : content);
12325
12788
  return hash.digest("hex");
12326
12789
  }
12790
+ function shortFingerprint(content) {
12791
+ return fingerprintContent(content).slice(0, 16);
12792
+ }
12327
12793
  function fingerprintFile(filePath) {
12328
12794
  let data;
12329
12795
  try {
12330
- data = fs6.readFileSync(filePath);
12796
+ data = fs7.readFileSync(filePath);
12331
12797
  } catch {
12332
12798
  return null;
12333
12799
  }
@@ -12336,15 +12802,24 @@ function fingerprintFile(filePath) {
12336
12802
 
12337
12803
  // src/bash_output_cache.ts
12338
12804
  init_define_import_meta_env();
12339
- import * as fs7 from "fs/promises";
12805
+ import * as fs8 from "fs/promises";
12340
12806
  import { resolve as resolve3 } from "path";
12341
12807
  var _byId = /* @__PURE__ */ new Map();
12342
12808
  var _globsByKey = /* @__PURE__ */ new Map();
12343
12809
  var _grepsByKey = /* @__PURE__ */ new Map();
12344
- var GIT_MUTABLE_RE = /^\s*git\s+(diff|status)\b/i;
12345
- var LS_CMD_RE = /^\s*(?:ls|eza|exa|dir|Get-ChildItem|gci)\b/i;
12346
- var DEP_LIST_RE = /^\s*(?:npm\s+(?:-\S+\s+)*(?:ls|list)\b|pip\s+(?:-\S+\s+)*(?:list|freeze)\b|uv\s+pip\s+(?:-\S+\s+)*(?:list|freeze)\b|pnpm\s+(?:-\S+\s+)*(?:list|ls)\b|yarn\s+(?:-\S+\s+)*(?:list)\b|cargo\s+(?:-\S+\s+)*tree\b|bundle\s+(?:-\S+\s+)*(?:list|show)\b|composer\s+(?:-\S+\s+)*show\b)/i;
12347
- var NPM_INSTALL_RE = /^\s*npm\s+(?:-\S+\s+)*(?:install|ci)\b/i;
12810
+ var COMMAND_PATTERNS = {
12811
+ gitMutable: /^\s*git\s+(diff|status)\b/i,
12812
+ gitImmutable: /^\s*git\s+show\s+[0-9a-f]{40}\b/i,
12813
+ gitDiffUnscoped: /^\s*git\s+diff\b/i,
12814
+ gitDiffScoped: /\s--\s+\S/,
12815
+ dirListing: /^\s*(?:ls|eza|exa|dir|Get-ChildItem|gci)\b/i,
12816
+ depList: /^\s*(?:npm\s+(?:-\S+\s+)*(?:ls|list)\b|pip\s+(?:-\S+\s+)*(?:list|freeze)\b|uv\s+pip\s+(?:-\S+\s+)*(?:list|freeze)\b|pnpm\s+(?:-\S+\s+)*(?:list|ls)\b|yarn\s+(?:-\S+\s+)*(?:list)\b|cargo\s+(?:-\S+\s+)*tree\b|bundle\s+(?:-\S+\s+)*(?:list|show)\b|composer\s+(?:-\S+\s+)*show\b)/i,
12817
+ npmInstall: /^\s*npm\s+(?:-\S+\s+)*(?:install|ci)\b/i,
12818
+ npmAudit: /^\s*npm\s+(?:-\S+\s+)*audit\b(?!.*(?:--fix|fix)\b)/i,
12819
+ npmOutdated: /^\s*npm\s+(?:-\S+\s+)*outdated\b/i,
12820
+ envProbe: /^\s*(?:node\s+(?:-v|--version)|npm\s+(?:-v|--version)|python3?\s+(?:(?:-V)\b|--?version)|git\s+--version|uv\s+--version|go\s+version|rustc\s+--version|cargo\s+--version|java\s+--version|ruby\s+--version|gem\s+--version|php\s+--version|which\b|where\b)/i,
12821
+ npx: /^\s*npx\s+(?:--?yes\s+)?(?!.*\b(?:install|add|remove|uninstall|i|rm|update|upgrade|set|get|publish|link|ci|audit|shrinkwrap|dedupe|prune|rebuild)\b)/i
12822
+ };
12348
12823
  var DEP_LOCKFILES = {
12349
12824
  npm: ["package-lock.json", "yarn.lock"],
12350
12825
  pip: ["requirements.txt"],
@@ -12355,18 +12830,14 @@ var DEP_LOCKFILES = {
12355
12830
  bundle: ["Gemfile.lock"],
12356
12831
  composer: ["composer.lock"]
12357
12832
  };
12358
- function isGitMutableCommand(cmd) {
12359
- return GIT_MUTABLE_RE.test(cmd);
12360
- }
12361
- function isDirListingCommand(cmd) {
12362
- return LS_CMD_RE.test(cmd);
12363
- }
12364
- function isDepListCommand(cmd) {
12365
- return DEP_LIST_RE.test(cmd);
12366
- }
12367
- function isNpmInstallCommand(cmd) {
12368
- return NPM_INSTALL_RE.test(cmd);
12369
- }
12833
+ function isCommandOfType(cmd, type) {
12834
+ const pattern = COMMAND_PATTERNS[type];
12835
+ return pattern?.test(cmd) ?? false;
12836
+ }
12837
+ var isGitMutableCommand = (cmd) => isCommandOfType(cmd, "gitMutable");
12838
+ var isDirListingCommand = (cmd) => isCommandOfType(cmd, "dirListing");
12839
+ var isDepListCommand = (cmd) => isCommandOfType(cmd, "depList");
12840
+ var isNpmInstallCommand = (cmd) => isCommandOfType(cmd, "npmInstall");
12370
12841
  async function gitStateFingerprint(cwd) {
12371
12842
  try {
12372
12843
  const headResult = runGit(["rev-parse", "HEAD"], { cwd });
@@ -12374,21 +12845,21 @@ async function gitStateFingerprint(cwd) {
12374
12845
  const headSha = headResult.stdout.trim();
12375
12846
  let indexMtime = "";
12376
12847
  try {
12377
- const stat3 = await fs7.stat(resolve3(cwd, ".git", "index"));
12848
+ const stat3 = await fs8.stat(resolve3(cwd, ".git", "index"));
12378
12849
  indexMtime = stat3.mtimeMs.toString();
12379
12850
  } catch {
12380
12851
  }
12381
12852
  const key = `${headSha}\0${indexMtime}`;
12382
- return fingerprintContent(key).slice(0, 16);
12853
+ return shortFingerprint(key);
12383
12854
  } catch {
12384
12855
  return null;
12385
12856
  }
12386
12857
  }
12387
- async function dirStateFingerprint(path15) {
12858
+ async function dirStateFingerprint(path17) {
12388
12859
  try {
12389
- const stat3 = await fs7.stat(path15);
12860
+ const stat3 = await fs8.stat(path17);
12390
12861
  if (!stat3.isDirectory()) return null;
12391
- return fingerprintContent(stat3.mtimeMs.toString()).slice(0, 16);
12862
+ return shortFingerprint(stat3.mtimeMs.toString());
12392
12863
  } catch {
12393
12864
  return null;
12394
12865
  }
@@ -12402,8 +12873,8 @@ async function depLockfileFingerprint(cmd, cwd) {
12402
12873
  if (!candidates) return null;
12403
12874
  for (const lockfile of candidates) {
12404
12875
  try {
12405
- const content = await fs7.readFile(resolve3(cwd, lockfile));
12406
- return fingerprintContent(content).slice(0, 16);
12876
+ const content = await fs8.readFile(resolve3(cwd, lockfile));
12877
+ return shortFingerprint(content);
12407
12878
  } catch {
12408
12879
  continue;
12409
12880
  }
@@ -12451,7 +12922,7 @@ async function commandHash(command, cwd = null) {
12451
12922
  const fp = await depLockfileFingerprint(command, cwd);
12452
12923
  if (fp) key = `${key}\0npm-install:${fp}`;
12453
12924
  }
12454
- return fingerprintContent(key).slice(0, 16);
12925
+ return shortFingerprint(key);
12455
12926
  }
12456
12927
  function extractLsTarget(cmd, cwd) {
12457
12928
  const tokens = cmd.trim().split(/\s+/);
@@ -12489,10 +12960,276 @@ function extractCommand(event) {
12489
12960
  const cmd = event.toolInput["command"];
12490
12961
  return typeof cmd === "string" && cmd.trim() !== "" ? cmd.trim() : void 0;
12491
12962
  }
12963
+ function isTempPath(fp) {
12964
+ const norm = fp.replace(/\\/g, "/");
12965
+ 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/");
12966
+ }
12967
+ function isOrchestratorStateFile(filePath) {
12968
+ const basename7 = (filePath.includes("/") ? filePath.split("/").at(-1) : filePath.split("\\").at(-1)) ?? filePath;
12969
+ return /^\.improve-state-/.test(basename7);
12970
+ }
12492
12971
  function extractCatSourceFile(cmd) {
12493
- 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))\s*$/.exec(cmd);
12972
+ 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);
12494
12973
  return m?.[1] ?? null;
12495
12974
  }
12975
+ function extractCatFile(cmd) {
12976
+ const m = /^cat(?:\s+(?:-[a-zA-Z]+|--[a-zA-Z-]+))*\s+(?:"([^"]+)"|'([^']+)'|(\S+))\s*$/.exec(cmd);
12977
+ if (!m) return null;
12978
+ const filePath = m[1] ?? m[2] ?? m[3];
12979
+ if (filePath === void 0) return null;
12980
+ if (isTempPath(filePath)) return null;
12981
+ const basename7 = (filePath.includes("/") ? filePath.split("/").at(-1) : filePath.split("\\").at(-1)) ?? filePath;
12982
+ const isEnvFile = /^\.env(\.\w+)?$/i.test(basename7);
12983
+ 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);
12984
+ if (!hasKnownExt && !isEnvFile) return null;
12985
+ const isSql = /\.sql$/i.test(filePath);
12986
+ const isDoc = /\.(?:md|mdx|rst|txt)$/i.test(filePath);
12987
+ const isEnv = isEnvFile || /\.env$/i.test(filePath);
12988
+ const isConfig = /\.(?:json|yaml|yml|toml|conf|cfg|ini|properties)$/i.test(filePath);
12989
+ return { filePath, isDoc, isEnv, isConfig, isSql };
12990
+ }
12991
+ function extractRgSymbolSearch(cmd) {
12992
+ if (!/^(?:rg|grep)\s+/.test(cmd)) return null;
12993
+ if (!/-n\b/.test(cmd)) return null;
12994
+ const patternMatch = /["']([^"']+)["']/.exec(cmd);
12995
+ const pattern = patternMatch?.[1];
12996
+ if (!pattern) return null;
12997
+ if (!/^[A-Za-z_][A-Za-z0-9_]*(\|[A-Za-z_][A-Za-z0-9_]*)*$/.test(pattern)) return null;
12998
+ const fileMatch = /(?:^|\s)(?:"([^"]+\.(?:ts|tsx|js|jsx|py|go|rs|java|rb|php|swift|kt|cpp|cc|cxx|c|h))"|'([^']+\.(?:ts|tsx|js|jsx|py|go|rs|java|rb|php|swift|kt|cpp|cc|cxx|c|h))'|([^\s"'|<>]+\.(?:ts|tsx|js|jsx|py|go|rs|java|rb|php|swift|kt|cpp|cc|cxx|c|h)))(?:\s|$|\|)/i.exec(cmd);
12999
+ if (!fileMatch) return null;
13000
+ const filePath = fileMatch[1] ?? fileMatch[2] ?? fileMatch[3];
13001
+ if (!filePath) return null;
13002
+ if (isTempPath(filePath)) return null;
13003
+ if (/-[a-zA-Z]*r[a-zA-Z]*\b/.test(cmd) || /--recursive\b/.test(cmd)) return null;
13004
+ return { filePath, identifier: pattern };
13005
+ }
13006
+ function extractCatJsonPipe(cmd) {
13007
+ const m = /^cat\s+(?:"([^"]+)"|'([^']+)'|(\S+))\s*\|\s*jq\b/.exec(cmd);
13008
+ if (!m) return null;
13009
+ const filePath = m[1] ?? m[2] ?? m[3];
13010
+ if (!filePath) return null;
13011
+ if (isTempPath(filePath)) return null;
13012
+ if (!/\.(?:json|yaml|yml|toml)$/i.test(filePath)) return null;
13013
+ return { filePath };
13014
+ }
13015
+ function extractWslCatFile(cmd) {
13016
+ const wslMatch = /^wsl(?:\s+-d\s+\S+)?\s+bash\s+-c\s+"cat(?:\s+(?:-[a-zA-Z]+|--[a-zA-Z-]+))*\s+\/mnt\/([a-z])\/([^"]*)"/.exec(cmd);
13017
+ if (!wslMatch) return null;
13018
+ const drive = wslMatch[1]?.toUpperCase();
13019
+ const pathRest = wslMatch[2];
13020
+ if (!drive || !pathRest) return null;
13021
+ const filePath = drive + ":/" + pathRest;
13022
+ if (isTempPath(filePath)) return null;
13023
+ const basename7 = (filePath.includes("/") ? filePath.split("/").at(-1) : filePath.split("\\").at(-1)) ?? filePath;
13024
+ const isEnvFile = /^\.env(\.\w+)?$/i.test(basename7);
13025
+ 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);
13026
+ if (!hasKnownExt && !isEnvFile) return null;
13027
+ const isSql = /\.sql$/i.test(filePath);
13028
+ const isDoc = /\.(?:md|mdx|rst|txt)$/i.test(filePath);
13029
+ const isEnv = isEnvFile || /\.env$/i.test(filePath);
13030
+ const isConfig = /\.(?:json|yaml|yml|toml|conf|cfg|ini|properties)$/i.test(filePath);
13031
+ return { filePath, isDoc, isEnv, isConfig, isSql };
13032
+ }
13033
+ function extractPythonFileRead(cmd) {
13034
+ if (!/python3?/.test(cmd)) return null;
13035
+ if (/open\s*\([^)]*,\s*['"][wa]/i.test(cmd) || /\.write\s*\(/.test(cmd)) return null;
13036
+ const OPEN_EXT = /\.(?:java|py|ts|tsx|js|jsx|go|rb|rs|cpp|cc|cxx|c|h|hpp|kt|swift|cs|php|scala|clj|md|mdx|rst|txt|json|yaml|yml|toml|xml|conf|cfg|ini|properties)/i;
13037
+ const heredocMatch = /^python3?\s+-\s+<<\s*'?(\w+)'?\s*\n([\s\S]*?)\n\1\s*$/.exec(cmd);
13038
+ if (heredocMatch) {
13039
+ const body = heredocMatch[2] ?? "";
13040
+ if (/open\s*\([^)]*,\s*['"][wa]/i.test(body) || /\.write\s*\(/.test(body) || /\.writelines\s*\(/.test(body)) return null;
13041
+ const heredocOpen = /open\s*\(\s*r?['"]([^'"]+\.(?:java|py|ts|tsx|js|jsx|go|rb|rs|cpp|cc|cxx|c|h|hpp|kt|swift|cs|php|scala|clj|md|mdx|rst|txt|json|yaml|yml|toml|xml|conf|cfg|ini|properties))['"]/i.exec(body);
13042
+ if (heredocOpen?.[1]) {
13043
+ const filePath = heredocOpen[1];
13044
+ if (isOrchestratorStateFile(filePath)) return null;
13045
+ const isDoc = /\.(?:md|mdx|rst|txt)$/i.test(filePath);
13046
+ return { filePath, isDoc };
13047
+ }
13048
+ if (/open\s*\(/.test(body)) {
13049
+ const literal = /['"]([^'"]+\.(?:java|py|ts|tsx|js|jsx|go|rb|rs|cpp|cc|cxx|c|h|hpp|kt|swift|cs|php|scala|clj|md|mdx|rst|txt|json|yaml|yml|toml|xml|conf|cfg|ini|properties))['"]/i.exec(body);
13050
+ if (literal?.[1]) {
13051
+ const filePath = literal[1];
13052
+ if (isOrchestratorStateFile(filePath)) return null;
13053
+ if (OPEN_EXT.test(filePath)) {
13054
+ const isDoc = /\.(?:md|mdx|rst|txt)$/i.test(filePath);
13055
+ return { filePath, isDoc };
13056
+ }
13057
+ }
13058
+ }
13059
+ return null;
13060
+ }
13061
+ const direct = /open\(['"]([^'"]+\.(?:java|py|ts|tsx|js|jsx|go|rb|rs|cpp|cc|cxx|c|h|hpp|kt|swift|cs|php|scala|clj|md|mdx|rst|txt|json|yaml|yml|toml|xml|conf|cfg|ini|properties))['"]/i.exec(cmd);
13062
+ if (direct) {
13063
+ const filePath = direct[1] ?? "";
13064
+ if (!filePath) return null;
13065
+ if (isOrchestratorStateFile(filePath)) return null;
13066
+ const isDoc = /\.(?:md|mdx|rst|txt)$/i.test(filePath);
13067
+ return { filePath, isDoc };
13068
+ }
13069
+ if (/open\s*\(/.test(cmd)) {
13070
+ const literal = /['"]([^'"]+\.(?:java|py|ts|tsx|js|jsx|go|rb|rs|cpp|cc|cxx|c|h|hpp|kt|swift|cs|php|scala|clj|md|mdx|rst|txt|json|yaml|yml|toml|xml|conf|cfg|ini|properties))['"]/i.exec(cmd);
13071
+ if (literal) {
13072
+ const filePath = literal[1] ?? "";
13073
+ if (filePath) {
13074
+ if (isOrchestratorStateFile(filePath)) return null;
13075
+ if (OPEN_EXT.test(filePath)) {
13076
+ const isDoc = /\.(?:md|mdx|rst|txt)$/i.test(filePath);
13077
+ return { filePath, isDoc };
13078
+ }
13079
+ }
13080
+ }
13081
+ }
13082
+ return null;
13083
+ }
13084
+ function extractHeadFile(cmd) {
13085
+ const m = /^head(?:\s+-n\s+(\d+)|\s+-(\d+))?\s+(?:"([^"]+)"|'([^']+)'|(\S+))\s*$/.exec(cmd);
13086
+ if (!m) return null;
13087
+ const n = parseInt(m[1] ?? m[2] ?? "0", 10);
13088
+ if (n < 10) return null;
13089
+ const filePath = m[3] ?? m[4] ?? m[5];
13090
+ if (filePath === void 0) return null;
13091
+ if (isTempPath(filePath)) return null;
13092
+ if (!/\.(?:ts|tsx|js|jsx|py|go|java|rs|rb|cs|md|mdx|rst|txt|json|yaml|yml|toml|sql|sh)$/i.test(filePath)) return null;
13093
+ const isDoc = /\.(?:md|mdx|rst|txt|sql)$/i.test(filePath);
13094
+ const isConfig = /\.(?:json|yaml|yml|toml|conf|cfg|ini|properties)$/i.test(filePath);
13095
+ return { filePath, isDoc, isConfig };
13096
+ }
13097
+ function extractNodeFileRead(cmd) {
13098
+ if (!/^node\s+-e/.test(cmd)) return null;
13099
+ const readSync = /readFileSync\(['"]([^'"]+\.(?:ts|tsx|js|jsx|py|go|java|rs|rb|cs|md|mdx|rst|txt|json|yaml|yml|toml|xml|conf|cfg|ini|properties|sql))['"]/i.exec(cmd);
13100
+ if (readSync?.[1]) {
13101
+ const filePath = readSync[1];
13102
+ if (isOrchestratorStateFile(filePath)) return null;
13103
+ if (isTempPath(filePath)) return null;
13104
+ const isDoc = /\.(?:md|mdx|rst|txt|sql)$/i.test(filePath);
13105
+ const isConfig = /\.(?:json|yaml|yml|toml|conf|cfg|ini|properties)$/i.test(filePath);
13106
+ return { filePath, isDoc, isConfig };
13107
+ }
13108
+ const requireM = /require\(['"]([^'"]+\.json)['"]\)/i.exec(cmd);
13109
+ if (requireM?.[1]) {
13110
+ const filePath = requireM[1];
13111
+ if (filePath.includes("node_modules")) return null;
13112
+ if (isOrchestratorStateFile(filePath)) return null;
13113
+ if (isTempPath(filePath)) return null;
13114
+ return { filePath, isDoc: false, isConfig: true };
13115
+ }
13116
+ return null;
13117
+ }
13118
+ function extractTailFile(cmd) {
13119
+ if (/-f\b/.test(cmd)) return null;
13120
+ if (/-c\b/.test(cmd)) return null;
13121
+ if (/-n\s*\+/.test(cmd)) return null;
13122
+ const m = /^tail(?:\s+-n\s+(\d+)|\s+-(\d+))?\s+(?:"([^"]+)"|'([^']+)'|(\S+))\s*$/.exec(cmd);
13123
+ if (!m) return null;
13124
+ const n = parseInt(m[1] ?? m[2] ?? "0", 10);
13125
+ if (n <= 10) return null;
13126
+ const filePath = m[3] ?? m[4] ?? m[5];
13127
+ if (!filePath) return null;
13128
+ if (isTempPath(filePath)) return null;
13129
+ if (!/\.(?:ts|tsx|js|jsx|py|go|java|rs|rb|cs|md|mdx|rst|txt|json|yaml|yml|toml|sql|sh)$/i.test(filePath)) return null;
13130
+ const isDoc = /\.(?:md|mdx|rst|txt|sql)$/i.test(filePath);
13131
+ return { filePath, isDoc };
13132
+ }
13133
+ function extractTasksOutput(cmd) {
13134
+ const taskOutputRe = /[/\\]tasks[/\\]([a-z0-9]+)\.output$/;
13135
+ const catM = /^cat(?:\s+(?:-[a-zA-Z]+|--[a-zA-Z-]+))*\s+(?:"([^"]+)"|'([^']+)'|(\S+))\s*$/.exec(cmd);
13136
+ if (catM) {
13137
+ const fp = catM[1] ?? catM[2] ?? catM[3];
13138
+ if (fp) {
13139
+ const m = taskOutputRe.exec(fp);
13140
+ if (m) return { id: m[1] };
13141
+ }
13142
+ }
13143
+ if (!/-f\b/.test(cmd) && !/-n\s*\+/.test(cmd)) {
13144
+ const tailM = /^tail(?:\s+-n\s+(\d+)|\s+-(\d+))?\s+(?:"([^"]+)"|'([^']+)'|(\S+))\s*$/.exec(cmd);
13145
+ if (tailM) {
13146
+ const fp = tailM[3] ?? tailM[4] ?? tailM[5];
13147
+ if (fp) {
13148
+ const m = taskOutputRe.exec(fp);
13149
+ if (m) {
13150
+ const nStr = tailM[1] ?? tailM[2];
13151
+ const n = nStr !== void 0 ? parseInt(nStr, 10) : void 0;
13152
+ return n !== void 0 ? { id: m[1], n } : { id: m[1] };
13153
+ }
13154
+ }
13155
+ }
13156
+ const byteTailM = /^tail\s+-c\s+\d+\s+(?:"([^"]+)"|'([^']+)'|(\S+))\s*$/.exec(cmd);
13157
+ if (byteTailM) {
13158
+ const fp = byteTailM[1] ?? byteTailM[2] ?? byteTailM[3];
13159
+ if (fp) {
13160
+ const m = taskOutputRe.exec(fp);
13161
+ if (m) return { id: m[1] };
13162
+ }
13163
+ }
13164
+ }
13165
+ return null;
13166
+ }
13167
+ function extractSedLineRange(cmd) {
13168
+ return /^sed\s+-n\s+['"]?\d+,\d+p['"]?/.test(cmd);
13169
+ }
13170
+ function extractDirectoryListing(cmd) {
13171
+ return /^eza\s+.*--long\s+\S+/.test(cmd) || /^eza\s+.*--tree/.test(cmd) || /^tree(\s|$)/.test(cmd) || /^ls\s+.*-[a-zA-Z]*R/.test(cmd) || /^ls\s+(?:-[la]+\s+)?(\S+)\s*[|]\s*head/.test(cmd);
13172
+ }
13173
+ function extractFindCommand(cmd) {
13174
+ if (!/^find\b/.test(cmd)) return null;
13175
+ const isXargsGrepL = /[|]\s*xargs\s+(?:grep|rg)\s+.*-l\b/.test(cmd) || /[|]\s*xargs\s+grep\s+-l\b/.test(cmd);
13176
+ const nameMatch = /-name\s+['"]([^'"]+)['"]/i.exec(cmd);
13177
+ const extGlob = nameMatch ? nameMatch[1] ?? null : null;
13178
+ return { extGlob, isXargsGrepL };
13179
+ }
13180
+ function extractMarkdownHeadingGrep(cmd) {
13181
+ if (!/^(?:rg|grep)\s+/.test(cmd)) return null;
13182
+ if (!/-n\b/.test(cmd)) return null;
13183
+ const hasHeadingPattern = /["']?\^#{1,6}["']?/.test(cmd) || /["']?\^#\+["']?/.test(cmd) || /["']?\^#\\+["']?/.test(cmd);
13184
+ if (!hasHeadingPattern) return null;
13185
+ const fileMatch = /(?:^|\s)(?:"([^"]+\.(?:md|markdown))"|'([^']+\.(?:md|markdown))'|([^\s"'|<>]+\.(?:md|markdown)))\s*(?:\||$)/.exec(cmd);
13186
+ if (!fileMatch) return null;
13187
+ const filePath = fileMatch[1] ?? fileMatch[2] ?? fileMatch[3];
13188
+ if (!filePath) return null;
13189
+ return { filePath };
13190
+ }
13191
+ function extractRgStructuralSearch(cmd) {
13192
+ if (!/^(?:rg|grep)\s+/.test(cmd)) return null;
13193
+ const hasStructural = /["']?\^?(?:def\s|class\s|function\s|func\s|fn\s|pub fn\s|import\s|from\s)/.test(cmd) || /["']\^(?:def|class|function|func|import|from)["']/.test(cmd) || /\\bdef\\b|\\bclass\\b/.test(cmd) || /["']?\^[ \t]+def\b/.test(cmd);
13194
+ if (!hasStructural) return null;
13195
+ const fileMatch = /(?:^|\s)(?:"([^"]+\.(?:py|ts|tsx|js|jsx|go|rs|rb|cs|java|cpp|cc|cxx|c|h|sh|bash))"|('([^']+\.(?:py|ts|tsx|js|jsx|go|rs|rb|cs|java|cpp|cc|cxx|c|h|sh|bash))')|([^\s"']+\.(?:py|ts|tsx|js|jsx|go|rs|rb|cs|java|cpp|cc|cxx|c|h|sh|bash)))\s*$/.exec(cmd);
13196
+ if (!fileMatch) return null;
13197
+ const filePath = fileMatch[1] ?? fileMatch[3] ?? fileMatch[4];
13198
+ if (!filePath) return null;
13199
+ if (isTempPath(filePath)) return null;
13200
+ return { filePath };
13201
+ }
13202
+ function extractGrepPipeChain(cmd) {
13203
+ return /^(?:rg|grep)\b.*\|\s*(?:rg|grep)\b/.test(cmd);
13204
+ }
13205
+ function extractCurlUrl(cmd) {
13206
+ const m = /(https?:\/\/[^\s'"]+)/.exec(cmd);
13207
+ return m?.[1] ?? null;
13208
+ }
13209
+ function isCurlGetCommand(cmd) {
13210
+ if (!/^curl\b/.test(cmd)) return false;
13211
+ if (/-X\s+(?:POST|PUT|PATCH|DELETE|HEAD|OPTIONS)/i.test(cmd)) return false;
13212
+ if (/--request\s+(?:POST|PUT|PATCH|DELETE|HEAD|OPTIONS)/i.test(cmd)) return false;
13213
+ if (/\s(?:-d|--data(?:-raw|-binary|-urlencode)?|-F|--form)\b/.test(cmd)) return false;
13214
+ if (/\s(?:-u|--user)\b/.test(cmd)) return false;
13215
+ if (/-H\s+['"]?Authorization/i.test(cmd)) return false;
13216
+ return true;
13217
+ }
13218
+ function extractCurlDownload(cmd) {
13219
+ if (!/^curl\b/.test(cmd)) return null;
13220
+ const outputMatch = /(?:^|\s)(?:-o|--output)\s+(?:"([^"]+)"|'([^']+)'|(\S+))/.exec(cmd);
13221
+ if (!outputMatch) return null;
13222
+ const outputPath = outputMatch[1] ?? outputMatch[2] ?? outputMatch[3];
13223
+ if (!outputPath) return null;
13224
+ if (/-X\s+(?:POST|PUT|PATCH|DELETE|HEAD|OPTIONS)/i.test(cmd)) return null;
13225
+ if (/--request\s+(?:POST|PUT|PATCH|DELETE|HEAD|OPTIONS)/i.test(cmd)) return null;
13226
+ if (/(?:^|\s)(?:-d|--data(?:-raw|-binary|-urlencode)?|-F|--form)\b/.test(cmd)) return null;
13227
+ if (/(?:^|\s)(?:-u|--user)\b/.test(cmd)) return null;
13228
+ if (/-H\s+['"]?Authorization/i.test(cmd)) return null;
13229
+ const urlMatch = /(https?:\/\/[^\s'"]+)/.exec(cmd);
13230
+ if (!urlMatch?.[1]) return null;
13231
+ return { url: urlMatch[1], outputPath };
13232
+ }
12496
13233
  function isTscCommand(cmd) {
12497
13234
  return /^\s*tsc(\s|$)/i.test(cmd);
12498
13235
  }
@@ -12512,9 +13249,133 @@ function buildRecallHint(cmd, outputId) {
12512
13249
  function preBashHandler(event) {
12513
13250
  const cmd = extractCommand(event);
12514
13251
  if (cmd === void 0) return passOutput();
13252
+ const taskOutput = extractTasksOutput(cmd);
13253
+ if (taskOutput !== null) {
13254
+ const { id } = taskOutput;
13255
+ recordStat("session_hint", 0, 0);
13256
+ return denyOutput(
13257
+ "Use `token-goat bash-output " + id + "` to recall this task output (already cached). Append `--tail <n>` or `--grep PATTERN` to slice it."
13258
+ );
13259
+ }
13260
+ const findResult = extractFindCommand(cmd);
13261
+ if (findResult !== null) {
13262
+ const { extGlob, isXargsGrepL } = findResult;
13263
+ recordStat("session_hint", 0, 0);
13264
+ if (isXargsGrepL) {
13265
+ return denyOutput(
13266
+ "`find | xargs grep -l` is a slow symbol search. Use `token-goat refs <symbol>` or `rg -l <symbol>` for faster symbol-file discovery."
13267
+ );
13268
+ }
13269
+ const fdHint = extGlob ? "Use `fd '" + extGlob + "'` for faster file discovery (respects .gitignore)." : "Use `fd` for faster file discovery (respects .gitignore).";
13270
+ return contextOutput(
13271
+ "`find` is slow and ignores .gitignore. " + fdHint + " For symbol definitions, use `token-goat symbol <Name>`."
13272
+ );
13273
+ }
13274
+ if (extractDirectoryListing(cmd)) {
13275
+ recordStat("session_hint", 0, 0);
13276
+ return contextOutput(
13277
+ "Use `token-goat map --compact` (~300 tokens) for a repo overview, or `token-goat map <dir>` for a subdirectory."
13278
+ );
13279
+ }
13280
+ if (extractSedLineRange(cmd)) {
13281
+ recordStat("session_hint", 0, 0);
13282
+ return contextOutput(
13283
+ 'Use `token-goat section "<file>::HeadingName"` to read one section instead of a line range.'
13284
+ );
13285
+ }
13286
+ const catJsonPipe = extractCatJsonPipe(cmd);
13287
+ if (catJsonPipe !== null) {
13288
+ const { filePath } = catJsonPipe;
13289
+ recordStat("session_hint", 0, 0);
13290
+ return contextOutput(
13291
+ '`cat | jq` loads the whole file. Use `token-goat config-get "' + filePath + '" KEY_NAME` or `token-goat section "' + filePath + '::sectionName"` to slice one value.'
13292
+ );
13293
+ }
13294
+ const catResult = extractCatFile(cmd);
13295
+ if (catResult !== null) {
13296
+ const { filePath, isDoc, isEnv, isConfig, isSql } = catResult;
13297
+ recordStat("session_hint", 0, 0);
13298
+ if (isSql) {
13299
+ return contextOutput(
13300
+ '`cat` loads the entire file into context. Use `token-goat section "' + filePath + '::table_name"` to pull one CREATE TABLE / CREATE TYPE block.'
13301
+ );
13302
+ }
13303
+ const hint = isEnv ? 'Use `token-goat config-get "' + filePath + '" KEY_NAME` to read a specific variable.' : isConfig ? 'Use `token-goat config-get "' + filePath + '" KEY_NAME` or `token-goat section "' + filePath + '::sectionName"` to read a specific value.' : isDoc ? 'Use `token-goat section "' + filePath + '::SectionHeading"` to read one section.' : 'Use `token-goat read "' + filePath + '::SymbolName"` to read one function or class.';
13304
+ return denyOutput("`cat` loads the entire file into context. " + hint);
13305
+ }
13306
+ const wslCatResult = extractWslCatFile(cmd);
13307
+ if (wslCatResult !== null) {
13308
+ const { filePath, isDoc, isEnv, isConfig, isSql } = wslCatResult;
13309
+ recordStat("session_hint", 0, 0);
13310
+ if (isSql) {
13311
+ return contextOutput(
13312
+ '`cat` loads the entire file into context. Use `token-goat section "' + filePath + '::table_name"` to pull one CREATE TABLE / CREATE TYPE block.'
13313
+ );
13314
+ }
13315
+ const hint = isEnv ? 'Use `token-goat config-get "' + filePath + '" KEY_NAME` to read a specific variable.' : isConfig ? 'Use `token-goat config-get "' + filePath + '" KEY_NAME` or `token-goat section "' + filePath + '::sectionName"` to read a specific value.' : isDoc ? 'Use `token-goat section "' + filePath + '::SectionHeading"` to read one section.' : 'Use `token-goat read "' + filePath + '::SymbolName"` to read one function or class.';
13316
+ return denyOutput("`cat` loads the entire file into context. " + hint);
13317
+ }
13318
+ const pyRead = extractPythonFileRead(cmd);
13319
+ if (pyRead !== null) {
13320
+ const { filePath, isDoc } = pyRead;
13321
+ const hint = isDoc ? 'Use `token-goat section "' + filePath + '::SectionHeading"` to read one section.' : 'Use `token-goat read "' + filePath + '::SymbolName"` to extract a specific symbol.';
13322
+ recordStat("session_hint", 0, 0);
13323
+ return denyOutput("Python `open()` file reads bypass read hooks. " + hint);
13324
+ }
13325
+ const tailResult = extractTailFile(cmd);
13326
+ if (tailResult !== null) {
13327
+ const { filePath, isDoc } = tailResult;
13328
+ const hint = isDoc ? 'Use `token-goat section "' + filePath + '::SectionHeading"` to read one section.' : 'Use `token-goat read "' + filePath + '::SymbolName"` or `token-goat skeleton "' + filePath + '"` to see the file structure.';
13329
+ recordStat("session_hint", 0, 0);
13330
+ return contextOutput("`tail` bypasses read hooks. " + hint);
13331
+ }
13332
+ const headResult = extractHeadFile(cmd);
13333
+ if (headResult !== null) {
13334
+ const { filePath, isDoc, isConfig } = headResult;
13335
+ const hint = isConfig ? 'Use `token-goat config-get "' + filePath + '" KEY_NAME` or `token-goat section "' + filePath + '::sectionName"` to read a specific value.' : isDoc ? 'Use `token-goat section "' + filePath + '::SectionHeading"` to read one section.' : 'Use `token-goat read "' + filePath + '::SymbolName"` or `token-goat skeleton "' + filePath + '"` to see the file structure.';
13336
+ recordStat("session_hint", 0, 0);
13337
+ return contextOutput("`head` bypasses read hooks. " + hint);
13338
+ }
13339
+ const nodeRead = extractNodeFileRead(cmd);
13340
+ if (nodeRead !== null) {
13341
+ const { filePath, isDoc, isConfig } = nodeRead;
13342
+ const hint = isDoc ? 'Use `token-goat section "' + filePath + '::SectionHeading"` to read one section.' : isConfig ? 'Use `token-goat config-get "' + filePath + '" KEY_NAME` or `token-goat section "' + filePath + '::sectionName"` to read a specific value.' : 'Use `token-goat read "' + filePath + '::SymbolName"` to extract a specific symbol.';
13343
+ recordStat("session_hint", 0, 0);
13344
+ return denyOutput("Node.js `fs.readFileSync()` bypasses read hooks. " + hint);
13345
+ }
13346
+ if (extractGrepPipeChain(cmd)) {
13347
+ recordStat("session_hint", 0, 0);
13348
+ return contextOutput(
13349
+ "Collapse `grep | grep` into `rg -e PAT1 -e PAT2` (single pass). For symbol discovery: `token-goat refs <symbol>` or `token-goat semantic`."
13350
+ );
13351
+ }
13352
+ const mdHeadingGrep = extractMarkdownHeadingGrep(cmd);
13353
+ if (mdHeadingGrep !== null) {
13354
+ const { filePath } = mdHeadingGrep;
13355
+ recordStat("session_hint", 0, 0);
13356
+ return contextOutput(
13357
+ 'Use `token-goat outline "' + filePath + '"` to get all headings with line ranges \u2014 then `token-goat section "' + filePath + '::Heading"` to read one section.'
13358
+ );
13359
+ }
13360
+ const rgSymbol = extractRgSymbolSearch(cmd);
13361
+ if (rgSymbol !== null) {
13362
+ const { identifier } = rgSymbol;
13363
+ recordStat("session_hint", 0, 0);
13364
+ return contextOutput(
13365
+ "Use `token-goat symbol " + identifier + "` to jump directly to the definition without scanning the file."
13366
+ );
13367
+ }
13368
+ const rgStructural = extractRgStructuralSearch(cmd);
13369
+ if (rgStructural !== null) {
13370
+ const { filePath } = rgStructural;
13371
+ recordStat("session_hint", 0, 0);
13372
+ return contextOutput(
13373
+ 'Searching for code definitions with `rg`/`grep` is slower than surgical reads. Use `token-goat skeleton "' + filePath + '"` to see all symbols with line numbers, or `token-goat outline "' + filePath + '"` for symbols with docstrings and line ranges.'
13374
+ );
13375
+ }
12515
13376
  const monitoringHint = getMonitoringRecallHint(cmd);
12516
13377
  if (monitoringHint !== null) {
12517
- const monCmdHash = fingerprintContent(cmd).slice(0, 16);
13378
+ const monCmdHash = shortFingerprint(cmd);
12518
13379
  const monOutputId = getBashOutputId(monCmdHash);
12519
13380
  if (monOutputId !== null) {
12520
13381
  const monEntry = getBashOutput(monOutputId);
@@ -12533,8 +13394,32 @@ function preBashHandler(event) {
12533
13394
  );
12534
13395
  }
12535
13396
  }
13397
+ const curlDl = extractCurlDownload(cmd);
13398
+ if (curlDl !== null) {
13399
+ const prevPath = getCurlDownloadPath(curlDl.url);
13400
+ if (prevPath !== null) {
13401
+ recordStat("session_hint", 0, 0);
13402
+ return denyOutput(
13403
+ "Already downloaded to " + prevPath + " earlier this session. Use `rg '<pattern>' " + prevPath + '` to search it, or `token-goat read "' + prevPath + '::SectionName"` to read a part of it.'
13404
+ );
13405
+ }
13406
+ }
13407
+ if (isCurlGetCommand(cmd)) {
13408
+ const curlCacheKey = extractCurlUrl(cmd) ?? cmd;
13409
+ const curlHash = shortFingerprint(curlCacheKey);
13410
+ const curlOutputId = getBashOutputId(curlHash);
13411
+ if (curlOutputId !== null) {
13412
+ const curlEntry = getBashOutput(curlOutputId);
13413
+ const curlBytes = curlEntry?.sizeBytes ?? 0;
13414
+ recordStat("bash_compress:recall", curlBytes, Math.round(curlBytes / 4));
13415
+ const curlPreview = cmd.length > 60 ? cmd.slice(0, 57) + "..." : cmd;
13416
+ return contextOutput(
13417
+ "curl response cached (`" + curlPreview + "`). Use `token-goat bash-output " + curlOutputId + "` to recall it. Append `--grep PATTERN` to filter or `--section HeadingName` for a markdown section."
13418
+ );
13419
+ }
13420
+ }
12536
13421
  if (!isBuildCommand(cmd)) return passOutput();
12537
- const cmdHash = fingerprintContent(cmd).slice(0, 16);
13422
+ const cmdHash = shortFingerprint(cmd);
12538
13423
  const outputId = getBashOutputId(cmdHash);
12539
13424
  if (outputId === null) return passOutput();
12540
13425
  const entry = getBashOutput(outputId);
@@ -12559,12 +13444,17 @@ async function postBashHandler(event) {
12559
13444
  try {
12560
13445
  const cmd = extractCommand(event);
12561
13446
  if (cmd === void 0) return passOutput();
13447
+ const curlDl = extractCurlDownload(cmd);
13448
+ if (curlDl !== null) {
13449
+ recordCurlDownload(curlDl.url, curlDl.outputPath);
13450
+ }
12562
13451
  const isMonitoring = getMonitoringRecallHint(cmd) !== null;
12563
- if (!isMonitoring && !isBuildCommand(cmd)) return passOutput();
13452
+ if (!isMonitoring && !isBuildCommand(cmd) && !isCurlGetCommand(cmd)) return passOutput();
12564
13453
  const output = extractBashOutput(event.raw);
12565
13454
  if (Buffer.byteLength(output, "utf-8") < MIN_CACHE_BYTES) return passOutput();
12566
13455
  const cwd = typeof event.raw["cwd"] === "string" ? event.raw["cwd"] : null;
12567
- const simpleHash = fingerprintContent(cmd).slice(0, 16);
13456
+ const cacheKey = isCurlGetCommand(cmd) ? extractCurlUrl(cmd) ?? cmd : cmd;
13457
+ const simpleHash = shortFingerprint(cacheKey);
12568
13458
  const id = await storeBashOutput(cmd, output, 0, cwd);
12569
13459
  recordBashOutput(simpleHash, id, Buffer.byteLength(output, "utf-8"));
12570
13460
  } catch {
@@ -12575,8 +13465,8 @@ registerHook("post_tool_use", postBashHandler, { toolName: "Bash" });
12575
13465
 
12576
13466
  // src/image_shrink.ts
12577
13467
  init_define_import_meta_env();
12578
- import * as fs8 from "node:fs";
12579
- import * as path10 from "node:path";
13468
+ import * as fs9 from "node:fs";
13469
+ import * as path12 from "node:path";
12580
13470
  var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
12581
13471
  ".png",
12582
13472
  ".jpg",
@@ -12603,7 +13493,7 @@ async function loadSharp() {
12603
13493
  return _sharpCache;
12604
13494
  }
12605
13495
  function isImagePath(p) {
12606
- return IMAGE_EXTENSIONS.has(path10.extname(p).toLowerCase());
13496
+ return IMAGE_EXTENSIONS.has(path12.extname(p).toLowerCase());
12607
13497
  }
12608
13498
  async function shrinkImage(input, opts) {
12609
13499
  const maxDimension = opts?.maxDimension ?? DEFAULT_MAX_DIMENSION;
@@ -12635,7 +13525,7 @@ async function shrinkImage(input, opts) {
12635
13525
  }
12636
13526
  function statSize2(absPath) {
12637
13527
  try {
12638
- const st = fs8.statSync(absPath);
13528
+ const st = fs9.statSync(absPath);
12639
13529
  return st.isFile() ? st.size : null;
12640
13530
  } catch {
12641
13531
  return null;
@@ -12649,7 +13539,7 @@ async function preReadImageHandler(event) {
12649
13539
  if (size === null || size < DEFAULT_SIZE_THRESHOLD_BYTES) return passOutput();
12650
13540
  let input;
12651
13541
  try {
12652
- input = fs8.readFileSync(filePath);
13542
+ input = fs9.readFileSync(filePath);
12653
13543
  } catch {
12654
13544
  return passOutput();
12655
13545
  }
@@ -12658,7 +13548,7 @@ async function preReadImageHandler(event) {
12658
13548
  const saved = result.originalBytes - result.shrunkBytes;
12659
13549
  const pct = Math.round(saved / result.originalBytes * 100);
12660
13550
  const dataUrl = `data:image/${result.format};base64,${result.data.toString("base64")}`;
12661
- const summary = `token-goat shrank ${path10.basename(filePath)}: ${Math.round(result.originalBytes / 1024)}kb -> ${Math.round(result.shrunkBytes / 1024)}kb (${pct}% smaller, ${result.width}x${result.height} ${result.format}).`;
13551
+ const summary = `token-goat shrank ${path12.basename(filePath)}: ${Math.round(result.originalBytes / 1024)}kb -> ${Math.round(result.shrunkBytes / 1024)}kb (${pct}% smaller, ${result.width}x${result.height} ${result.format}).`;
12662
13552
  return contextOutput(`${summary}
12663
13553
  ${dataUrl}`);
12664
13554
  }
@@ -12737,7 +13627,7 @@ async function relay(eventName) {
12737
13627
 
12738
13628
  // src/section_reader.ts
12739
13629
  init_define_import_meta_env();
12740
- import { readFileSync as readFileSync5 } from "node:fs";
13630
+ import { readFileSync as readFileSync6 } from "node:fs";
12741
13631
  function parseHeadingSpec(spec) {
12742
13632
  const m = /^(.*?)#(\d+)$/.exec(spec);
12743
13633
  if (m !== null && m[1] !== void 0 && m[2] !== void 0) {
@@ -12745,6 +13635,18 @@ function parseHeadingSpec(spec) {
12745
13635
  }
12746
13636
  return { base: spec.trim(), ordinal: null };
12747
13637
  }
13638
+ function normalizeHeading(s) {
13639
+ let n = s.replace(/[—–]/g, "-");
13640
+ n = n.replace(/\s*\([^)]+\)\s*$/, "");
13641
+ n = n.replace(/^\d+\.\s+/, "");
13642
+ return n.replace(/\s+/g, " ").trim();
13643
+ }
13644
+ function normalizeHeadingStrip(s) {
13645
+ let n = s.replace(/\s*[—–].*$/, "");
13646
+ n = n.replace(/\s*\([^)]+\)\s*$/, "");
13647
+ n = n.replace(/^\d+\.\s+/, "");
13648
+ return n.replace(/\s+/g, " ").trim();
13649
+ }
12748
13650
  var MARKDOWN_HEADER_RE = /^(#{1,6})\s+(.+?)\s*#*\s*$/;
12749
13651
  var TABLE_HEADER_RE = /^\s*\[+\s*([^\]]+?)\s*\]+\s*$/;
12750
13652
  var PYTHON_HEADER_RE = /^(\s*)(?:async\s+)?(?:def|class)\s+([A-Za-z_]\w*)/;
@@ -12819,7 +13721,7 @@ function sectionEndIndex(headers, headerPos, totalLines) {
12819
13721
  function readSection(filePath, headingSpec) {
12820
13722
  let text;
12821
13723
  try {
12822
- text = readFileSync5(filePath, "utf-8");
13724
+ text = readFileSync6(filePath, "utf-8");
12823
13725
  } catch {
12824
13726
  return null;
12825
13727
  }
@@ -12829,10 +13731,15 @@ function readSection(filePath, headingSpec) {
12829
13731
  const headers = findHeaders(text, language);
12830
13732
  const lines = text.split("\n");
12831
13733
  const target = base.toLowerCase();
13734
+ const normalizedTarget = normalizeHeading(base).toLowerCase();
13735
+ const strippedTarget = normalizeHeadingStrip(base).toLowerCase();
12832
13736
  const matches = [];
12833
13737
  for (let i = 0; i < headers.length; i++) {
12834
13738
  const h = headers[i];
12835
- if (h !== void 0 && h.heading.toLowerCase() === target) matches.push(i);
13739
+ if (h === void 0) continue;
13740
+ if (h.heading.toLowerCase() === target || normalizeHeading(h.heading).toLowerCase() === normalizedTarget || normalizeHeadingStrip(h.heading).toLowerCase() === strippedTarget) {
13741
+ matches.push(i);
13742
+ }
12836
13743
  }
12837
13744
  if (matches.length === 0) return null;
12838
13745
  const pick = ordinal === null ? 0 : ordinal - 1;
@@ -12856,9 +13763,9 @@ function readSection(filePath, headingSpec) {
12856
13763
 
12857
13764
  // src/install.ts
12858
13765
  init_define_import_meta_env();
12859
- import * as fs9 from "node:fs";
12860
- import * as os2 from "node:os";
12861
- import * as path11 from "node:path";
13766
+ import * as fs10 from "node:fs";
13767
+ import * as os3 from "node:os";
13768
+ import * as path13 from "node:path";
12862
13769
  var HOOK_EVENT_MAP = [
12863
13770
  ["PreToolUse", "pre_tool_use"],
12864
13771
  ["PostToolUse", "post_tool_use"],
@@ -12869,13 +13776,13 @@ function hookCommand(eventArg) {
12869
13776
  return `token-goat hook ${eventArg}`;
12870
13777
  }
12871
13778
  function settingsPath(scope) {
12872
- const base = scope === "user" ? path11.join(os2.homedir(), ".claude") : path11.join(process.cwd(), ".claude");
12873
- return path11.join(base, "settings.json");
13779
+ const base = scope === "user" ? path13.join(os3.homedir(), ".claude") : path13.join(process.cwd(), ".claude");
13780
+ return path13.join(base, "settings.json");
12874
13781
  }
12875
13782
  function readSettings(p) {
12876
13783
  let raw;
12877
13784
  try {
12878
- raw = fs9.readFileSync(p, "utf8");
13785
+ raw = fs10.readFileSync(p, "utf8");
12879
13786
  } catch {
12880
13787
  return {};
12881
13788
  }
@@ -12915,7 +13822,7 @@ function installHooks(scope = "user") {
12915
13822
  return { scope, settingsPath: p, alreadyInstalled: true };
12916
13823
  }
12917
13824
  settings.hooks = hooks;
12918
- fs9.mkdirSync(path11.dirname(p), { recursive: true });
13825
+ fs10.mkdirSync(path13.dirname(p), { recursive: true });
12919
13826
  atomicWriteText(p, `${JSON.stringify(settings, null, 2)}
12920
13827
  `);
12921
13828
  return { scope, settingsPath: p, alreadyInstalled: false };
@@ -12954,7 +13861,7 @@ function uninstallHooks(scope = "user") {
12954
13861
  } else {
12955
13862
  settings.hooks = hooks;
12956
13863
  }
12957
- fs9.mkdirSync(path11.dirname(p), { recursive: true });
13864
+ fs10.mkdirSync(path13.dirname(p), { recursive: true });
12958
13865
  atomicWriteText(p, `${JSON.stringify(settings, null, 2)}
12959
13866
  `);
12960
13867
  return true;
@@ -12963,21 +13870,21 @@ function uninstallHooks(scope = "user") {
12963
13870
  // src/worker.ts
12964
13871
  init_define_import_meta_env();
12965
13872
  import { spawn } from "node:child_process";
12966
- import * as fs10 from "node:fs";
12967
- import * as path12 from "node:path";
13873
+ import * as fs11 from "node:fs";
13874
+ import * as path14 from "node:path";
12968
13875
  import { Worker, isMainThread, parentPort, workerData } from "node:worker_threads";
12969
13876
  import { fileURLToPath } from "node:url";
12970
13877
  var DEFAULT_POLL_INTERVAL_MS = 2e3;
12971
13878
  function dirtyQueuePathFor(dir) {
12972
- return path12.join(dir, "queue", "dirty.txt");
13879
+ return path14.join(dir, "queue", "dirty.txt");
12973
13880
  }
12974
13881
  function workerPidPath(dir = dataDir()) {
12975
- return path12.join(dir, "worker.pid");
13882
+ return path14.join(dir, "worker.pid");
12976
13883
  }
12977
13884
  function getDirtyPathsFor(dir) {
12978
13885
  let raw;
12979
13886
  try {
12980
- raw = fs10.readFileSync(dirtyQueuePathFor(dir), "utf8");
13887
+ raw = fs11.readFileSync(dirtyQueuePathFor(dir), "utf8");
12981
13888
  } catch {
12982
13889
  return [];
12983
13890
  }
@@ -12993,7 +13900,7 @@ function getDirtyPathsFor(dir) {
12993
13900
  }
12994
13901
  function clearDirtyQueueFor(dir) {
12995
13902
  try {
12996
- fs10.rmSync(dirtyQueuePathFor(dir), { force: true });
13903
+ fs11.rmSync(dirtyQueuePathFor(dir), { force: true });
12997
13904
  } catch {
12998
13905
  }
12999
13906
  }
@@ -13003,7 +13910,7 @@ function processDirtyBatch(paths, index = (absPath) => {
13003
13910
  }) {
13004
13911
  let indexed = 0;
13005
13912
  for (const p of paths) {
13006
- if (!fs10.existsSync(p)) continue;
13913
+ if (!p || !fs11.existsSync(p)) continue;
13007
13914
  const sha = fingerprintFile(p);
13008
13915
  if (sha === null) continue;
13009
13916
  index(p, sha);
@@ -13028,7 +13935,7 @@ function pidAlive(pid) {
13028
13935
  }
13029
13936
  function readPidFile(dir) {
13030
13937
  try {
13031
- const raw = fs10.readFileSync(workerPidPath(dir), "utf8").trim();
13938
+ const raw = fs11.readFileSync(workerPidPath(dir), "utf8").trim();
13032
13939
  if (!/^\d+$/.test(raw)) return null;
13033
13940
  return parseInt(raw, 10);
13034
13941
  } catch {
@@ -13051,7 +13958,7 @@ function stopWorker(dir = dataDir()) {
13051
13958
  }
13052
13959
  }
13053
13960
  try {
13054
- fs10.rmSync(workerPidPath(dir), { force: true });
13961
+ fs11.rmSync(workerPidPath(dir), { force: true });
13055
13962
  } catch {
13056
13963
  }
13057
13964
  return alive;
@@ -13059,7 +13966,11 @@ function stopWorker(dir = dataDir()) {
13059
13966
  function startDetachedWorker(opts) {
13060
13967
  const pollIntervalMs = opts?.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
13061
13968
  const dir = opts?.dataDir ?? dataDir();
13062
- fs10.mkdirSync(dir, { recursive: true });
13969
+ try {
13970
+ fs11.mkdirSync(dir, { recursive: true });
13971
+ } catch (e) {
13972
+ if (e.code !== "EEXIST" || !fs11.existsSync(dir)) throw e;
13973
+ }
13063
13974
  const child = spawn(
13064
13975
  process.execPath,
13065
13976
  [fileURLToPath(import.meta.url), "--worker-daemon"],
@@ -13078,7 +13989,7 @@ function startDetachedWorker(opts) {
13078
13989
  if (pid === void 0) {
13079
13990
  throw new Error("startDetachedWorker: spawn produced no pid");
13080
13991
  }
13081
- fs10.writeFileSync(workerPidPath(dir), `${pid}
13992
+ fs11.writeFileSync(workerPidPath(dir), `${pid}
13082
13993
  `);
13083
13994
  child.unref();
13084
13995
  return pid;
@@ -13121,7 +14032,7 @@ workerEntry();
13121
14032
 
13122
14033
  // src/skill_cache.ts
13123
14034
  init_define_import_meta_env();
13124
- import * as fs11 from "fs/promises";
14035
+ import * as fs12 from "fs/promises";
13125
14036
  import { resolve as resolve4 } from "path";
13126
14037
  var COMPACT_END_MARKER = "<!-- COMPACT_END -->";
13127
14038
  var _skillOutputsDirOverride = null;
@@ -13134,7 +14045,7 @@ function skillOutputsDir() {
13134
14045
  }
13135
14046
  async function ensureSkillsDir() {
13136
14047
  try {
13137
- await fs11.mkdir(skillOutputsDir(), { recursive: true });
14048
+ await fs12.mkdir(skillOutputsDir(), { recursive: true });
13138
14049
  } catch {
13139
14050
  }
13140
14051
  }
@@ -13161,7 +14072,7 @@ function extractCompactFromMarker(body) {
13161
14072
  const lines = body.split("\n");
13162
14073
  for (let i = 0; i < lines.length; i++) {
13163
14074
  const stripped = lines[i].trim();
13164
- if (stripped.startsWith("```") || stripped.startsWith("~~~")) {
14075
+ if (isCodeFenceDelimiter(stripped)) {
13165
14076
  inCodeBlock = !inCodeBlock;
13166
14077
  continue;
13167
14078
  }
@@ -13176,14 +14087,14 @@ function extractCompactFromMarker(body) {
13176
14087
  async function listOutputs() {
13177
14088
  try {
13178
14089
  const dir = skillOutputsDir();
13179
- const entries = await fs11.readdir(dir, { withFileTypes: true });
14090
+ const entries = await fs12.readdir(dir, { withFileTypes: true });
13180
14091
  const metas = [];
13181
14092
  for (const entry of entries) {
13182
14093
  if (!entry.isFile() || !entry.name.endsWith(".meta")) {
13183
14094
  continue;
13184
14095
  }
13185
14096
  try {
13186
- const content = await fs11.readFile(resolve4(dir, entry.name), "utf-8");
14097
+ const content = await fs12.readFile(resolve4(dir, entry.name), "utf-8");
13187
14098
  const meta = JSON.parse(content);
13188
14099
  metas.push(meta);
13189
14100
  } catch {
@@ -13228,13 +14139,13 @@ async function listSkills(sessionId) {
13228
14139
  const compactFileId = `${safeSession}-${meta.skillName.replace(":", "_")}-compact`;
13229
14140
  let compactLen = 0;
13230
14141
  try {
13231
- const stat3 = await fs11.stat(resolve4(dir, compactFileId));
14142
+ const stat3 = await fs12.stat(resolve4(dir, compactFileId));
13232
14143
  compactLen = stat3.size;
13233
14144
  } catch {
13234
14145
  compactLen = 0;
13235
14146
  }
13236
14147
  const hasMarker = extractCompactFromMarker(
13237
- await fs11.readFile(resolve4(dir, `${meta.outputId}.txt`), "utf-8").catch(() => "")
14148
+ await fs12.readFile(resolve4(dir, `${meta.outputId}.txt`), "utf-8").catch(() => "")
13238
14149
  ) !== null;
13239
14150
  results.push({
13240
14151
  name: meta.skillName,
@@ -13266,7 +14177,7 @@ async function getSkillFilePath(skillName) {
13266
14177
 
13267
14178
  // src/config.ts
13268
14179
  init_define_import_meta_env();
13269
- import * as fs12 from "node:fs";
14180
+ import * as fs13 from "node:fs";
13270
14181
 
13271
14182
  // node_modules/smol-toml/dist/index.js
13272
14183
  init_define_import_meta_env();
@@ -13998,8 +14909,8 @@ function envInt(key, defaultVal) {
13998
14909
  }
13999
14910
 
14000
14911
  // src/config.ts
14001
- function defaultCompactAssistConfig() {
14002
- return {
14912
+ var CONFIG_DEFAULTS = {
14913
+ compact_assist: {
14003
14914
  enabled: true,
14004
14915
  triggers: ["manual", "auto"],
14005
14916
  min_events: 3,
@@ -14014,10 +14925,8 @@ function defaultCompactAssistConfig() {
14014
14925
  lazy_skill_injection: true,
14015
14926
  max_manifest_chars: 1600,
14016
14927
  harness: "auto"
14017
- };
14018
- }
14019
- function defaultBashCompressConfig() {
14020
- return {
14928
+ },
14929
+ bash_compress: {
14021
14930
  enabled: true,
14022
14931
  disabled_filters: [],
14023
14932
  max_lines: 1e3,
@@ -14027,32 +14936,22 @@ function defaultBashCompressConfig() {
14027
14936
  cache_max_file_count: 4096,
14028
14937
  cache_max_bytes: 16 * 1024 * 1024,
14029
14938
  cache_max_bytes_per_output: 50 * 1024 * 1024
14030
- };
14031
- }
14032
- function defaultBashDiffConfig() {
14033
- return {
14939
+ },
14940
+ bash_diff: {
14034
14941
  max_hunks_per_file: 10,
14035
14942
  hunk_density_cap: true
14036
- };
14037
- }
14038
- function defaultSeverityLogConfig() {
14039
- return {
14943
+ },
14944
+ bash_severity_log: {
14040
14945
  context_lines: 3,
14041
14946
  score_threshold: 0.5
14042
- };
14043
- }
14044
- function defaultCodeCompressConfig() {
14045
- return {
14947
+ },
14948
+ post_read_code_compress: {
14046
14949
  min_lines: 200
14047
- };
14048
- }
14049
- function defaultSessionBriefConfig() {
14050
- return {
14950
+ },
14951
+ session_brief: {
14051
14952
  enabled: true
14052
- };
14053
- }
14054
- function defaultSkillPreservationConfig() {
14055
- return {
14953
+ },
14954
+ skill_preservation: {
14056
14955
  enabled: true,
14057
14956
  max_cache_bytes: 5 * 1024 * 1024,
14058
14957
  orphan_sweep_enabled: true,
@@ -14064,25 +14963,19 @@ function defaultSkillPreservationConfig() {
14064
14963
  pre_skill_enabled: true,
14065
14964
  first_load_compact: false,
14066
14965
  post_compact_full_loads: false
14067
- };
14068
- }
14069
- function defaultCuratorConfig() {
14070
- return {
14966
+ },
14967
+ curator: {
14071
14968
  enabled: true,
14072
14969
  min_samples: 10,
14073
14970
  threshold_pct: 20
14074
- };
14075
- }
14076
- function defaultHintBudgetConfig() {
14077
- return {
14971
+ },
14972
+ hint_budget: {
14078
14973
  enabled: true,
14079
14974
  max_per_session: 100,
14080
14975
  max_structured_per_session: 30,
14081
14976
  max_index_only_per_session: 30
14082
- };
14083
- }
14084
- function defaultImageShrinkConfig() {
14085
- return {
14977
+ },
14978
+ image_shrink: {
14086
14979
  prefer_avif: true,
14087
14980
  avif_quality: 60,
14088
14981
  jpeg_quality: 75,
@@ -14090,27 +14983,19 @@ function defaultImageShrinkConfig() {
14090
14983
  orphan_sweep_enabled: true,
14091
14984
  orphan_age_secs: 604800,
14092
14985
  screenshot_redirect: true
14093
- };
14094
- }
14095
- function defaultRepomapConfig() {
14096
- return {
14986
+ },
14987
+ repomap: {
14097
14988
  compact_file_threshold: 50,
14098
14989
  exclude_tests: true
14099
- };
14100
- }
14101
- function defaultOverflowGuardConfig() {
14102
- return {
14990
+ },
14991
+ overflow_guard: {
14103
14992
  enabled: true,
14104
14993
  max_tokens: 25e3
14105
- };
14106
- }
14107
- function defaultStatsConfig() {
14108
- return {
14994
+ },
14995
+ stats: {
14109
14996
  record_zero_savings: false
14110
- };
14111
- }
14112
- function defaultHintsConfig() {
14113
- return {
14997
+ },
14998
+ hints: {
14114
14999
  suppress_after_ignored: 5,
14115
15000
  quiet_hours: "",
14116
15001
  json_sidecar: false,
@@ -14134,51 +15019,40 @@ function defaultHintsConfig() {
14134
15019
  truncated_read_min_lines: 200,
14135
15020
  protect_recent_reads: 4,
14136
15021
  prompt_triggers: []
14137
- };
14138
- }
14139
- function defaultHooksConfig() {
14140
- return {
15022
+ },
15023
+ hooks: {
14141
15024
  watchdog_ms: 700
14142
- };
14143
- }
14144
- function defaultWebFetchConfig() {
14145
- return {
15025
+ },
15026
+ webfetch: {
14146
15027
  allow: [],
14147
15028
  deny: [],
14148
15029
  max_file_count: 4096,
14149
15030
  max_bytes: 32 * 1024 * 1024,
14150
15031
  compress_bodies: true,
14151
15032
  compress_min_bytes: 16 * 1024
14152
- };
14153
- }
14154
- function defaultWorkerConfig() {
14155
- return {
15033
+ },
15034
+ worker: {
14156
15035
  watchdog_enabled: true,
14157
15036
  max_pool_workers: 4,
14158
15037
  blocked_roots: []
14159
- };
14160
- }
14161
- function defaultIndexingConfig() {
14162
- return {
15038
+ },
15039
+ indexing: {
14163
15040
  large_file_symbol_only_kb: 500,
14164
15041
  large_file_skip_kb: 2048,
14165
15042
  skip_dirs: []
14166
- };
14167
- }
14168
- function defaultCompressionConfig() {
14169
- return {
15043
+ },
15044
+ compression: {
14170
15045
  profile: "auto"
14171
- };
14172
- }
14173
- function defaultContextConfig() {
14174
- return {
15046
+ },
15047
+ context: {
14175
15048
  model_window_tokens: 2e5
14176
- };
14177
- }
14178
- function defaultInjectionConfig() {
14179
- return {
15049
+ },
15050
+ injection: {
14180
15051
  enabled: true
14181
- };
15052
+ }
15053
+ };
15054
+ function getDefaultConfig(section2) {
15055
+ return structuredClone(CONFIG_DEFAULTS[section2] ?? {});
14182
15056
  }
14183
15057
  function validatedBool(raw, def) {
14184
15058
  if (typeof raw === "boolean") return raw;
@@ -14251,7 +15125,7 @@ function loadConfig() {
14251
15125
  const p = configPath();
14252
15126
  let currentMtime = 0;
14253
15127
  try {
14254
- currentMtime = fs12.statSync(p).mtimeMs;
15128
+ currentMtime = fs13.statSync(p).mtimeMs;
14255
15129
  } catch {
14256
15130
  }
14257
15131
  const envFp = configEnvFingerprint();
@@ -14261,7 +15135,7 @@ function loadConfig() {
14261
15135
  let raw = {};
14262
15136
  if (currentMtime !== 0) {
14263
15137
  try {
14264
- const text = fs12.readFileSync(p, "utf8");
15138
+ const text = fs13.readFileSync(p, "utf8");
14265
15139
  raw = parse(text);
14266
15140
  } catch {
14267
15141
  }
@@ -14272,7 +15146,7 @@ function loadConfig() {
14272
15146
  }
14273
15147
  function _buildConfig(raw) {
14274
15148
  const ca_raw = section(raw, "compact_assist");
14275
- const ca = defaultCompactAssistConfig();
15149
+ const ca = getDefaultConfig("compact_assist");
14276
15150
  ca.enabled = validatedBool(ca_raw["enabled"], ca.enabled);
14277
15151
  ca.triggers = validatedStrList(ca_raw["triggers"], ca.triggers);
14278
15152
  ca.min_events = validatedInt(ca_raw["min_events"], ca.min_events, 0, 1e3);
@@ -14290,7 +15164,7 @@ function _buildConfig(raw) {
14290
15164
  ca.enabled = envBool("TOKEN_GOAT_COMPACT_ASSIST", envBool("TOKENWISE_COMPACT_ASSIST", ca.enabled));
14291
15165
  ca.lazy_skill_injection = envBool("TOKEN_GOAT_LAZY_SKILL_INJECTION", ca.lazy_skill_injection);
14292
15166
  const bc_raw = section(raw, "bash_compress");
14293
- const bc = defaultBashCompressConfig();
15167
+ const bc = getDefaultConfig("bash_compress");
14294
15168
  bc.enabled = validatedBool(bc_raw["enabled"], bc.enabled);
14295
15169
  bc.disabled_filters = validatedStrList(bc_raw["disabled_filters"], bc.disabled_filters);
14296
15170
  bc.max_lines = validatedInt(bc_raw["max_lines"], bc.max_lines, 50, 1e5);
@@ -14306,22 +15180,22 @@ function _buildConfig(raw) {
14306
15180
  bc.cache_max_bytes = envInt("TOKEN_GOAT_BASH_CACHE_MAX_BYTES", bc.cache_max_bytes);
14307
15181
  bc.cache_max_bytes_per_output = envInt("TOKEN_GOAT_BASH_CACHE_MAX_BYTES_PER_OUTPUT", bc.cache_max_bytes_per_output);
14308
15182
  const bd_raw = section(raw, "bash_diff");
14309
- const bd = defaultBashDiffConfig();
15183
+ const bd = getDefaultConfig("bash_diff");
14310
15184
  bd.max_hunks_per_file = validatedInt(bd_raw["max_hunks_per_file"], bd.max_hunks_per_file, 1, 1e4);
14311
15185
  bd.hunk_density_cap = validatedBool(bd_raw["hunk_density_cap"], bd.hunk_density_cap);
14312
15186
  const sl_raw = section(raw, "bash_severity_log");
14313
- const sl = defaultSeverityLogConfig();
15187
+ const sl = getDefaultConfig("bash_severity_log");
14314
15188
  sl.context_lines = validatedInt(sl_raw["context_lines"], sl.context_lines, 0, 100);
14315
15189
  sl.score_threshold = validatedFloat(sl_raw["score_threshold"], sl.score_threshold, 0, 1);
14316
15190
  const cc_raw = section(raw, "post_read_code_compress");
14317
- const cc = defaultCodeCompressConfig();
15191
+ const cc = getDefaultConfig("post_read_code_compress");
14318
15192
  cc.min_lines = validatedInt(cc_raw["min_lines"], cc.min_lines, 0, 1e6);
14319
15193
  const sb_raw = section(raw, "session_brief");
14320
- const sb = defaultSessionBriefConfig();
15194
+ const sb = getDefaultConfig("session_brief");
14321
15195
  sb.enabled = validatedBool(sb_raw["enabled"], sb.enabled);
14322
15196
  sb.enabled = envBool("TOKEN_GOAT_SESSION_BRIEF", sb.enabled);
14323
15197
  const sp_raw = section(raw, "skill_preservation");
14324
- const sp = defaultSkillPreservationConfig();
15198
+ const sp = getDefaultConfig("skill_preservation");
14325
15199
  sp.enabled = validatedBool(sp_raw["enabled"], sp.enabled);
14326
15200
  sp.max_cache_bytes = validatedInt(sp_raw["max_cache_bytes"], sp.max_cache_bytes, 64 * 1024, 512 * 1024 * 1024);
14327
15201
  sp.orphan_sweep_enabled = validatedBool(sp_raw["orphan_sweep_enabled"], sp.orphan_sweep_enabled);
@@ -14338,7 +15212,7 @@ function _buildConfig(raw) {
14338
15212
  sp.pre_skill_enabled = envBool("TOKEN_GOAT_PRE_SKILL", sp.pre_skill_enabled);
14339
15213
  sp.orphan_sweep_enabled = envBool("TOKEN_GOAT_ORPHAN_SWEEP", sp.orphan_sweep_enabled);
14340
15214
  const is_raw = section(raw, "image_shrink");
14341
- const is_cfg = defaultImageShrinkConfig();
15215
+ const is_cfg = getDefaultConfig("image_shrink");
14342
15216
  is_cfg.prefer_avif = validatedBool(is_raw["prefer_avif"], is_cfg.prefer_avif);
14343
15217
  is_cfg.avif_quality = validatedInt(is_raw["avif_quality"], is_cfg.avif_quality, 1, 100);
14344
15218
  is_cfg.jpeg_quality = validatedInt(is_raw["jpeg_quality"], is_cfg.jpeg_quality, 1, 100);
@@ -14349,33 +15223,33 @@ function _buildConfig(raw) {
14349
15223
  is_cfg.prefer_avif = envBool("TOKEN_GOAT_PREFER_AVIF", is_cfg.prefer_avif);
14350
15224
  is_cfg.max_image_pixels = envInt("TOKEN_GOAT_MAX_IMAGE_PIXELS", is_cfg.max_image_pixels);
14351
15225
  const cur_raw = section(raw, "curator");
14352
- const cur = defaultCuratorConfig();
15226
+ const cur = getDefaultConfig("curator");
14353
15227
  cur.enabled = validatedBool(cur_raw["enabled"], cur.enabled);
14354
15228
  cur.min_samples = validatedInt(cur_raw["min_samples"], cur.min_samples, 0, 1e4);
14355
15229
  cur.threshold_pct = validatedInt(cur_raw["threshold_pct"], cur.threshold_pct, 0, 100);
14356
15230
  cur.enabled = envBool("TOKEN_GOAT_CURATOR", cur.enabled);
14357
15231
  const hb_raw = section(raw, "hint_budget");
14358
- const hb = defaultHintBudgetConfig();
15232
+ const hb = getDefaultConfig("hint_budget");
14359
15233
  hb.enabled = validatedBool(hb_raw["enabled"], hb.enabled);
14360
15234
  hb.max_per_session = validatedInt(hb_raw["max_per_session"], hb.max_per_session, 0, 1e6);
14361
15235
  hb.max_structured_per_session = validatedInt(hb_raw["max_structured_per_session"], hb.max_structured_per_session, 0, 1e6);
14362
15236
  hb.max_index_only_per_session = validatedInt(hb_raw["max_index_only_per_session"], hb.max_index_only_per_session, 0, 1e6);
14363
15237
  hb.enabled = envBool("TOKEN_GOAT_HINT_BUDGET", hb.enabled);
14364
15238
  const rm_raw = section(raw, "repomap");
14365
- const rm = defaultRepomapConfig();
15239
+ const rm = getDefaultConfig("repomap");
14366
15240
  rm.compact_file_threshold = validatedInt(rm_raw["compact_file_threshold"], rm.compact_file_threshold, 0, 1e5);
14367
15241
  rm.exclude_tests = validatedBool(rm_raw["exclude_tests"], rm.exclude_tests);
14368
15242
  rm.compact_file_threshold = envInt("TOKEN_GOAT_REPOMAP_COMPACT_THRESHOLD", rm.compact_file_threshold);
14369
15243
  rm.exclude_tests = envBool("TOKEN_GOAT_REPOMAP_EXCLUDE_TESTS", rm.exclude_tests);
14370
15244
  const og_raw = section(raw, "overflow_guard");
14371
- const og = defaultOverflowGuardConfig();
15245
+ const og = getDefaultConfig("overflow_guard");
14372
15246
  og.enabled = validatedBool(og_raw["enabled"], og.enabled);
14373
15247
  og.max_tokens = validatedInt(og_raw["max_tokens"], og.max_tokens, 1e3, 1e6);
14374
15248
  const st_raw = section(raw, "stats");
14375
- const st = defaultStatsConfig();
15249
+ const st = getDefaultConfig("stats");
14376
15250
  st.record_zero_savings = validatedBool(st_raw["record_zero_savings"], st.record_zero_savings);
14377
15251
  const hi_raw = section(raw, "hints");
14378
- const hi = defaultHintsConfig();
15252
+ const hi = getDefaultConfig("hints");
14379
15253
  hi.suppress_after_ignored = validatedInt(hi_raw["suppress_after_ignored"], hi.suppress_after_ignored, 0, 1e3);
14380
15254
  hi.quiet_hours = validatedStr(hi_raw["quiet_hours"], hi.quiet_hours);
14381
15255
  hi.json_sidecar = validatedBool(hi_raw["json_sidecar"], hi.json_sidecar);
@@ -14408,11 +15282,11 @@ function _buildConfig(raw) {
14408
15282
  })).filter((t) => t.keywords.length > 0 && t.hint.length > 0);
14409
15283
  }
14410
15284
  const hk_raw = section(raw, "hooks");
14411
- const hk = defaultHooksConfig();
15285
+ const hk = getDefaultConfig("hooks");
14412
15286
  hk.watchdog_ms = validatedInt(hk_raw["watchdog_ms"], hk.watchdog_ms, 100, 3e4);
14413
15287
  hk.watchdog_ms = envInt("TOKEN_GOAT_HOOK_WATCHDOG_MS", hk.watchdog_ms);
14414
15288
  const wf_raw = section(raw, "webfetch");
14415
- const wf = defaultWebFetchConfig();
15289
+ const wf = getDefaultConfig("webfetch");
14416
15290
  wf.allow = validatedStrList(wf_raw["allow"], wf.allow);
14417
15291
  wf.deny = validatedStrList(wf_raw["deny"], wf.deny);
14418
15292
  wf.max_file_count = validatedInt(wf_raw["max_file_count"], wf.max_file_count, 0, 1e7);
@@ -14421,27 +15295,27 @@ function _buildConfig(raw) {
14421
15295
  wf.compress_min_bytes = validatedInt(wf_raw["compress_min_bytes"], wf.compress_min_bytes, 1024, 10 * 1024 * 1024);
14422
15296
  wf.compress_bodies = envBool("TOKEN_GOAT_WEB_COMPRESS", wf.compress_bodies);
14423
15297
  const wk_raw = section(raw, "worker");
14424
- const wk = defaultWorkerConfig();
15298
+ const wk = getDefaultConfig("worker");
14425
15299
  wk.watchdog_enabled = validatedBool(wk_raw["watchdog_enabled"], wk.watchdog_enabled);
14426
15300
  wk.max_pool_workers = validatedInt(wk_raw["max_pool_workers"], wk.max_pool_workers, 1, 8);
14427
15301
  wk.blocked_roots = validatedStrList(wk_raw["blocked_roots"], wk.blocked_roots);
14428
15302
  wk.watchdog_enabled = envBool("TOKEN_GOAT_WORKER_WATCHDOG", wk.watchdog_enabled);
14429
15303
  wk.max_pool_workers = envInt("TOKEN_GOAT_WORKER_MAX_POOL", wk.max_pool_workers);
14430
15304
  const ix_raw = section(raw, "indexing");
14431
- const ix = defaultIndexingConfig();
15305
+ const ix = getDefaultConfig("indexing");
14432
15306
  ix.large_file_symbol_only_kb = validatedInt(ix_raw["large_file_symbol_only_kb"], ix.large_file_symbol_only_kb, 1, 1048576);
14433
15307
  ix.large_file_skip_kb = validatedInt(ix_raw["large_file_skip_kb"], ix.large_file_skip_kb, 1, 1048576);
14434
15308
  ix.skip_dirs = validatedStrList(ix_raw["skip_dirs"], ix.skip_dirs);
14435
15309
  const cpr_raw = section(raw, "compression");
14436
- const cpr = defaultCompressionConfig();
15310
+ const cpr = getDefaultConfig("compression");
14437
15311
  cpr.profile = validatedStr(cpr_raw["profile"], cpr.profile);
14438
15312
  cpr.profile = envStr("TOKEN_GOAT_COMPRESS_PROFILE", cpr.profile);
14439
15313
  const ctx_raw = section(raw, "context");
14440
- const ctx = defaultContextConfig();
15314
+ const ctx = getDefaultConfig("context");
14441
15315
  ctx.model_window_tokens = validatedInt(ctx_raw["model_window_tokens"], ctx.model_window_tokens, 1e4, 1e7);
14442
15316
  ctx.model_window_tokens = envInt("TOKEN_GOAT_MODEL_WINDOW_TOKENS", ctx.model_window_tokens);
14443
15317
  const inj_raw = section(raw, "injection");
14444
- const inj = defaultInjectionConfig();
15318
+ const inj = getDefaultConfig("injection");
14445
15319
  inj.enabled = validatedBool(inj_raw["enabled"], inj.enabled);
14446
15320
  inj.enabled = envBool("TOKEN_GOAT_INJECTION_ENABLED", inj.enabled);
14447
15321
  return {
@@ -14471,9 +15345,9 @@ function _buildConfig(raw) {
14471
15345
 
14472
15346
  // src/cli_doctor.ts
14473
15347
  init_define_import_meta_env();
14474
- import * as fs13 from "fs";
14475
- import * as path13 from "path";
14476
- import { execSync } from "child_process";
15348
+ import * as fs14 from "fs";
15349
+ import * as path15 from "path";
15350
+ import { execSync, spawnSync as spawnSync2 } from "child_process";
14477
15351
  function checkWorkerRunning() {
14478
15352
  try {
14479
15353
  const output = execSync("tasklist", { encoding: "utf-8" });
@@ -14483,8 +15357,8 @@ function checkWorkerRunning() {
14483
15357
  }
14484
15358
  }
14485
15359
  function checkDbExists(dataDir2) {
14486
- const dbPath = path13.join(dataDir2, "index.db");
14487
- if (!fs13.existsSync(dbPath)) {
15360
+ const dbPath = path15.join(dataDir2, "index.db");
15361
+ if (!fs14.existsSync(dbPath)) {
14488
15362
  return {
14489
15363
  name: "Database",
14490
15364
  status: "warn",
@@ -14494,7 +15368,7 @@ function checkDbExists(dataDir2) {
14494
15368
  return {
14495
15369
  name: "Database",
14496
15370
  status: "ok",
14497
- message: `index.db exists (${Math.round(fs13.statSync(dbPath).size / 1024)} KB)`
15371
+ message: `index.db exists (${Math.round(fs14.statSync(dbPath).size / 1024)} KB)`
14498
15372
  };
14499
15373
  }
14500
15374
  function checkInstall() {
@@ -14514,7 +15388,7 @@ function checkInstall() {
14514
15388
  }
14515
15389
  }
14516
15390
  function checkConfigValid(configPath2) {
14517
- if (!fs13.existsSync(configPath2)) {
15391
+ if (!fs14.existsSync(configPath2)) {
14518
15392
  return {
14519
15393
  name: "Config",
14520
15394
  status: "warn",
@@ -14522,7 +15396,7 @@ function checkConfigValid(configPath2) {
14522
15396
  };
14523
15397
  }
14524
15398
  try {
14525
- const content = fs13.readFileSync(configPath2, "utf-8");
15399
+ const content = fs14.readFileSync(configPath2, "utf-8");
14526
15400
  JSON.parse(content);
14527
15401
  return {
14528
15402
  name: "Config",
@@ -14533,18 +15407,22 @@ function checkConfigValid(configPath2) {
14533
15407
  return {
14534
15408
  name: "Config",
14535
15409
  status: "fail",
14536
- message: `config invalid: ${err2 instanceof Error ? err2.message : "unknown error"}`
15410
+ message: `config invalid: ${extractErrorMessage(err2, "unknown error")}`
14537
15411
  };
14538
15412
  }
14539
15413
  }
14540
15414
  function checkDiskSpace(dataDir2) {
14541
15415
  try {
14542
- const statSync8 = execSync(`df -h ${dataDir2}`, { encoding: "utf-8" });
14543
- const lines = statSync8.trim().split("\n");
15416
+ const result = spawnSync2("df", ["-h", dataDir2], { encoding: "utf-8" });
15417
+ const stdout = typeof result.stdout === "string" ? result.stdout : "";
15418
+ if (result.error !== void 0 || result.status !== 0 || !stdout) {
15419
+ return { name: "Disk Space", status: "warn", message: "could not determine" };
15420
+ }
15421
+ const lines = stdout.trim().split("\n");
14544
15422
  if (lines.length < 2) {
14545
15423
  return { name: "Disk Space", status: "warn", message: "could not determine" };
14546
15424
  }
14547
- const parts = lines[1].split(/\s+/);
15425
+ const parts = lines[1].trim().split(/\s+/);
14548
15426
  const available = parts[3] || "unknown";
14549
15427
  return { name: "Disk Space", status: "ok", message: `${available} available` };
14550
15428
  } catch {
@@ -14556,9 +15434,9 @@ function runDoctor(dataDir2, configPath2) {
14556
15434
  results.push(checkInstall());
14557
15435
  results.push(checkWorkerRunning() ? { name: "Worker", status: "ok", message: "running" } : { name: "Worker", status: "warn", message: "not running" });
14558
15436
  const homeDir = process.env["HOME"] || process.env["USERPROFILE"] || "~";
14559
- const actualDataDir = dataDir2 || path13.join(homeDir, ".token-goat");
15437
+ const actualDataDir = dataDir2 || path15.join(homeDir, ".token-goat");
14560
15438
  results.push(checkDbExists(actualDataDir));
14561
- const actualConfigPath = configPath2 || path13.join(actualDataDir, "config.json");
15439
+ const actualConfigPath = configPath2 || path15.join(actualDataDir, "config.json");
14562
15440
  results.push(checkConfigValid(actualConfigPath));
14563
15441
  results.push(checkDiskSpace(actualDataDir));
14564
15442
  return results;
@@ -14597,7 +15475,7 @@ init_define_import_meta_env();
14597
15475
  var _byId2 = /* @__PURE__ */ new Map();
14598
15476
  var _urlIndex = /* @__PURE__ */ new Map();
14599
15477
  function cacheIdForUrl(url) {
14600
- return fingerprintContent(url).slice(0, 16);
15478
+ return shortFingerprint(url);
14601
15479
  }
14602
15480
  function storeWebOutput(url, content) {
14603
15481
  const cacheId = cacheIdForUrl(url);
@@ -14717,12 +15595,10 @@ async function getSectionContent(fileId, heading) {
14717
15595
  var CliError = class extends Error {
14718
15596
  };
14719
15597
  function out(text) {
14720
- process.stdout.write(text.endsWith("\n") ? text : `${text}
14721
- `);
15598
+ process.stdout.write(ensureNewline(text));
14722
15599
  }
14723
15600
  function err(text) {
14724
- process.stderr.write(text.endsWith("\n") ? text : `${text}
14725
- `);
15601
+ process.stderr.write(ensureNewline(text));
14726
15602
  }
14727
15603
  function previewLines(body, n) {
14728
15604
  return body.split(/\r?\n/).slice(0, n).join("\n");
@@ -14882,7 +15758,7 @@ function _applyFiltersAndPrint(content, opts) {
14882
15758
  const n = Number.parseInt(opts.tail, 10);
14883
15759
  return Number.isFinite(n) && n > 0 ? n : 80;
14884
15760
  })() : 80;
14885
- const applyElision = (lines2, headN2, tailN2) => lines2.length > headN2 + tailN2 ? [...lines2.slice(0, headN2), "...(elided)...", ...lines2.slice(lines2.length - tailN2)] : lines2;
15761
+ const applyElision = (lines2, headN2, tailN2) => lines2.length > headN2 + tailN2 + 1 ? [...lines2.slice(0, headN2), "...(elided)...", ...lines2.slice(lines2.length - tailN2)] : lines2;
14886
15762
  let result = lines;
14887
15763
  if (opts.head === void 0 && opts.tail === void 0 && opts.grep === void 0) {
14888
15764
  result = applyElision(lines, headN, tailN);
@@ -14897,10 +15773,21 @@ function _applyFiltersAndPrint(content, opts) {
14897
15773
  }
14898
15774
  function cmdBashOutput(id, opts) {
14899
15775
  if (opts.file !== void 0) {
15776
+ if (opts.file.includes("\0")) {
15777
+ throw new CliError("--file path contains a null byte");
15778
+ }
15779
+ if (!isWindows() && /^\/dev\/(stdin|fd\/0)$|^\/proc\/self\/fd\/0$/.test(opts.file) && process.stdin.isTTY) {
15780
+ throw new CliError("--file /dev/stdin requires piped input; redirect a file instead");
15781
+ }
14900
15782
  let content;
14901
15783
  try {
14902
- content = fs14.readFileSync(opts.file, "utf-8");
14903
- } catch {
15784
+ const st = fs15.statSync(opts.file);
15785
+ if (st.isFIFO() || st.isSocket()) {
15786
+ throw new CliError(`--file '${opts.file}' is a special file (FIFO or socket) \u2014 only regular files are supported`);
15787
+ }
15788
+ content = fs15.readFileSync(opts.file, "utf-8");
15789
+ } catch (e) {
15790
+ if (e instanceof CliError) throw e;
14904
15791
  throw new CliError(`cannot read file: ${opts.file}`);
14905
15792
  }
14906
15793
  _applyFiltersAndPrint(content, opts);
@@ -14920,12 +15807,12 @@ async function cmdSkillBody(name, opts) {
14920
15807
  if (filePath === null) {
14921
15808
  throw new CliError(`skill '${name}' not found`);
14922
15809
  }
14923
- const body = fs14.readFileSync(filePath, "utf-8");
15810
+ const body = fs15.readFileSync(filePath, "utf-8");
14924
15811
  if (opts.compact === true) {
14925
15812
  const lines = body.split("\n");
14926
15813
  const end = lines.findIndex((l) => l.includes("COMPACT_END"));
14927
15814
  if (end !== -1) {
14928
- out(lines.slice(0, end).join("\n"));
15815
+ out(lines.slice(end + 1).join("\n"));
14929
15816
  } else {
14930
15817
  out(body);
14931
15818
  }
@@ -14938,7 +15825,7 @@ async function cmdSkillCompact(name) {
14938
15825
  if (filePath === null) {
14939
15826
  throw new CliError(`skill '${name}' not found`);
14940
15827
  }
14941
- const body = fs14.readFileSync(filePath, "utf-8");
15828
+ const body = fs15.readFileSync(filePath, "utf-8");
14942
15829
  const sessionFiles = getSessionFiles();
14943
15830
  const sessionId = Array.from(sessionFiles.keys())[0] ?? "default";
14944
15831
  await storeCompact(sessionId, name, body);
@@ -15025,7 +15912,7 @@ function cmdConfigGet(file, key) {
15025
15912
  }
15026
15913
  function atomicWriteBuffer(dest, data) {
15027
15914
  try {
15028
- if (fs14.statSync(dest).isDirectory()) {
15915
+ if (fs15.statSync(dest).isDirectory()) {
15029
15916
  const e = Object.assign(new Error(`EISDIR: illegal operation on a directory, open '${dest}'`), { code: "EISDIR", path: dest });
15030
15917
  throw e;
15031
15918
  }
@@ -15033,16 +15920,16 @@ function atomicWriteBuffer(dest, data) {
15033
15920
  if (e.code !== "ENOENT") throw e;
15034
15921
  }
15035
15922
  const rnd = Math.random().toString(36).slice(2, 8);
15036
- const tmp = path14.join(path14.dirname(path14.resolve(dest)), `.tmp.${process.pid}.${rnd}`);
15923
+ const tmp = path16.join(path16.dirname(path16.resolve(dest)), `.tmp.${process.pid}.${rnd}`);
15037
15924
  try {
15038
- fs14.writeFileSync(tmp, data, { mode: 384 });
15925
+ fs15.writeFileSync(tmp, data, { mode: 384 });
15039
15926
  try {
15040
- fs14.renameSync(tmp, dest);
15927
+ fs15.renameSync(tmp, dest);
15041
15928
  } catch (e) {
15042
15929
  if (e.code === "EXDEV") {
15043
- fs14.copyFileSync(tmp, dest);
15930
+ fs15.copyFileSync(tmp, dest);
15044
15931
  try {
15045
- fs14.unlinkSync(tmp);
15932
+ fs15.unlinkSync(tmp);
15046
15933
  } catch (ue) {
15047
15934
  process.stderr.write(`token-goat write-file: warning: could not remove temp file ${tmp}: ${ue.message}
15048
15935
  `);
@@ -15053,7 +15940,7 @@ function atomicWriteBuffer(dest, data) {
15053
15940
  }
15054
15941
  } catch (e) {
15055
15942
  try {
15056
- fs14.unlinkSync(tmp);
15943
+ fs15.unlinkSync(tmp);
15057
15944
  } catch {
15058
15945
  }
15059
15946
  throw e;
@@ -15063,9 +15950,9 @@ function mapFsError(e, src, dest) {
15063
15950
  const fe = e;
15064
15951
  if (fe.code === "ENOENT") {
15065
15952
  const errPath = fe.path ?? "";
15066
- const isSource = src !== void 0 && path14.resolve(errPath) === path14.resolve(src);
15953
+ const isSource = src !== void 0 && path16.resolve(errPath) === path16.resolve(src);
15067
15954
  if (isSource) throw new CliError(`source file not found: ${src}`);
15068
- const destDir = dest ? path14.dirname(path14.resolve(dest)) : path14.dirname(path14.resolve(errPath || "."));
15955
+ const destDir = dest ? path16.dirname(path16.resolve(dest)) : path16.dirname(path16.resolve(errPath || "."));
15069
15956
  throw new CliError(`destination directory does not exist: ${destDir}`);
15070
15957
  }
15071
15958
  if (fe.code === "ENOTDIR") {
@@ -15073,7 +15960,7 @@ function mapFsError(e, src, dest) {
15073
15960
  }
15074
15961
  if (fe.code === "EISDIR") {
15075
15962
  const errPath = fe.path ?? "";
15076
- const isSource = src !== void 0 && (errPath === "" || path14.resolve(errPath) === path14.resolve(src));
15963
+ const isSource = src !== void 0 && (errPath === "" || path16.resolve(errPath) === path16.resolve(src));
15077
15964
  if (isSource) throw new CliError(`source is a directory, not a file: ${src}`);
15078
15965
  throw new CliError(`destination is a directory, not a file: ${dest ?? (errPath || "(unknown)")}`);
15079
15966
  }
@@ -15138,8 +16025,8 @@ function cmdWriteFile(dest, opts) {
15138
16025
  if (dest.includes("\0")) {
15139
16026
  throw new CliError("destination path contains a null byte");
15140
16027
  }
15141
- if (process.platform === "win32") {
15142
- const base = path14.basename(dest);
16028
+ if (isWindows()) {
16029
+ const base = path16.basename(dest);
15143
16030
  const stem = base.replace(/\.[^.]*$/, "").toUpperCase();
15144
16031
  if (WIN_RESERVED.has(stem)) {
15145
16032
  throw new CliError(`destination '${base}' is a reserved Windows device name`);
@@ -15158,20 +16045,23 @@ function cmdWriteFile(dest, opts) {
15158
16045
  if (opts.from.includes("\0")) {
15159
16046
  throw new CliError("--from path contains a null byte");
15160
16047
  }
15161
- if (process.platform !== "win32" && /^\/dev\/(stdin|fd\/0)$|^\/proc\/self\/fd\/0$/.test(opts.from) && process.stdin.isTTY) {
16048
+ if (!isWindows() && /^\/dev\/(stdin|fd\/0)$|^\/proc\/self\/fd\/0$/.test(opts.from) && process.stdin.isTTY) {
15162
16049
  throw new CliError("--from /dev/stdin requires piped input; use piped stdin mode or --b64 for interactive use");
15163
16050
  }
15164
16051
  try {
15165
- const st = fs14.statSync(opts.from);
16052
+ const st = fs15.statSync(opts.from);
15166
16053
  if (st.isFIFO() || st.isSocket()) {
15167
16054
  throw new CliError(`--from '${opts.from}' is a special file (FIFO or socket) \u2014 only regular files are supported`);
15168
16055
  }
15169
16056
  const maxFromMB = parseInt(process.env["TOKEN_GOAT_MAX_STDIN_MB"] ?? "512", 10);
15170
- const maxFromBytes = (Number.isFinite(maxFromMB) && maxFromMB > 0 ? maxFromMB : 512) * 1024 * 1024;
16057
+ if (!Number.isFinite(maxFromMB) || maxFromMB <= 0) {
16058
+ throw new CliError(`TOKEN_GOAT_MAX_STDIN_MB must be a positive integer; got '${process.env["TOKEN_GOAT_MAX_STDIN_MB"] ?? ""}'`);
16059
+ }
16060
+ const maxFromBytes = maxFromMB * 1024 * 1024;
15171
16061
  if (st.size > maxFromBytes) {
15172
16062
  throw new CliError(`--from source exceeds size limit (${Math.round(st.size / 1024 / 1024)} MB); set TOKEN_GOAT_MAX_STDIN_MB to override`);
15173
16063
  }
15174
- atomicWriteBuffer(dest, fs14.readFileSync(opts.from));
16064
+ atomicWriteBuffer(dest, fs15.readFileSync(opts.from));
15175
16065
  } catch (e) {
15176
16066
  if (e instanceof CliError) throw e;
15177
16067
  mapFsError(e, opts.from, dest);
@@ -15184,7 +16074,10 @@ function cmdWriteFile(dest, opts) {
15184
16074
  throw new CliError("--b64 payload contains only whitespace \u2014 likely a shell expansion error; pass an empty string explicitly for a zero-byte file");
15185
16075
  }
15186
16076
  const maxB64MB = parseInt(process.env["TOKEN_GOAT_MAX_STDIN_MB"] ?? "512", 10);
15187
- const maxB64Bytes = (Number.isFinite(maxB64MB) && maxB64MB > 0 ? maxB64MB : 512) * 1024 * 1024;
16077
+ if (!Number.isFinite(maxB64MB) || maxB64MB <= 0) {
16078
+ throw new CliError(`TOKEN_GOAT_MAX_STDIN_MB must be a positive integer; got '${process.env["TOKEN_GOAT_MAX_STDIN_MB"] ?? ""}'`);
16079
+ }
16080
+ const maxB64Bytes = maxB64MB * 1024 * 1024;
15188
16081
  const decodedSize = Math.floor(normalized.replace(/=+$/, "").length * 3 / 4);
15189
16082
  if (decodedSize > maxB64Bytes) {
15190
16083
  throw new CliError(`--b64 payload would decode to ${Math.round(decodedSize / 1024 / 1024)} MB which exceeds size limit; set TOKEN_GOAT_MAX_STDIN_MB to override`);
@@ -15285,7 +16178,7 @@ function buildProgram() {
15285
16178
  await fn(...args);
15286
16179
  process.exitCode = 0;
15287
16180
  } catch (e) {
15288
- const msg = e instanceof Error ? e.message : String(e);
16181
+ const msg = extractErrorMessage(e);
15289
16182
  err(`token-goat: ${msg}`);
15290
16183
  process.exitCode = 1;
15291
16184
  }
@@ -15337,7 +16230,7 @@ async function run(argv = process.argv) {
15337
16230
  process.exitCode = 1;
15338
16231
  return;
15339
16232
  }
15340
- const msg = e instanceof Error ? e.message : String(e);
16233
+ const msg = extractErrorMessage(e);
15341
16234
  err(`token-goat: ${msg}`);
15342
16235
  process.exitCode = 1;
15343
16236
  }