token-goat 2.0.1 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/token-goat.mjs +281 -92
  2. package/package.json +1 -1
@@ -982,7 +982,7 @@ var require_command = __commonJS({
982
982
  var EventEmitter = __require("node:events").EventEmitter;
983
983
  var childProcess = __require("node:child_process");
984
984
  var path14 = __require("node:path");
985
- var fs14 = __require("node:fs");
985
+ var fs15 = __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();
@@ -1915,10 +1915,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
1915
1915
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
1916
1916
  function findFile(baseDir, baseName) {
1917
1917
  const localBin = path14.resolve(baseDir, baseName);
1918
- if (fs14.existsSync(localBin)) return localBin;
1918
+ if (fs15.existsSync(localBin)) return localBin;
1919
1919
  if (sourceExt.includes(path14.extname(baseName))) return void 0;
1920
1920
  const foundExt = sourceExt.find(
1921
- (ext) => fs14.existsSync(`${localBin}${ext}`)
1921
+ (ext) => fs15.existsSync(`${localBin}${ext}`)
1922
1922
  );
1923
1923
  if (foundExt) return `${localBin}${foundExt}`;
1924
1924
  return void 0;
@@ -1930,7 +1930,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1930
1930
  if (this._scriptPath) {
1931
1931
  let resolvedScriptPath;
1932
1932
  try {
1933
- resolvedScriptPath = fs14.realpathSync(this._scriptPath);
1933
+ resolvedScriptPath = fs15.realpathSync(this._scriptPath);
1934
1934
  } catch (err2) {
1935
1935
  resolvedScriptPath = this._scriptPath;
1936
1936
  }
@@ -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 fs14 = __require("fs");
3161
+ var fs15 = __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
3165
  var readFileSync11 = (path14) => {
3166
- const fd = fs14.openSync(path14, "r");
3166
+ const fd = fs15.openSync(path14, "r");
3167
3167
  const buffer = Buffer.alloc(MAX_LENGTH);
3168
- const bytesRead = fs14.readSync(fd, buffer, 0, MAX_LENGTH, 0);
3169
- fs14.close(fd, () => {
3168
+ const bytesRead = fs15.readSync(fd, buffer, 0, MAX_LENGTH, 0);
3169
+ fs15.close(fd, () => {
3170
3170
  });
3171
3171
  return buffer.subarray(0, bytesRead);
3172
3172
  };
3173
- var readFile2 = (path14) => new Promise((resolve4, reject) => {
3174
- fs14.open(path14, "r", (err2, fd) => {
3173
+ var readFile3 = (path14) => new Promise((resolve5, reject) => {
3174
+ fs15.open(path14, "r", (err2, fd) => {
3175
3175
  if (err2) {
3176
3176
  reject(err2);
3177
3177
  } else {
3178
3178
  const buffer = Buffer.alloc(MAX_LENGTH);
3179
- fs14.read(fd, buffer, 0, MAX_LENGTH, 0, (_, bytesRead) => {
3180
- resolve4(buffer.subarray(0, bytesRead));
3181
- fs14.close(fd, () => {
3179
+ fs15.read(fd, buffer, 0, MAX_LENGTH, 0, (_, bytesRead) => {
3180
+ resolve5(buffer.subarray(0, bytesRead));
3181
+ fs15.close(fd, () => {
3182
3182
  });
3183
3183
  });
3184
3184
  }
@@ -3188,7 +3188,7 @@ var require_filesystem = __commonJS({
3188
3188
  LDD_PATH,
3189
3189
  SELF_PATH,
3190
3190
  readFileSync: readFileSync11,
3191
- readFile: readFile2
3191
+ readFile: readFile3
3192
3192
  };
3193
3193
  }
3194
3194
  });
@@ -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: readFile2, readFileSync: readFileSync11 } = require_filesystem();
3241
+ var { LDD_PATH, SELF_PATH, readFile: readFile3, readFileSync: readFileSync11 } = require_filesystem();
3242
3242
  var { interpreterPath } = require_elf();
3243
3243
  var cachedFamilyInterpreter;
3244
3244
  var cachedFamilyFilesystem;
@@ -3247,10 +3247,10 @@ var require_detect_libc = __commonJS({
3247
3247
  var commandOut = "";
3248
3248
  var safeCommand = () => {
3249
3249
  if (!commandOut) {
3250
- return new Promise((resolve4) => {
3250
+ return new Promise((resolve5) => {
3251
3251
  childProcess.exec(command, (err2, out2) => {
3252
3252
  commandOut = err2 ? " " : out2;
3253
- resolve4(commandOut);
3253
+ resolve5(commandOut);
3254
3254
  });
3255
3255
  });
3256
3256
  }
@@ -3318,7 +3318,7 @@ var require_detect_libc = __commonJS({
3318
3318
  }
3319
3319
  cachedFamilyFilesystem = null;
3320
3320
  try {
3321
- const lddContent = await readFile2(LDD_PATH);
3321
+ const lddContent = await readFile3(LDD_PATH);
3322
3322
  cachedFamilyFilesystem = getFamilyFromLddContent(lddContent);
3323
3323
  } catch (e) {
3324
3324
  }
@@ -3342,7 +3342,7 @@ var require_detect_libc = __commonJS({
3342
3342
  }
3343
3343
  cachedFamilyInterpreter = null;
3344
3344
  try {
3345
- const selfContent = await readFile2(SELF_PATH);
3345
+ const selfContent = await readFile3(SELF_PATH);
3346
3346
  const path14 = interpreterPath(selfContent);
3347
3347
  cachedFamilyInterpreter = familyFromInterpreterPath(path14);
3348
3348
  } catch (e) {
@@ -3404,7 +3404,7 @@ var require_detect_libc = __commonJS({
3404
3404
  }
3405
3405
  cachedVersionFilesystem = null;
3406
3406
  try {
3407
- const lddContent = await readFile2(LDD_PATH);
3407
+ const lddContent = await readFile3(LDD_PATH);
3408
3408
  const versionMatch = lddContent.match(RE_GLIBC_VERSION);
3409
3409
  if (versionMatch) {
3410
3410
  cachedVersionFilesystem = versionMatch[1];
@@ -7392,14 +7392,14 @@ var require_input = __commonJS({
7392
7392
  return this;
7393
7393
  } else {
7394
7394
  if (this._isStreamInput()) {
7395
- return new Promise((resolve4, reject) => {
7395
+ return new Promise((resolve5, reject) => {
7396
7396
  const finished = () => {
7397
7397
  this._flattenBufferIn();
7398
7398
  sharp.metadata(this.options, (err2, metadata2) => {
7399
7399
  if (err2) {
7400
7400
  reject(is.nativeError(err2, stack));
7401
7401
  } else {
7402
- resolve4(metadata2);
7402
+ resolve5(metadata2);
7403
7403
  }
7404
7404
  });
7405
7405
  };
@@ -7410,12 +7410,12 @@ var require_input = __commonJS({
7410
7410
  }
7411
7411
  });
7412
7412
  } else {
7413
- return new Promise((resolve4, reject) => {
7413
+ return new Promise((resolve5, reject) => {
7414
7414
  sharp.metadata(this.options, (err2, metadata2) => {
7415
7415
  if (err2) {
7416
7416
  reject(is.nativeError(err2, stack));
7417
7417
  } else {
7418
- resolve4(metadata2);
7418
+ resolve5(metadata2);
7419
7419
  }
7420
7420
  });
7421
7421
  });
@@ -7448,25 +7448,25 @@ var require_input = __commonJS({
7448
7448
  return this;
7449
7449
  } else {
7450
7450
  if (this._isStreamInput()) {
7451
- return new Promise((resolve4, reject) => {
7451
+ return new Promise((resolve5, reject) => {
7452
7452
  this.on("finish", function() {
7453
7453
  this._flattenBufferIn();
7454
7454
  sharp.stats(this.options, (err2, stats2) => {
7455
7455
  if (err2) {
7456
7456
  reject(is.nativeError(err2, stack));
7457
7457
  } else {
7458
- resolve4(stats2);
7458
+ resolve5(stats2);
7459
7459
  }
7460
7460
  });
7461
7461
  });
7462
7462
  });
7463
7463
  } else {
7464
- return new Promise((resolve4, reject) => {
7464
+ return new Promise((resolve5, reject) => {
7465
7465
  sharp.stats(this.options, (err2, stats2) => {
7466
7466
  if (err2) {
7467
7467
  reject(is.nativeError(err2, stack));
7468
7468
  } else {
7469
- resolve4(stats2);
7469
+ resolve5(stats2);
7470
7470
  }
7471
7471
  });
7472
7472
  });
@@ -9242,7 +9242,7 @@ var require_output = __commonJS({
9242
9242
  return this;
9243
9243
  } else {
9244
9244
  if (this._isStreamInput()) {
9245
- return new Promise((resolve4, reject) => {
9245
+ return new Promise((resolve5, reject) => {
9246
9246
  this.once("finish", () => {
9247
9247
  this._flattenBufferIn();
9248
9248
  sharp.pipeline(this.options, (err2, data, info) => {
@@ -9250,24 +9250,24 @@ var require_output = __commonJS({
9250
9250
  reject(is.nativeError(err2, stack));
9251
9251
  } else {
9252
9252
  if (this.options.resolveWithObject) {
9253
- resolve4({ data, info });
9253
+ resolve5({ data, info });
9254
9254
  } else {
9255
- resolve4(data);
9255
+ resolve5(data);
9256
9256
  }
9257
9257
  }
9258
9258
  });
9259
9259
  });
9260
9260
  });
9261
9261
  } else {
9262
- return new Promise((resolve4, reject) => {
9262
+ return new Promise((resolve5, reject) => {
9263
9263
  sharp.pipeline(this.options, (err2, data, info) => {
9264
9264
  if (err2) {
9265
9265
  reject(is.nativeError(err2, stack));
9266
9266
  } else {
9267
9267
  if (this.options.resolveWithObject) {
9268
- resolve4({ data, info });
9268
+ resolve5({ data, info });
9269
9269
  } else {
9270
- resolve4(data);
9270
+ resolve5(data);
9271
9271
  }
9272
9272
  }
9273
9273
  });
@@ -9475,7 +9475,7 @@ var {
9475
9475
  } = import_index.default;
9476
9476
 
9477
9477
  // src/cli.ts
9478
- import * as fs13 from "fs";
9478
+ import * as fs14 from "fs";
9479
9479
 
9480
9480
  // src/baseline.ts
9481
9481
  init_define_import_meta_env();
@@ -9492,7 +9492,7 @@ init_define_import_meta_env();
9492
9492
  import { createRequire } from "node:module";
9493
9493
  function resolveVersion() {
9494
9494
  if (true) {
9495
- return "2.0.1";
9495
+ return "2.0.2";
9496
9496
  }
9497
9497
  const require2 = createRequire(import.meta.url);
9498
9498
  const pkg = require2("../package.json");
@@ -10297,8 +10297,11 @@ function wasFileReadThisSession(filePath) {
10297
10297
  function getSessionWebFetches() {
10298
10298
  return _webFetches;
10299
10299
  }
10300
- function getBashOutputId(commandHash) {
10301
- return _bashOutputs.get(commandHash) ?? null;
10300
+ function recordBashOutput(commandHash2, outputId, _sizeBytes) {
10301
+ _bashOutputs.set(commandHash2, outputId);
10302
+ }
10303
+ function getBashOutputId(commandHash2) {
10304
+ return _bashOutputs.get(commandHash2) ?? null;
10302
10305
  }
10303
10306
  registerReset(() => {
10304
10307
  _files = /* @__PURE__ */ new Map();
@@ -10624,7 +10627,19 @@ var MONITORING_COMMAND_PATTERNS = [
10624
10627
  { pattern: /^(?:npx\s+)?eslint(?:\s|$)/, recallHint: '--grep "error|warning|\u2716|problems"' },
10625
10628
  { pattern: /^(?:npx\s+)?prettier(?:\s|$)/, recallHint: '--grep "unchanged|reformatted|error"' },
10626
10629
  { pattern: /^ruff(?:\s|$)/, recallHint: '--grep "error|warning|Found"' },
10627
- { pattern: /^(?:cargo\s+)?clippy/, recallHint: '--grep "error\\[|warning\\["' }
10630
+ { pattern: /^(?:cargo\s+)?clippy/, recallHint: '--grep "error\\[|warning\\["' },
10631
+ // git diff (full diff output — can be very large; excludes --stat which is small)
10632
+ { pattern: /^git diff(?!\s+--stat)(?:\s+HEAD)?(?:\s|$)/, recallHint: '--grep "@@|\\+\\+\\+|---|diff --git"' },
10633
+ { pattern: /^git diff\s+--cached(?!\s+--stat)/, recallHint: '--grep "@@|\\+\\+\\+|---|diff --git"' },
10634
+ // npm run * wrappers (npm test is excluded — too generic; npm run test is explicit)
10635
+ { pattern: /^npm run (?:test|spec)(?:\s|$)/, recallHint: '--grep "FAIL|PASS|Error|Tests:|\u2713|\u2717"' },
10636
+ { pattern: /^npm run build(?:\s|$)/, recallHint: '--grep "error|Built|Failed|\u2713|\u2717"' },
10637
+ { pattern: /^npm run (?:lint|typecheck|check|type-check)(?:\s|$)/, recallHint: '--grep "error|warning|\u2716|problems"' },
10638
+ // External AI peer-review CLI tools (produce large outputs, run repeatedly per session)
10639
+ { pattern: /^codex(?:\s|$)/, recallHint: '--tail 100 --grep "error|suggestion|verdict|conclusion"' },
10640
+ { pattern: /^(?:~\/\.claude\/bin\/|\.claude\/bin\/)?glm\.sh(?:\s|$)/, recallHint: '--tail 100 --grep "error|verdict|conclusion|suggestion"' },
10641
+ // cat of a single source file — output is the full file; pre-bash emits a token-goat read suggestion
10642
+ { pattern: /^cat\s+\S+\.(java|py|ts|tsx|js|jsx|go|rb|rs|cpp|cc|cxx|c|h|hpp|kt|swift|cs|php|scala|clj)\s*$/, recallHint: "--tail 50" }
10628
10643
  ];
10629
10644
  function getMonitoringRecallHint(cmd) {
10630
10645
  for (const { pattern, recallHint } of MONITORING_COMMAND_PATTERNS) {
@@ -11334,11 +11349,164 @@ function fingerprintFile(filePath) {
11334
11349
  return fingerprintContent(data);
11335
11350
  }
11336
11351
 
11352
+ // src/bash_output_cache.ts
11353
+ init_define_import_meta_env();
11354
+ import * as fs7 from "fs/promises";
11355
+ import { resolve as resolve3 } from "path";
11356
+ var _byId = /* @__PURE__ */ new Map();
11357
+ var _globsByKey = /* @__PURE__ */ new Map();
11358
+ var _grepsByKey = /* @__PURE__ */ new Map();
11359
+ var GIT_MUTABLE_RE = /^\s*git\s+(diff|status)\b/i;
11360
+ var LS_CMD_RE = /^\s*(?:ls|eza|exa|dir|Get-ChildItem|gci)\b/i;
11361
+ 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;
11362
+ var NPM_INSTALL_RE = /^\s*npm\s+(?:-\S+\s+)*(?:install|ci)\b/i;
11363
+ var DEP_LOCKFILES = {
11364
+ npm: ["package-lock.json", "yarn.lock"],
11365
+ pip: ["requirements.txt"],
11366
+ uv: ["uv.lock", "requirements.txt"],
11367
+ pnpm: ["pnpm-lock.yaml"],
11368
+ yarn: ["yarn.lock"],
11369
+ cargo: ["Cargo.lock"],
11370
+ bundle: ["Gemfile.lock"],
11371
+ composer: ["composer.lock"]
11372
+ };
11373
+ function isGitMutableCommand(cmd) {
11374
+ return GIT_MUTABLE_RE.test(cmd);
11375
+ }
11376
+ function isDirListingCommand(cmd) {
11377
+ return LS_CMD_RE.test(cmd);
11378
+ }
11379
+ function isDepListCommand(cmd) {
11380
+ return DEP_LIST_RE.test(cmd);
11381
+ }
11382
+ function isNpmInstallCommand(cmd) {
11383
+ return NPM_INSTALL_RE.test(cmd);
11384
+ }
11385
+ async function gitStateFingerprint(cwd) {
11386
+ try {
11387
+ const headResult = runGit(["rev-parse", "HEAD"], { cwd });
11388
+ if (headResult.exitCode !== 0) return null;
11389
+ const headSha = headResult.stdout.trim();
11390
+ let indexMtime = "";
11391
+ try {
11392
+ const stat3 = await fs7.stat(resolve3(cwd, ".git", "index"));
11393
+ indexMtime = stat3.mtimeMs.toString();
11394
+ } catch {
11395
+ }
11396
+ const key = `${headSha}\0${indexMtime}`;
11397
+ return fingerprintContent(key).slice(0, 16);
11398
+ } catch {
11399
+ return null;
11400
+ }
11401
+ }
11402
+ async function dirStateFingerprint(path14) {
11403
+ try {
11404
+ const stat3 = await fs7.stat(path14);
11405
+ if (!stat3.isDirectory()) return null;
11406
+ return fingerprintContent(stat3.mtimeMs.toString()).slice(0, 16);
11407
+ } catch {
11408
+ return null;
11409
+ }
11410
+ }
11411
+ async function depLockfileFingerprint(cmd, cwd) {
11412
+ if (!cwd) return null;
11413
+ const stripped = cmd.trim();
11414
+ const firstToken = stripped.split(/\s+/)[0]?.toLowerCase() || "";
11415
+ const candidates = firstToken === "uv" ? DEP_LOCKFILES["uv"] : DEP_LOCKFILES[firstToken];
11416
+ if (!candidates) return null;
11417
+ for (const lockfile of candidates) {
11418
+ try {
11419
+ const content = await fs7.readFile(resolve3(cwd, lockfile));
11420
+ return fingerprintContent(content).slice(0, 16);
11421
+ } catch {
11422
+ continue;
11423
+ }
11424
+ }
11425
+ return null;
11426
+ }
11427
+ function normalizeCommandForCacheKey(cmd) {
11428
+ let normalized = cmd.trim();
11429
+ normalized = normalized.replace(/\s+/g, " ");
11430
+ normalized = normalized.replace(/\\/g, "/");
11431
+ const tokens = normalized.split(" ");
11432
+ const normalized_tokens = tokens.map((token) => {
11433
+ if (token.startsWith("-") || ["&&", "||", "|", ">", ">>", ";", "&"].includes(token)) {
11434
+ return token;
11435
+ }
11436
+ if (token.startsWith("./") && !token.startsWith("../")) {
11437
+ token = token.slice(2);
11438
+ }
11439
+ if (token.endsWith("/") && token !== "/") {
11440
+ token = token.slice(0, -1);
11441
+ }
11442
+ return token || ".";
11443
+ });
11444
+ return normalized_tokens.join(" ");
11445
+ }
11446
+ async function commandHash(command, cwd = null) {
11447
+ const normalized = normalizeCommandForCacheKey(command);
11448
+ let key = cwd ? `${normalizePath(cwd)}\0${normalized}` : normalized;
11449
+ if (cwd && isGitMutableCommand(command)) {
11450
+ const fp = await gitStateFingerprint(cwd);
11451
+ if (fp) key = `${key}\0git:${fp}`;
11452
+ }
11453
+ if (cwd && isDirListingCommand(command)) {
11454
+ const target = extractLsTarget(command, cwd);
11455
+ if (target) {
11456
+ const fp = await dirStateFingerprint(target);
11457
+ if (fp) key = `${key}\0dir:${fp}`;
11458
+ }
11459
+ }
11460
+ if (isDepListCommand(command)) {
11461
+ const fp = await depLockfileFingerprint(command, cwd);
11462
+ if (fp) key = `${key}\0lockfile:${fp}`;
11463
+ }
11464
+ if (cwd && isNpmInstallCommand(command)) {
11465
+ const fp = await depLockfileFingerprint(command, cwd);
11466
+ if (fp) key = `${key}\0npm-install:${fp}`;
11467
+ }
11468
+ return fingerprintContent(key).slice(0, 16);
11469
+ }
11470
+ function extractLsTarget(cmd, cwd) {
11471
+ const tokens = cmd.trim().split(/\s+/);
11472
+ for (let i = 1; i < tokens.length; i++) {
11473
+ if (!tokens[i].startsWith("-")) {
11474
+ return tokens[i];
11475
+ }
11476
+ }
11477
+ return cwd;
11478
+ }
11479
+ async function storeBashOutput(command, output, exitCode, cwd = null) {
11480
+ const id = await commandHash(command, cwd);
11481
+ const entry = {
11482
+ id,
11483
+ command,
11484
+ output,
11485
+ exitCode,
11486
+ storedAt: Date.now(),
11487
+ sizeBytes: Buffer.byteLength(output, "utf-8")
11488
+ };
11489
+ _byId.set(id, entry);
11490
+ return id;
11491
+ }
11492
+ function getBashOutput(id) {
11493
+ return _byId.get(id) ?? null;
11494
+ }
11495
+ registerReset(() => {
11496
+ _byId = /* @__PURE__ */ new Map();
11497
+ _globsByKey = /* @__PURE__ */ new Map();
11498
+ _grepsByKey = /* @__PURE__ */ new Map();
11499
+ });
11500
+
11337
11501
  // src/hooks_bash.ts
11338
11502
  function extractCommand(event) {
11339
11503
  const cmd = event.toolInput["command"];
11340
11504
  return typeof cmd === "string" && cmd.trim() !== "" ? cmd.trim() : void 0;
11341
11505
  }
11506
+ function extractCatSourceFile(cmd) {
11507
+ 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);
11508
+ return m?.[1] ?? null;
11509
+ }
11342
11510
  function isTscCommand(cmd) {
11343
11511
  return /^\s*tsc(\s|$)/i.test(cmd);
11344
11512
  }
@@ -11363,6 +11531,12 @@ function preBashHandler(event) {
11363
11531
  const monCmdHash = fingerprintContent(cmd).slice(0, 16);
11364
11532
  const monOutputId = getBashOutputId(monCmdHash);
11365
11533
  if (monOutputId !== null) {
11534
+ const catFile = extractCatSourceFile(cmd);
11535
+ if (catFile !== null) {
11536
+ return contextOutput(
11537
+ "Prior output from `" + cmd + "` is cached. Use `token-goat bash-output " + monOutputId + "` to recall the full file, or `token-goat read '" + catFile + "::SymbolName'` to extract only the symbol you need."
11538
+ );
11539
+ }
11366
11540
  const cmdSummary = cmd.length > 60 ? cmd.slice(0, 57) + "..." : cmd;
11367
11541
  return contextOutput(
11368
11542
  "Prior output from `" + cmdSummary + "` is cached.\nUse `token-goat bash-output " + monOutputId + " " + monitoringHint + "` to re-inspect without re-running."
@@ -11376,10 +11550,39 @@ function preBashHandler(event) {
11376
11550
  return contextOutput(buildRecallHint(cmd, outputId));
11377
11551
  }
11378
11552
  registerHook("pre_tool_use", preBashHandler, { toolName: "Bash" });
11553
+ var MIN_CACHE_BYTES = 512;
11554
+ function extractBashOutput(raw) {
11555
+ const resp = raw["tool_response"];
11556
+ if (typeof resp === "string") return resp;
11557
+ if (resp !== null && typeof resp === "object") {
11558
+ const r = resp;
11559
+ for (const key of ["output", "content", "text", "body"]) {
11560
+ if (typeof r[key] === "string") return r[key];
11561
+ }
11562
+ }
11563
+ return "";
11564
+ }
11565
+ async function postBashHandler(event) {
11566
+ try {
11567
+ const cmd = extractCommand(event);
11568
+ if (cmd === void 0) return passOutput();
11569
+ const isMonitoring = getMonitoringRecallHint(cmd) !== null;
11570
+ if (!isMonitoring && !isBuildCommand(cmd)) return passOutput();
11571
+ const output = extractBashOutput(event.raw);
11572
+ if (Buffer.byteLength(output, "utf-8") < MIN_CACHE_BYTES) return passOutput();
11573
+ const cwd = typeof event.raw["cwd"] === "string" ? event.raw["cwd"] : null;
11574
+ const simpleHash = fingerprintContent(cmd).slice(0, 16);
11575
+ const id = await storeBashOutput(cmd, output, 0, cwd);
11576
+ recordBashOutput(simpleHash, id, Buffer.byteLength(output, "utf-8"));
11577
+ } catch {
11578
+ }
11579
+ return passOutput();
11580
+ }
11581
+ registerHook("post_tool_use", postBashHandler, { toolName: "Bash" });
11379
11582
 
11380
11583
  // src/image_shrink.ts
11381
11584
  init_define_import_meta_env();
11382
- import * as fs7 from "node:fs";
11585
+ import * as fs8 from "node:fs";
11383
11586
  import * as path9 from "node:path";
11384
11587
  var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
11385
11588
  ".png",
@@ -11445,7 +11648,7 @@ async function shrinkImage(input, opts) {
11445
11648
  }
11446
11649
  function statSize2(absPath) {
11447
11650
  try {
11448
- const st = fs7.statSync(absPath);
11651
+ const st = fs8.statSync(absPath);
11449
11652
  return st.isFile() ? st.size : null;
11450
11653
  } catch {
11451
11654
  return null;
@@ -11459,7 +11662,7 @@ async function preReadImageHandler(event) {
11459
11662
  if (size === null || size < DEFAULT_SIZE_THRESHOLD_BYTES) return passOutput();
11460
11663
  let input;
11461
11664
  try {
11462
- input = fs7.readFileSync(filePath);
11665
+ input = fs8.readFileSync(filePath);
11463
11666
  } catch {
11464
11667
  return passOutput();
11465
11668
  }
@@ -11477,7 +11680,7 @@ registerHook("pre_tool_use", preReadImageHandler, { toolName: "Read" });
11477
11680
  // src/relay.ts
11478
11681
  var DEFAULT_STDIN_TIMEOUT_MS = 5e3;
11479
11682
  function readStdinJson(timeoutMs = DEFAULT_STDIN_TIMEOUT_MS) {
11480
- return new Promise((resolve4, reject) => {
11683
+ return new Promise((resolve5, reject) => {
11481
11684
  const chunks = [];
11482
11685
  let settled = false;
11483
11686
  const finish = (fn) => {
@@ -11503,7 +11706,7 @@ function readStdinJson(timeoutMs = DEFAULT_STDIN_TIMEOUT_MS) {
11503
11706
  return;
11504
11707
  }
11505
11708
  try {
11506
- resolve4(JSON.parse(text));
11709
+ resolve5(JSON.parse(text));
11507
11710
  } catch (err2) {
11508
11711
  reject(err2 instanceof Error ? err2 : new Error(String(err2)));
11509
11712
  }
@@ -11666,7 +11869,7 @@ function readSection(filePath, headingSpec) {
11666
11869
 
11667
11870
  // src/install.ts
11668
11871
  init_define_import_meta_env();
11669
- import * as fs8 from "node:fs";
11872
+ import * as fs9 from "node:fs";
11670
11873
  import * as os2 from "node:os";
11671
11874
  import * as path10 from "node:path";
11672
11875
  var HOOK_EVENT_MAP = [
@@ -11685,7 +11888,7 @@ function settingsPath(scope) {
11685
11888
  function readSettings(p) {
11686
11889
  let raw;
11687
11890
  try {
11688
- raw = fs8.readFileSync(p, "utf8");
11891
+ raw = fs9.readFileSync(p, "utf8");
11689
11892
  } catch {
11690
11893
  return {};
11691
11894
  }
@@ -11725,7 +11928,7 @@ function installHooks(scope = "user") {
11725
11928
  return { scope, settingsPath: p, alreadyInstalled: true };
11726
11929
  }
11727
11930
  settings.hooks = hooks;
11728
- fs8.mkdirSync(path10.dirname(p), { recursive: true });
11931
+ fs9.mkdirSync(path10.dirname(p), { recursive: true });
11729
11932
  atomicWriteText(p, `${JSON.stringify(settings, null, 2)}
11730
11933
  `);
11731
11934
  return { scope, settingsPath: p, alreadyInstalled: false };
@@ -11764,7 +11967,7 @@ function uninstallHooks(scope = "user") {
11764
11967
  } else {
11765
11968
  settings.hooks = hooks;
11766
11969
  }
11767
- fs8.mkdirSync(path10.dirname(p), { recursive: true });
11970
+ fs9.mkdirSync(path10.dirname(p), { recursive: true });
11768
11971
  atomicWriteText(p, `${JSON.stringify(settings, null, 2)}
11769
11972
  `);
11770
11973
  return true;
@@ -11773,7 +11976,7 @@ function uninstallHooks(scope = "user") {
11773
11976
  // src/worker.ts
11774
11977
  init_define_import_meta_env();
11775
11978
  import { spawn } from "node:child_process";
11776
- import * as fs9 from "node:fs";
11979
+ import * as fs10 from "node:fs";
11777
11980
  import * as path11 from "node:path";
11778
11981
  import { Worker, isMainThread, parentPort, workerData } from "node:worker_threads";
11779
11982
  import { fileURLToPath } from "node:url";
@@ -11787,7 +11990,7 @@ function workerPidPath(dir = dataDir()) {
11787
11990
  function getDirtyPathsFor(dir) {
11788
11991
  let raw;
11789
11992
  try {
11790
- raw = fs9.readFileSync(dirtyQueuePathFor(dir), "utf8");
11993
+ raw = fs10.readFileSync(dirtyQueuePathFor(dir), "utf8");
11791
11994
  } catch {
11792
11995
  return [];
11793
11996
  }
@@ -11803,7 +12006,7 @@ function getDirtyPathsFor(dir) {
11803
12006
  }
11804
12007
  function clearDirtyQueueFor(dir) {
11805
12008
  try {
11806
- fs9.rmSync(dirtyQueuePathFor(dir), { force: true });
12009
+ fs10.rmSync(dirtyQueuePathFor(dir), { force: true });
11807
12010
  } catch {
11808
12011
  }
11809
12012
  }
@@ -11813,7 +12016,7 @@ function processDirtyBatch(paths, index = (absPath) => {
11813
12016
  }) {
11814
12017
  let indexed = 0;
11815
12018
  for (const p of paths) {
11816
- if (!fs9.existsSync(p)) continue;
12019
+ if (!fs10.existsSync(p)) continue;
11817
12020
  const sha = fingerprintFile(p);
11818
12021
  if (sha === null) continue;
11819
12022
  index(p, sha);
@@ -11838,7 +12041,7 @@ function pidAlive(pid) {
11838
12041
  }
11839
12042
  function readPidFile(dir) {
11840
12043
  try {
11841
- const raw = fs9.readFileSync(workerPidPath(dir), "utf8").trim();
12044
+ const raw = fs10.readFileSync(workerPidPath(dir), "utf8").trim();
11842
12045
  if (!/^\d+$/.test(raw)) return null;
11843
12046
  return parseInt(raw, 10);
11844
12047
  } catch {
@@ -11861,7 +12064,7 @@ function stopWorker(dir = dataDir()) {
11861
12064
  }
11862
12065
  }
11863
12066
  try {
11864
- fs9.rmSync(workerPidPath(dir), { force: true });
12067
+ fs10.rmSync(workerPidPath(dir), { force: true });
11865
12068
  } catch {
11866
12069
  }
11867
12070
  return alive;
@@ -11869,7 +12072,7 @@ function stopWorker(dir = dataDir()) {
11869
12072
  function startDetachedWorker(opts) {
11870
12073
  const pollIntervalMs = opts?.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
11871
12074
  const dir = opts?.dataDir ?? dataDir();
11872
- fs9.mkdirSync(dir, { recursive: true });
12075
+ fs10.mkdirSync(dir, { recursive: true });
11873
12076
  const child = spawn(
11874
12077
  process.execPath,
11875
12078
  [fileURLToPath(import.meta.url), "--worker-daemon"],
@@ -11888,7 +12091,7 @@ function startDetachedWorker(opts) {
11888
12091
  if (pid === void 0) {
11889
12092
  throw new Error("startDetachedWorker: spawn produced no pid");
11890
12093
  }
11891
- fs9.writeFileSync(workerPidPath(dir), `${pid}
12094
+ fs10.writeFileSync(workerPidPath(dir), `${pid}
11892
12095
  `);
11893
12096
  child.unref();
11894
12097
  return pid;
@@ -11900,7 +12103,7 @@ async function runWorkerLoop(dir, pollIntervalMs, shouldStop = () => false) {
11900
12103
  } catch {
11901
12104
  }
11902
12105
  if (shouldStop()) break;
11903
- await new Promise((resolve4) => setTimeout(resolve4, pollIntervalMs));
12106
+ await new Promise((resolve5) => setTimeout(resolve5, pollIntervalMs));
11904
12107
  }
11905
12108
  }
11906
12109
  function workerEntry() {
@@ -11925,24 +12128,10 @@ function workerEntry() {
11925
12128
  }
11926
12129
  workerEntry();
11927
12130
 
11928
- // src/bash_output_cache.ts
11929
- init_define_import_meta_env();
11930
- var _byId = /* @__PURE__ */ new Map();
11931
- var _globsByKey = /* @__PURE__ */ new Map();
11932
- var _grepsByKey = /* @__PURE__ */ new Map();
11933
- function getBashOutput(id) {
11934
- return _byId.get(id) ?? null;
11935
- }
11936
- registerReset(() => {
11937
- _byId = /* @__PURE__ */ new Map();
11938
- _globsByKey = /* @__PURE__ */ new Map();
11939
- _grepsByKey = /* @__PURE__ */ new Map();
11940
- });
11941
-
11942
12131
  // src/skill_cache.ts
11943
12132
  init_define_import_meta_env();
11944
- import * as fs10 from "fs/promises";
11945
- import { resolve as resolve3 } from "path";
12133
+ import * as fs11 from "fs/promises";
12134
+ import { resolve as resolve4 } from "path";
11946
12135
  var COMPACT_END_MARKER = "<!-- COMPACT_END -->";
11947
12136
  var _skillOutputsDirOverride = null;
11948
12137
  registerReset(() => {
@@ -11950,11 +12139,11 @@ registerReset(() => {
11950
12139
  });
11951
12140
  function skillOutputsDir() {
11952
12141
  if (_skillOutputsDirOverride) return _skillOutputsDirOverride;
11953
- return resolve3(dataDir(), "skills");
12142
+ return resolve4(dataDir(), "skills");
11954
12143
  }
11955
12144
  async function ensureSkillsDir() {
11956
12145
  try {
11957
- await fs10.mkdir(skillOutputsDir(), { recursive: true });
12146
+ await fs11.mkdir(skillOutputsDir(), { recursive: true });
11958
12147
  } catch {
11959
12148
  }
11960
12149
  }
@@ -11996,14 +12185,14 @@ function extractCompactFromMarker(body) {
11996
12185
  async function listOutputs() {
11997
12186
  try {
11998
12187
  const dir = skillOutputsDir();
11999
- const entries = await fs10.readdir(dir, { withFileTypes: true });
12188
+ const entries = await fs11.readdir(dir, { withFileTypes: true });
12000
12189
  const metas = [];
12001
12190
  for (const entry of entries) {
12002
12191
  if (!entry.isFile() || !entry.name.endsWith(".meta")) {
12003
12192
  continue;
12004
12193
  }
12005
12194
  try {
12006
- const content = await fs10.readFile(resolve3(dir, entry.name), "utf-8");
12195
+ const content = await fs11.readFile(resolve4(dir, entry.name), "utf-8");
12007
12196
  const meta = JSON.parse(content);
12008
12197
  metas.push(meta);
12009
12198
  } catch {
@@ -12028,7 +12217,7 @@ async function storeCompact(sessionId, skillName, compactText, sourceSha) {
12028
12217
  text = `<!-- source_sha: ${sourceSha.slice(0, 12)} -->
12029
12218
  ${text}`;
12030
12219
  }
12031
- await atomicWriteText(resolve3(dir, fileId), text);
12220
+ await atomicWriteText(resolve4(dir, fileId), text);
12032
12221
  } catch {
12033
12222
  }
12034
12223
  }
@@ -12048,13 +12237,13 @@ async function listSkills(sessionId) {
12048
12237
  const compactFileId = `${safeSession}-${meta.skillName.replace(":", "_")}-compact`;
12049
12238
  let compactLen = 0;
12050
12239
  try {
12051
- const stat2 = await fs10.stat(resolve3(dir, compactFileId));
12052
- compactLen = stat2.size;
12240
+ const stat3 = await fs11.stat(resolve4(dir, compactFileId));
12241
+ compactLen = stat3.size;
12053
12242
  } catch {
12054
12243
  compactLen = 0;
12055
12244
  }
12056
12245
  const hasMarker = extractCompactFromMarker(
12057
- await fs10.readFile(resolve3(dir, `${meta.outputId}.txt`), "utf-8").catch(() => "")
12246
+ await fs11.readFile(resolve4(dir, `${meta.outputId}.txt`), "utf-8").catch(() => "")
12058
12247
  ) !== null;
12059
12248
  results.push({
12060
12249
  name: meta.skillName,
@@ -12086,7 +12275,7 @@ async function getSkillFilePath(skillName) {
12086
12275
 
12087
12276
  // src/config.ts
12088
12277
  init_define_import_meta_env();
12089
- import * as fs11 from "node:fs";
12278
+ import * as fs12 from "node:fs";
12090
12279
 
12091
12280
  // node_modules/smol-toml/dist/index.js
12092
12281
  init_define_import_meta_env();
@@ -13071,7 +13260,7 @@ function loadConfig() {
13071
13260
  const p = configPath();
13072
13261
  let currentMtime = 0;
13073
13262
  try {
13074
- currentMtime = fs11.statSync(p).mtimeMs;
13263
+ currentMtime = fs12.statSync(p).mtimeMs;
13075
13264
  } catch {
13076
13265
  }
13077
13266
  const envFp = configEnvFingerprint();
@@ -13081,7 +13270,7 @@ function loadConfig() {
13081
13270
  let raw = {};
13082
13271
  if (currentMtime !== 0) {
13083
13272
  try {
13084
- const text = fs11.readFileSync(p, "utf8");
13273
+ const text = fs12.readFileSync(p, "utf8");
13085
13274
  raw = parse(text);
13086
13275
  } catch {
13087
13276
  }
@@ -14232,7 +14421,7 @@ function renderStats2(opts) {
14232
14421
 
14233
14422
  // src/cli_doctor.ts
14234
14423
  init_define_import_meta_env();
14235
- import * as fs12 from "fs";
14424
+ import * as fs13 from "fs";
14236
14425
  import * as path13 from "path";
14237
14426
  import { execSync } from "child_process";
14238
14427
  function checkWorkerRunning() {
@@ -14245,7 +14434,7 @@ function checkWorkerRunning() {
14245
14434
  }
14246
14435
  function checkDbExists(dataDir2) {
14247
14436
  const dbPath = path13.join(dataDir2, "index.db");
14248
- if (!fs12.existsSync(dbPath)) {
14437
+ if (!fs13.existsSync(dbPath)) {
14249
14438
  return {
14250
14439
  name: "Database",
14251
14440
  status: "warn",
@@ -14255,7 +14444,7 @@ function checkDbExists(dataDir2) {
14255
14444
  return {
14256
14445
  name: "Database",
14257
14446
  status: "ok",
14258
- message: `index.db exists (${Math.round(fs12.statSync(dbPath).size / 1024)} KB)`
14447
+ message: `index.db exists (${Math.round(fs13.statSync(dbPath).size / 1024)} KB)`
14259
14448
  };
14260
14449
  }
14261
14450
  function checkInstall() {
@@ -14275,7 +14464,7 @@ function checkInstall() {
14275
14464
  }
14276
14465
  }
14277
14466
  function checkConfigValid(configPath2) {
14278
- if (!fs12.existsSync(configPath2)) {
14467
+ if (!fs13.existsSync(configPath2)) {
14279
14468
  return {
14280
14469
  name: "Config",
14281
14470
  status: "warn",
@@ -14283,7 +14472,7 @@ function checkConfigValid(configPath2) {
14283
14472
  };
14284
14473
  }
14285
14474
  try {
14286
- const content = fs12.readFileSync(configPath2, "utf-8");
14475
+ const content = fs13.readFileSync(configPath2, "utf-8");
14287
14476
  JSON.parse(content);
14288
14477
  return {
14289
14478
  name: "Config",
@@ -14647,7 +14836,7 @@ function cmdBashOutput(id, opts) {
14647
14836
  if (opts.file !== void 0) {
14648
14837
  let content;
14649
14838
  try {
14650
- content = fs13.readFileSync(opts.file, "utf-8");
14839
+ content = fs14.readFileSync(opts.file, "utf-8");
14651
14840
  } catch {
14652
14841
  throw new CliError(`cannot read file: ${opts.file}`);
14653
14842
  }
@@ -14668,7 +14857,7 @@ async function cmdSkillBody(name, opts) {
14668
14857
  if (filePath === null) {
14669
14858
  throw new CliError(`skill '${name}' not found`);
14670
14859
  }
14671
- const body = fs13.readFileSync(filePath, "utf-8");
14860
+ const body = fs14.readFileSync(filePath, "utf-8");
14672
14861
  if (opts.compact === true) {
14673
14862
  const lines = body.split("\n");
14674
14863
  const end = lines.findIndex((l) => l.includes("COMPACT_END"));
@@ -14686,7 +14875,7 @@ async function cmdSkillCompact(name) {
14686
14875
  if (filePath === null) {
14687
14876
  throw new CliError(`skill '${name}' not found`);
14688
14877
  }
14689
- const body = fs13.readFileSync(filePath, "utf-8");
14878
+ const body = fs14.readFileSync(filePath, "utf-8");
14690
14879
  const sessionFiles = getSessionFiles();
14691
14880
  const sessionId = Array.from(sessionFiles.keys())[0] ?? "default";
14692
14881
  await storeCompact(sessionId, name, body);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "token-goat",
3
- "version": "2.0.1",
3
+ "version": "2.0.2",
4
4
  "description": "Surgical token-reduction companion for Claude Code and other AI coding agents",
5
5
  "type": "module",
6
6
  "main": "./dist/token-goat.mjs",