token-goat 2.0.0 → 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.
- package/README.md +46 -124
- package/dist/token-goat.mjs +1746 -188
- package/package.json +1 -1
package/dist/token-goat.mjs
CHANGED
|
@@ -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
|
|
985
|
-
var
|
|
984
|
+
var path14 = __require("node:path");
|
|
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();
|
|
@@ -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 =
|
|
1918
|
-
if (
|
|
1919
|
-
if (sourceExt.includes(
|
|
1917
|
+
const localBin = path14.resolve(baseDir, baseName);
|
|
1918
|
+
if (fs15.existsSync(localBin)) return localBin;
|
|
1919
|
+
if (sourceExt.includes(path14.extname(baseName))) return void 0;
|
|
1920
1920
|
const foundExt = sourceExt.find(
|
|
1921
|
-
(ext) =>
|
|
1921
|
+
(ext) => fs15.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 =
|
|
1933
|
+
resolvedScriptPath = fs15.realpathSync(this._scriptPath);
|
|
1934
1934
|
} catch (err2) {
|
|
1935
1935
|
resolvedScriptPath = this._scriptPath;
|
|
1936
1936
|
}
|
|
1937
|
-
executableDir =
|
|
1938
|
-
|
|
1937
|
+
executableDir = path14.resolve(
|
|
1938
|
+
path14.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 =
|
|
1945
|
+
const legacyName = path14.basename(
|
|
1946
1946
|
this._scriptPath,
|
|
1947
|
-
|
|
1947
|
+
path14.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(
|
|
1958
|
+
launchWithNode = sourceExt.includes(path14.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 =
|
|
2798
|
+
this._name = path14.basename(filename, path14.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(
|
|
2813
|
-
if (
|
|
2814
|
-
this._executableDir =
|
|
2812
|
+
executableDir(path15) {
|
|
2813
|
+
if (path15 === void 0) return this._executableDir;
|
|
2814
|
+
this._executableDir = path15;
|
|
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
|
|
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
|
-
var
|
|
3166
|
-
const fd =
|
|
3165
|
+
var readFileSync11 = (path14) => {
|
|
3166
|
+
const fd = fs15.openSync(path14, "r");
|
|
3167
3167
|
const buffer = Buffer.alloc(MAX_LENGTH);
|
|
3168
|
-
const bytesRead =
|
|
3169
|
-
|
|
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
|
|
3174
|
-
|
|
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
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
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
|
}
|
|
@@ -3187,8 +3187,8 @@ var require_filesystem = __commonJS({
|
|
|
3187
3187
|
module.exports = {
|
|
3188
3188
|
LDD_PATH,
|
|
3189
3189
|
SELF_PATH,
|
|
3190
|
-
readFileSync:
|
|
3191
|
-
readFile:
|
|
3190
|
+
readFileSync: readFileSync11,
|
|
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:
|
|
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((
|
|
3250
|
+
return new Promise((resolve5) => {
|
|
3251
3251
|
childProcess.exec(command, (err2, out2) => {
|
|
3252
3252
|
commandOut = err2 ? " " : out2;
|
|
3253
|
-
|
|
3253
|
+
resolve5(commandOut);
|
|
3254
3254
|
});
|
|
3255
3255
|
});
|
|
3256
3256
|
}
|
|
@@ -3292,11 +3292,11 @@ var require_detect_libc = __commonJS({
|
|
|
3292
3292
|
}
|
|
3293
3293
|
return null;
|
|
3294
3294
|
};
|
|
3295
|
-
var familyFromInterpreterPath = (
|
|
3296
|
-
if (
|
|
3297
|
-
if (
|
|
3295
|
+
var familyFromInterpreterPath = (path14) => {
|
|
3296
|
+
if (path14) {
|
|
3297
|
+
if (path14.includes("/ld-musl-")) {
|
|
3298
3298
|
return MUSL;
|
|
3299
|
-
} else if (
|
|
3299
|
+
} else if (path14.includes("/ld-linux-")) {
|
|
3300
3300
|
return GLIBC;
|
|
3301
3301
|
}
|
|
3302
3302
|
}
|
|
@@ -3318,7 +3318,7 @@ var require_detect_libc = __commonJS({
|
|
|
3318
3318
|
}
|
|
3319
3319
|
cachedFamilyFilesystem = null;
|
|
3320
3320
|
try {
|
|
3321
|
-
const lddContent = await
|
|
3321
|
+
const lddContent = await readFile3(LDD_PATH);
|
|
3322
3322
|
cachedFamilyFilesystem = getFamilyFromLddContent(lddContent);
|
|
3323
3323
|
} catch (e) {
|
|
3324
3324
|
}
|
|
@@ -3330,7 +3330,7 @@ var require_detect_libc = __commonJS({
|
|
|
3330
3330
|
}
|
|
3331
3331
|
cachedFamilyFilesystem = null;
|
|
3332
3332
|
try {
|
|
3333
|
-
const lddContent =
|
|
3333
|
+
const lddContent = readFileSync11(LDD_PATH);
|
|
3334
3334
|
cachedFamilyFilesystem = getFamilyFromLddContent(lddContent);
|
|
3335
3335
|
} catch (e) {
|
|
3336
3336
|
}
|
|
@@ -3342,9 +3342,9 @@ var require_detect_libc = __commonJS({
|
|
|
3342
3342
|
}
|
|
3343
3343
|
cachedFamilyInterpreter = null;
|
|
3344
3344
|
try {
|
|
3345
|
-
const selfContent = await
|
|
3346
|
-
const
|
|
3347
|
-
cachedFamilyInterpreter = familyFromInterpreterPath(
|
|
3345
|
+
const selfContent = await readFile3(SELF_PATH);
|
|
3346
|
+
const path14 = interpreterPath(selfContent);
|
|
3347
|
+
cachedFamilyInterpreter = familyFromInterpreterPath(path14);
|
|
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 =
|
|
3359
|
-
const
|
|
3360
|
-
cachedFamilyInterpreter = familyFromInterpreterPath(
|
|
3358
|
+
const selfContent = readFileSync11(SELF_PATH);
|
|
3359
|
+
const path14 = interpreterPath(selfContent);
|
|
3360
|
+
cachedFamilyInterpreter = familyFromInterpreterPath(path14);
|
|
3361
3361
|
} catch (e) {
|
|
3362
3362
|
}
|
|
3363
3363
|
return cachedFamilyInterpreter;
|
|
@@ -3404,7 +3404,7 @@ var require_detect_libc = __commonJS({
|
|
|
3404
3404
|
}
|
|
3405
3405
|
cachedVersionFilesystem = null;
|
|
3406
3406
|
try {
|
|
3407
|
-
const lddContent = await
|
|
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];
|
|
@@ -3419,7 +3419,7 @@ var require_detect_libc = __commonJS({
|
|
|
3419
3419
|
}
|
|
3420
3420
|
cachedVersionFilesystem = null;
|
|
3421
3421
|
try {
|
|
3422
|
-
const lddContent =
|
|
3422
|
+
const lddContent = readFileSync11(LDD_PATH);
|
|
3423
3423
|
const versionMatch = lddContent.match(RE_GLIBC_VERSION);
|
|
3424
3424
|
if (versionMatch) {
|
|
3425
3425
|
cachedVersionFilesystem = versionMatch[1];
|
|
@@ -5138,9 +5138,9 @@ var require_sharp = __commonJS({
|
|
|
5138
5138
|
];
|
|
5139
5139
|
var sharp;
|
|
5140
5140
|
var errors = [];
|
|
5141
|
-
for (const
|
|
5141
|
+
for (const path14 of paths) {
|
|
5142
5142
|
try {
|
|
5143
|
-
sharp = __require(
|
|
5143
|
+
sharp = __require(path14);
|
|
5144
5144
|
break;
|
|
5145
5145
|
} catch (err2) {
|
|
5146
5146
|
errors.push(err2);
|
|
@@ -6552,15 +6552,15 @@ var require_route = __commonJS({
|
|
|
6552
6552
|
};
|
|
6553
6553
|
}
|
|
6554
6554
|
function wrapConversion(toModel, graph) {
|
|
6555
|
-
const
|
|
6555
|
+
const path14 = [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
|
-
|
|
6559
|
+
path14.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 =
|
|
6563
|
+
fn.conversion = path14;
|
|
6564
6564
|
return fn;
|
|
6565
6565
|
}
|
|
6566
6566
|
module.exports = function(fromModel) {
|
|
@@ -7392,14 +7392,14 @@ var require_input = __commonJS({
|
|
|
7392
7392
|
return this;
|
|
7393
7393
|
} else {
|
|
7394
7394
|
if (this._isStreamInput()) {
|
|
7395
|
-
return new Promise((
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
7458
|
+
resolve5(stats2);
|
|
7459
7459
|
}
|
|
7460
7460
|
});
|
|
7461
7461
|
});
|
|
7462
7462
|
});
|
|
7463
7463
|
} else {
|
|
7464
|
-
return new Promise((
|
|
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
|
-
|
|
7469
|
+
resolve5(stats2);
|
|
7470
7470
|
}
|
|
7471
7471
|
});
|
|
7472
7472
|
});
|
|
@@ -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
|
|
8467
|
+
var path14 = __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) &&
|
|
8498
|
+
} else if (is.string(this.options.input.file) && path14.resolve(this.options.input.file) === path14.resolve(fileOut)) {
|
|
8499
8499
|
err2 = new Error("Cannot use same file for input and output");
|
|
8500
|
-
} else if (jp2Regex.test(
|
|
8500
|
+
} else if (jp2Regex.test(path14.extname(fileOut)) && !this.constructor.format.jp2k.output.file) {
|
|
8501
8501
|
err2 = errJp2Save();
|
|
8502
8502
|
}
|
|
8503
8503
|
if (err2) {
|
|
@@ -9242,7 +9242,7 @@ var require_output = __commonJS({
|
|
|
9242
9242
|
return this;
|
|
9243
9243
|
} else {
|
|
9244
9244
|
if (this._isStreamInput()) {
|
|
9245
|
-
return new Promise((
|
|
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
|
-
|
|
9253
|
+
resolve5({ data, info });
|
|
9254
9254
|
} else {
|
|
9255
|
-
|
|
9255
|
+
resolve5(data);
|
|
9256
9256
|
}
|
|
9257
9257
|
}
|
|
9258
9258
|
});
|
|
9259
9259
|
});
|
|
9260
9260
|
});
|
|
9261
9261
|
} else {
|
|
9262
|
-
return new Promise((
|
|
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
|
-
|
|
9268
|
+
resolve5({ data, info });
|
|
9269
9269
|
} else {
|
|
9270
|
-
|
|
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
|
|
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.
|
|
9495
|
+
return "2.0.2";
|
|
9496
9496
|
}
|
|
9497
9497
|
const require2 = createRequire(import.meta.url);
|
|
9498
9498
|
const pkg = require2("../package.json");
|
|
@@ -10125,23 +10125,23 @@ function isNoisePath(inputPath) {
|
|
|
10125
10125
|
}
|
|
10126
10126
|
}
|
|
10127
10127
|
const slashIdx = p.lastIndexOf("/");
|
|
10128
|
-
const
|
|
10129
|
-
if (NOISE_BASENAMES.has(
|
|
10128
|
+
const basename6 = slashIdx >= 0 ? p.slice(slashIdx + 1) : p;
|
|
10129
|
+
if (NOISE_BASENAMES.has(basename6)) {
|
|
10130
10130
|
return true;
|
|
10131
10131
|
}
|
|
10132
|
-
if (
|
|
10132
|
+
if (basename6.startsWith(".improve-state-") || basename6.startsWith("improve_commit_msg_")) {
|
|
10133
10133
|
return true;
|
|
10134
10134
|
}
|
|
10135
|
-
const dotIdx =
|
|
10135
|
+
const dotIdx = basename6.lastIndexOf(".");
|
|
10136
10136
|
if (dotIdx >= 0) {
|
|
10137
|
-
const ext =
|
|
10137
|
+
const ext = basename6.slice(dotIdx);
|
|
10138
10138
|
if (NOISE_EXTS.has(ext)) {
|
|
10139
10139
|
return true;
|
|
10140
10140
|
}
|
|
10141
10141
|
}
|
|
10142
10142
|
for (const ext of NOISE_EXTS) {
|
|
10143
10143
|
if (ext.includes(".") && ext.split(".").length > 2) {
|
|
10144
|
-
if (
|
|
10144
|
+
if (basename6.endsWith(ext)) {
|
|
10145
10145
|
return true;
|
|
10146
10146
|
}
|
|
10147
10147
|
}
|
|
@@ -10297,6 +10297,12 @@ function wasFileReadThisSession(filePath) {
|
|
|
10297
10297
|
function getSessionWebFetches() {
|
|
10298
10298
|
return _webFetches;
|
|
10299
10299
|
}
|
|
10300
|
+
function recordBashOutput(commandHash2, outputId, _sizeBytes) {
|
|
10301
|
+
_bashOutputs.set(commandHash2, outputId);
|
|
10302
|
+
}
|
|
10303
|
+
function getBashOutputId(commandHash2) {
|
|
10304
|
+
return _bashOutputs.get(commandHash2) ?? null;
|
|
10305
|
+
}
|
|
10300
10306
|
registerReset(() => {
|
|
10301
10307
|
_files = /* @__PURE__ */ new Map();
|
|
10302
10308
|
_hintsShown = /* @__PURE__ */ new Set();
|
|
@@ -10362,6 +10368,7 @@ var HOOK_EVENTS = [
|
|
|
10362
10368
|
// src/hooks_read.ts
|
|
10363
10369
|
init_define_import_meta_env();
|
|
10364
10370
|
import * as fs4 from "node:fs";
|
|
10371
|
+
import * as path6 from "node:path";
|
|
10365
10372
|
|
|
10366
10373
|
// src/hooks_common.ts
|
|
10367
10374
|
init_define_import_meta_env();
|
|
@@ -10378,12 +10385,495 @@ function getFilePath(event) {
|
|
|
10378
10385
|
function passOutput() {
|
|
10379
10386
|
return { hookType: "pass" };
|
|
10380
10387
|
}
|
|
10388
|
+
function denyOutput(message) {
|
|
10389
|
+
return { hookType: "deny", message };
|
|
10390
|
+
}
|
|
10381
10391
|
function contextOutput(context) {
|
|
10382
10392
|
return { hookType: "context", context };
|
|
10383
10393
|
}
|
|
10384
10394
|
|
|
10395
|
+
// src/hints.ts
|
|
10396
|
+
init_define_import_meta_env();
|
|
10397
|
+
var HINT_PRIORITY_MEDIUM = 3;
|
|
10398
|
+
var STALE_READ_AGE_SECONDS = 30 * 60;
|
|
10399
|
+
function buildPackageManifestHint(options) {
|
|
10400
|
+
try {
|
|
10401
|
+
const hasOffset = options.offset !== null && options.offset !== void 0 && options.offset >= 0;
|
|
10402
|
+
const hasLimit = options.limit !== null && options.limit !== void 0 && options.limit > 0;
|
|
10403
|
+
if (hasOffset || hasLimit) {
|
|
10404
|
+
return null;
|
|
10405
|
+
}
|
|
10406
|
+
const fname = _sanitizeHintPath(options.file_path.split(/[/\\]/).pop() ?? "");
|
|
10407
|
+
const basenameLower = fname.toLowerCase();
|
|
10408
|
+
if (basenameLower === "package.json") {
|
|
10409
|
+
const text = `\`${fname}\` is a package manifest. Consider \`token-goat section package.json::dependencies\` or \`token-goat section package.json::devDependencies\` for focused reads.`;
|
|
10410
|
+
return {
|
|
10411
|
+
text,
|
|
10412
|
+
hint_priority: HINT_PRIORITY_MEDIUM
|
|
10413
|
+
};
|
|
10414
|
+
}
|
|
10415
|
+
if (basenameLower === "package-lock.json") {
|
|
10416
|
+
const text = `\`${fname}\` is a large lockfile. Consider \`npm ls\`, \`npm outdated\`, or \`npm audit\` instead for targeted info.`;
|
|
10417
|
+
return {
|
|
10418
|
+
text,
|
|
10419
|
+
hint_priority: HINT_PRIORITY_MEDIUM
|
|
10420
|
+
};
|
|
10421
|
+
}
|
|
10422
|
+
return null;
|
|
10423
|
+
} catch {
|
|
10424
|
+
return null;
|
|
10425
|
+
}
|
|
10426
|
+
}
|
|
10427
|
+
function _sanitizeHintPath(path14) {
|
|
10428
|
+
if (typeof path14 !== "string") {
|
|
10429
|
+
return "???";
|
|
10430
|
+
}
|
|
10431
|
+
return path14.replace(/[\x00]/g, "").slice(0, 200);
|
|
10432
|
+
}
|
|
10433
|
+
|
|
10434
|
+
// src/hints/lang_patterns.ts
|
|
10435
|
+
init_define_import_meta_env();
|
|
10436
|
+
var LOCK_FILE_NAMES = /* @__PURE__ */ new Set([
|
|
10437
|
+
"package-lock.json",
|
|
10438
|
+
"yarn.lock",
|
|
10439
|
+
"pnpm-lock.yaml",
|
|
10440
|
+
"cargo.lock",
|
|
10441
|
+
"poetry.lock",
|
|
10442
|
+
"pipfile.lock",
|
|
10443
|
+
"uv.lock",
|
|
10444
|
+
"gemfile.lock",
|
|
10445
|
+
"go.sum",
|
|
10446
|
+
"composer.lock",
|
|
10447
|
+
"mix.lock",
|
|
10448
|
+
"pubspec.lock",
|
|
10449
|
+
"package.resolved"
|
|
10450
|
+
]);
|
|
10451
|
+
var MANIFEST_FILE_NAMES = /* @__PURE__ */ new Set([
|
|
10452
|
+
"package.json",
|
|
10453
|
+
"pyproject.toml",
|
|
10454
|
+
"cargo.toml",
|
|
10455
|
+
"go.mod",
|
|
10456
|
+
"go.work",
|
|
10457
|
+
"pom.xml",
|
|
10458
|
+
"build.gradle",
|
|
10459
|
+
"build.gradle.kts",
|
|
10460
|
+
"settings.gradle",
|
|
10461
|
+
"composer.json",
|
|
10462
|
+
"gemfile",
|
|
10463
|
+
"mix.exs",
|
|
10464
|
+
"pubspec.yaml",
|
|
10465
|
+
"cmakelists.txt",
|
|
10466
|
+
"makefile",
|
|
10467
|
+
"project.clj",
|
|
10468
|
+
// TypeScript / JavaScript project configs
|
|
10469
|
+
"tsconfig.json",
|
|
10470
|
+
"jsconfig.json",
|
|
10471
|
+
// Bundler / framework configs
|
|
10472
|
+
"vite.config.ts",
|
|
10473
|
+
"vite.config.js",
|
|
10474
|
+
"webpack.config.js",
|
|
10475
|
+
"webpack.config.ts",
|
|
10476
|
+
"rollup.config.js",
|
|
10477
|
+
"rollup.config.ts",
|
|
10478
|
+
"esbuild.config.js",
|
|
10479
|
+
"next.config.js",
|
|
10480
|
+
"next.config.ts",
|
|
10481
|
+
"nuxt.config.ts"
|
|
10482
|
+
]);
|
|
10483
|
+
var MANIFEST_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
10484
|
+
".cabal"
|
|
10485
|
+
]);
|
|
10486
|
+
var MANIFEST_BASENAME_PATTERNS = [
|
|
10487
|
+
/^tsconfig(\..+)?\.json$/i
|
|
10488
|
+
];
|
|
10489
|
+
var BUILD_DIR_NAMES = /* @__PURE__ */ new Set([
|
|
10490
|
+
"dist",
|
|
10491
|
+
"target",
|
|
10492
|
+
"build",
|
|
10493
|
+
"out",
|
|
10494
|
+
"__pycache__",
|
|
10495
|
+
".next",
|
|
10496
|
+
".nuxt",
|
|
10497
|
+
".output",
|
|
10498
|
+
".gradle",
|
|
10499
|
+
"_build",
|
|
10500
|
+
".build",
|
|
10501
|
+
"pkg",
|
|
10502
|
+
"obj"
|
|
10503
|
+
]);
|
|
10504
|
+
var ALWAYS_GENERATED_EXTS = /* @__PURE__ */ new Set([
|
|
10505
|
+
".pyc",
|
|
10506
|
+
".pyo",
|
|
10507
|
+
".pyd",
|
|
10508
|
+
".class",
|
|
10509
|
+
".o",
|
|
10510
|
+
".a",
|
|
10511
|
+
".so",
|
|
10512
|
+
".dylib",
|
|
10513
|
+
".dll",
|
|
10514
|
+
".tsbuildinfo"
|
|
10515
|
+
]);
|
|
10516
|
+
var CONDITIONALLY_GENERATED_EXTS = /* @__PURE__ */ new Set([
|
|
10517
|
+
".map",
|
|
10518
|
+
".d.ts"
|
|
10519
|
+
]);
|
|
10520
|
+
var BUILD_COMMAND_PATTERNS = [
|
|
10521
|
+
// Rust / Cargo
|
|
10522
|
+
/^\s*cargo\s+(build|test|run|check|clippy)\b/i,
|
|
10523
|
+
// Go
|
|
10524
|
+
/^\s*go\s+(build|test|run|vet)\b/i,
|
|
10525
|
+
// Maven
|
|
10526
|
+
/^\s*mvn\b/i,
|
|
10527
|
+
// Gradle (direct or wrapper)
|
|
10528
|
+
/^\s*(?:gradle|\.\/gradlew|gradlew)\b/i,
|
|
10529
|
+
// Python / pip
|
|
10530
|
+
/^\s*pip\s+(install|freeze)\b/i,
|
|
10531
|
+
// Poetry
|
|
10532
|
+
/^\s*poetry\s+(install|update)\b/i,
|
|
10533
|
+
// uv
|
|
10534
|
+
/^\s*uv\s+(sync|pip\s+install)\b/i,
|
|
10535
|
+
// Bundler (Ruby)
|
|
10536
|
+
/^\s*bundle\s+(install|update)\b/i,
|
|
10537
|
+
// Mix (Elixir)
|
|
10538
|
+
/^\s*mix\s+(deps\.get|compile|test)\b/i,
|
|
10539
|
+
// dotnet
|
|
10540
|
+
/^\s*dotnet\s+(build|test|restore)\b/i,
|
|
10541
|
+
// Make
|
|
10542
|
+
/^\s*make\b/i,
|
|
10543
|
+
// CMake build
|
|
10544
|
+
/^\s*cmake\s+--build\b/i,
|
|
10545
|
+
// Rake (Ruby)
|
|
10546
|
+
/^\s*rake\b/i,
|
|
10547
|
+
// TypeScript compiler
|
|
10548
|
+
/^\s*tsc\b/i,
|
|
10549
|
+
// Vite
|
|
10550
|
+
/^\s*vite\s+(build|dev|preview)\b/i,
|
|
10551
|
+
// Next.js
|
|
10552
|
+
/^\s*next\s+(build|dev|start)\b/i,
|
|
10553
|
+
// Nuxt
|
|
10554
|
+
/^\s*nuxt\s+(build|dev)\b/i,
|
|
10555
|
+
// Webpack
|
|
10556
|
+
/^\s*webpack\b/i,
|
|
10557
|
+
// esbuild
|
|
10558
|
+
/^\s*esbuild\b/i,
|
|
10559
|
+
// Rollup
|
|
10560
|
+
/^\s*rollup\b/i,
|
|
10561
|
+
// Turbo
|
|
10562
|
+
/^\s*turbo\s+(build|dev)\b/i
|
|
10563
|
+
];
|
|
10564
|
+
function isLockFile(basename6) {
|
|
10565
|
+
return LOCK_FILE_NAMES.has(basename6.toLowerCase());
|
|
10566
|
+
}
|
|
10567
|
+
function isManifestFile(basename6) {
|
|
10568
|
+
const lower = basename6.toLowerCase();
|
|
10569
|
+
if (MANIFEST_FILE_NAMES.has(lower)) return true;
|
|
10570
|
+
const dot = lower.lastIndexOf(".");
|
|
10571
|
+
if (dot !== -1 && MANIFEST_EXTENSIONS.has(lower.slice(dot))) return true;
|
|
10572
|
+
if (MANIFEST_BASENAME_PATTERNS.some((re) => re.test(basename6))) return true;
|
|
10573
|
+
return false;
|
|
10574
|
+
}
|
|
10575
|
+
function pathSegments(filePath) {
|
|
10576
|
+
return filePath.split(/[/\\]/).filter((s) => s.length > 0);
|
|
10577
|
+
}
|
|
10578
|
+
function isInBuildDir(filePath) {
|
|
10579
|
+
const segments = pathSegments(filePath);
|
|
10580
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
10581
|
+
const seg = segments[i];
|
|
10582
|
+
if (seg !== void 0 && BUILD_DIR_NAMES.has(seg.toLowerCase())) return true;
|
|
10583
|
+
}
|
|
10584
|
+
return false;
|
|
10585
|
+
}
|
|
10586
|
+
function isGeneratedFile(filePath) {
|
|
10587
|
+
const lower = filePath.toLowerCase();
|
|
10588
|
+
for (const ext of ALWAYS_GENERATED_EXTS) {
|
|
10589
|
+
if (lower.endsWith(ext)) return true;
|
|
10590
|
+
}
|
|
10591
|
+
for (const ext of CONDITIONALLY_GENERATED_EXTS) {
|
|
10592
|
+
if (lower.endsWith(ext) && isInBuildDir(filePath)) return true;
|
|
10593
|
+
}
|
|
10594
|
+
return false;
|
|
10595
|
+
}
|
|
10596
|
+
function isBuildCommand(cmd) {
|
|
10597
|
+
return BUILD_COMMAND_PATTERNS.some((re) => re.test(cmd));
|
|
10598
|
+
}
|
|
10599
|
+
var MONITORING_COMMAND_PATTERNS = [
|
|
10600
|
+
// GitHub CI
|
|
10601
|
+
{ pattern: /^gh run (?:watch|view|list)/, recallHint: '--grep "fail|error|pass|\u2713|\u2717|conclusion"' },
|
|
10602
|
+
{ pattern: /^gh run view.*--log/, recallHint: '--tail 100 --grep "Error|FAIL|error"' },
|
|
10603
|
+
{ pattern: /^gh pr checks/, recallHint: '--grep "fail|error|pass|pending"' },
|
|
10604
|
+
{ pattern: /^gh workflow (?:run|list|view)/, recallHint: '--grep "completed|failed|in_progress"' },
|
|
10605
|
+
// Dev servers (Next, Vite, Nuxt, Remix, Astro)
|
|
10606
|
+
{ pattern: /^(?:npx\s+)?next dev/, recallHint: '--tail 30 --grep "error|warn|ready|compiled"' },
|
|
10607
|
+
{ pattern: /^(?:npx\s+)?next build/, recallHint: '--grep "error|warn|Failed|\u2713"' },
|
|
10608
|
+
{ pattern: /^(?:npx\s+)?vite(?:\s+dev|\s+build|\s+preview)?$/, recallHint: '--tail 20 --grep "error|warn|ready"' },
|
|
10609
|
+
{ pattern: /^(?:npx\s+)?nuxt dev/, recallHint: '--tail 30 --grep "error|warn|ready"' },
|
|
10610
|
+
{ pattern: /^(?:npx\s+)?remix dev/, recallHint: '--tail 20 --grep "error|warn|ready"' },
|
|
10611
|
+
{ pattern: /^(?:npx\s+)?astro dev/, recallHint: '--tail 20 --grep "error|warn|ready"' },
|
|
10612
|
+
// Test watchers
|
|
10613
|
+
{ pattern: /^(?:npx\s+)?vitest(?:\s+run|\s+watch)?/, recallHint: '--grep "FAIL|PASS|Error|\u2713|\u2717"' },
|
|
10614
|
+
{ pattern: /^(?:npx\s+)?jest(?:\s+--watch)?/, recallHint: '--grep "FAIL|PASS|Error|Tests:"' },
|
|
10615
|
+
{ pattern: /^pytest(?:\s|$)/, recallHint: '--grep "FAILED|PASSED|ERROR|passed|failed"' },
|
|
10616
|
+
{ pattern: /^(?:cargo\s+test|cargo\s+watch)/, recallHint: '--grep "FAILED|ok|error\\["' },
|
|
10617
|
+
{ pattern: /^go test/, recallHint: '--grep "FAIL|ok|---"' },
|
|
10618
|
+
// Docker / compose
|
|
10619
|
+
{ pattern: /^docker(?:\s+compose)?\s+logs/, recallHint: '--tail 50 --grep "error|warn|Error|WARN"' },
|
|
10620
|
+
{ pattern: /^docker-compose\s+logs/, recallHint: '--tail 50 --grep "error|warn|Error|WARN"' },
|
|
10621
|
+
// File watchers / hot-reload
|
|
10622
|
+
{ pattern: /^nodemon/, recallHint: '--tail 20 --grep "error|crash|restart"' },
|
|
10623
|
+
{ pattern: /^air(?:\s|$)/, recallHint: '--tail 20 --grep "error|build failed"' },
|
|
10624
|
+
{ pattern: /^cargo watch/, recallHint: '--tail 20 --grep "error\\[|warning\\["' },
|
|
10625
|
+
{ pattern: /^watchexec/, recallHint: '--tail 20 --grep "error|warn"' },
|
|
10626
|
+
// Linters / formatters run repeatedly
|
|
10627
|
+
{ pattern: /^(?:npx\s+)?eslint(?:\s|$)/, recallHint: '--grep "error|warning|\u2716|problems"' },
|
|
10628
|
+
{ pattern: /^(?:npx\s+)?prettier(?:\s|$)/, recallHint: '--grep "unchanged|reformatted|error"' },
|
|
10629
|
+
{ pattern: /^ruff(?:\s|$)/, recallHint: '--grep "error|warning|Found"' },
|
|
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" }
|
|
10643
|
+
];
|
|
10644
|
+
function getMonitoringRecallHint(cmd) {
|
|
10645
|
+
for (const { pattern, recallHint } of MONITORING_COMMAND_PATTERNS) {
|
|
10646
|
+
if (pattern.test(cmd.trim())) return recallHint;
|
|
10647
|
+
}
|
|
10648
|
+
return null;
|
|
10649
|
+
}
|
|
10650
|
+
|
|
10651
|
+
// src/hints/markdown_hints.ts
|
|
10652
|
+
init_define_import_meta_env();
|
|
10653
|
+
var MARKDOWN_SIZE_THRESHOLD = 8e3;
|
|
10654
|
+
var MAX_HEADINGS = 40;
|
|
10655
|
+
var MAX_OUTPUT_LINES = 60;
|
|
10656
|
+
function extractMarkdownHeadings(content) {
|
|
10657
|
+
const headings = [];
|
|
10658
|
+
const lines = content.split("\n");
|
|
10659
|
+
for (let i = 0; i < lines.length; i++) {
|
|
10660
|
+
const line = lines[i];
|
|
10661
|
+
if (!line) continue;
|
|
10662
|
+
const match = /^(#+)\s+(.+?)\s*$/.exec(line);
|
|
10663
|
+
if (!match || match.length < 3) continue;
|
|
10664
|
+
const hashes = match[1];
|
|
10665
|
+
const headingText = match[2];
|
|
10666
|
+
const level = hashes.length;
|
|
10667
|
+
if (level > 3) continue;
|
|
10668
|
+
const text = headingText.trim();
|
|
10669
|
+
if (!text) continue;
|
|
10670
|
+
headings.push({
|
|
10671
|
+
level,
|
|
10672
|
+
text,
|
|
10673
|
+
lineNumber: i + 1
|
|
10674
|
+
});
|
|
10675
|
+
if (headings.length >= MAX_HEADINGS) break;
|
|
10676
|
+
}
|
|
10677
|
+
return headings;
|
|
10678
|
+
}
|
|
10679
|
+
function formatHeadingTree(headings, filePath) {
|
|
10680
|
+
if (headings.length === 0) return "";
|
|
10681
|
+
const seenTexts = /* @__PURE__ */ new Map();
|
|
10682
|
+
const dedupedHeadings = [];
|
|
10683
|
+
for (const h of headings) {
|
|
10684
|
+
const count = (seenTexts.get(h.text) ?? 0) + 1;
|
|
10685
|
+
seenTexts.set(h.text, count);
|
|
10686
|
+
const suffix = count > 1 ? ` #${count}` : "";
|
|
10687
|
+
dedupedHeadings.push({
|
|
10688
|
+
text: h.text + suffix,
|
|
10689
|
+
level: h.level
|
|
10690
|
+
});
|
|
10691
|
+
}
|
|
10692
|
+
const lines = [];
|
|
10693
|
+
lines.push(`Large markdown file (${headings.length} headings). Use token-goat section to read a specific section:`);
|
|
10694
|
+
lines.push(` token-goat section "${filePath}::Heading Name"`);
|
|
10695
|
+
lines.push(``);
|
|
10696
|
+
lines.push(`Sections:`);
|
|
10697
|
+
let headingsAdded = 0;
|
|
10698
|
+
for (const h of dedupedHeadings) {
|
|
10699
|
+
if (lines.length + 1 >= MAX_OUTPUT_LINES) {
|
|
10700
|
+
const remaining = dedupedHeadings.length - headingsAdded;
|
|
10701
|
+
lines.push(` ... (${remaining} more headings)`);
|
|
10702
|
+
break;
|
|
10703
|
+
}
|
|
10704
|
+
const indent = h.level === 1 ? "" : h.level === 2 ? " " : " ";
|
|
10705
|
+
const marker = "#".repeat(h.level);
|
|
10706
|
+
lines.push(` ${indent}${marker} ${h.text}`);
|
|
10707
|
+
headingsAdded++;
|
|
10708
|
+
}
|
|
10709
|
+
return lines.join("\n");
|
|
10710
|
+
}
|
|
10711
|
+
var WELL_KNOWN_SECTIONS = {
|
|
10712
|
+
"CHANGELOG.md": ["Unreleased"],
|
|
10713
|
+
"README.md": ["Install", "Usage", "API", "Configuration", "Getting Started"],
|
|
10714
|
+
"CONTRIBUTING.md": ["Setup", "Commands", "Testing", "Development"],
|
|
10715
|
+
"CLAUDE.md": ["Commands", "Architecture"],
|
|
10716
|
+
"CLAUDE.arch.md": ["Component Map", "Architecture"]
|
|
10717
|
+
};
|
|
10718
|
+
function getWellKnownSections(basename6) {
|
|
10719
|
+
return WELL_KNOWN_SECTIONS[basename6] ?? [];
|
|
10720
|
+
}
|
|
10721
|
+
function extractChangelogVersionHint(content, filePath) {
|
|
10722
|
+
const lines = content.split("\n");
|
|
10723
|
+
let foundUnreleased = false;
|
|
10724
|
+
for (const line of lines) {
|
|
10725
|
+
const m = /^##\s+(\[?[\d]+\.[\d]+\.[\d]+\]?)/.exec(line);
|
|
10726
|
+
if (m) {
|
|
10727
|
+
if (foundUnreleased || !line.toLowerCase().includes("unreleased")) {
|
|
10728
|
+
const ver = m[1];
|
|
10729
|
+
return ` | token-goat section "${filePath}::${ver}"`;
|
|
10730
|
+
}
|
|
10731
|
+
}
|
|
10732
|
+
if (/^##\s+\[?unreleased\]?/i.test(line)) {
|
|
10733
|
+
foundUnreleased = true;
|
|
10734
|
+
}
|
|
10735
|
+
}
|
|
10736
|
+
return "";
|
|
10737
|
+
}
|
|
10738
|
+
|
|
10739
|
+
// src/hints/file_type_handler.ts
|
|
10740
|
+
init_define_import_meta_env();
|
|
10741
|
+
var FILE_TYPE_THRESHOLDS = {
|
|
10742
|
+
pdf: 0,
|
|
10743
|
+
// always intercept (any size)
|
|
10744
|
+
html: 5e4,
|
|
10745
|
+
txt: 2e4,
|
|
10746
|
+
csv: 1e4,
|
|
10747
|
+
tsv: 1e4,
|
|
10748
|
+
office: 0,
|
|
10749
|
+
// always block (.docx etc)
|
|
10750
|
+
generic: 1e5
|
|
10751
|
+
// catch-all for unrecognized large files
|
|
10752
|
+
};
|
|
10753
|
+
function formatBytes(n) {
|
|
10754
|
+
if (n < 1024) return `${n} B`;
|
|
10755
|
+
if (n < 1048576) return `${(n / 1024).toFixed(1)} KB`;
|
|
10756
|
+
return `${(n / 1048576).toFixed(1)} MB`;
|
|
10757
|
+
}
|
|
10758
|
+
function handlePdf(filePath, contentLength) {
|
|
10759
|
+
return {
|
|
10760
|
+
shouldBlock: true,
|
|
10761
|
+
message: [
|
|
10762
|
+
`PDF file (${formatBytes(contentLength)}).`,
|
|
10763
|
+
`Use the \`pages\` parameter to scope the read: Read({ file_path: "${filePath}", pages: "1-5" })`,
|
|
10764
|
+
`For the full page count, run: pdfinfo "${filePath}" | grep Pages`
|
|
10765
|
+
].join("\n")
|
|
10766
|
+
};
|
|
10767
|
+
}
|
|
10768
|
+
function handleHtml(filePath, content) {
|
|
10769
|
+
if (content.length < FILE_TYPE_THRESHOLDS.html) return { shouldBlock: false, message: "" };
|
|
10770
|
+
const title = content.match(/<title[^>]*>([^<]+)<\/title>/i)?.[1]?.trim();
|
|
10771
|
+
const headings = [...content.matchAll(/<h([1-6])[^>]*>([^<]+)<\/h\1>/gi)].slice(0, 20).map((m) => {
|
|
10772
|
+
const level = m[1];
|
|
10773
|
+
const text = m[2];
|
|
10774
|
+
if (!level || !text) return "";
|
|
10775
|
+
return `${" ".repeat(Number(level) - 1)}h${level}: ${text.trim()}`;
|
|
10776
|
+
}).filter(Boolean);
|
|
10777
|
+
const isMinified = !content.includes("\n") || content.length > 5e4 && content.split("\n").length < 10;
|
|
10778
|
+
if (isMinified) {
|
|
10779
|
+
return {
|
|
10780
|
+
shouldBlock: true,
|
|
10781
|
+
message: `HTML file appears minified (${formatBytes(content.length)}). Consider fetching the source or converting with: pandoc "${filePath}" -t plain`
|
|
10782
|
+
};
|
|
10783
|
+
}
|
|
10784
|
+
return {
|
|
10785
|
+
shouldBlock: true,
|
|
10786
|
+
message: [
|
|
10787
|
+
`Large HTML file (${formatBytes(content.length)})${title ? `: "${title}"` : ""}.`,
|
|
10788
|
+
headings.length > 0 ? `Headings:
|
|
10789
|
+
${headings.join("\n")}` : "",
|
|
10790
|
+
`Use token-goat section to extract a section by heading, or convert to text: pandoc "${filePath}" -t plain`
|
|
10791
|
+
].filter(Boolean).join("\n")
|
|
10792
|
+
};
|
|
10793
|
+
}
|
|
10794
|
+
function handleTxt(filePath, content) {
|
|
10795
|
+
if (content.length < FILE_TYPE_THRESHOLDS.txt) return { shouldBlock: false, message: "" };
|
|
10796
|
+
const lines = content.split("\n");
|
|
10797
|
+
const isLog = /\.(log|out|err|trace)$/i.test(filePath) || filePath.includes("/logs/");
|
|
10798
|
+
const preview = [
|
|
10799
|
+
"--- first 5 lines ---",
|
|
10800
|
+
...lines.slice(0, 5),
|
|
10801
|
+
`... (${lines.length.toLocaleString()} lines total) ...`,
|
|
10802
|
+
"--- last 5 lines ---",
|
|
10803
|
+
...lines.slice(-5)
|
|
10804
|
+
].join("\n");
|
|
10805
|
+
const recall = isLog ? 'Log file \u2014 use Read with offset/limit params, or: token-goat bash-output <id> --tail 100 --grep "error|ERROR"' : "Use Read with offset and limit params to sample specific line ranges.";
|
|
10806
|
+
return {
|
|
10807
|
+
shouldBlock: true,
|
|
10808
|
+
message: `Large text file (${formatBytes(content.length)}, ${lines.length.toLocaleString()} lines).
|
|
10809
|
+
${preview}
|
|
10810
|
+
|
|
10811
|
+
${recall}`
|
|
10812
|
+
};
|
|
10813
|
+
}
|
|
10814
|
+
function handleOfficeBinary(filePath) {
|
|
10815
|
+
const ext = filePath.split(".").pop()?.toLowerCase();
|
|
10816
|
+
return {
|
|
10817
|
+
shouldBlock: true,
|
|
10818
|
+
message: [
|
|
10819
|
+
`Binary Office file (.${ext}) \u2014 cannot be read as text.`,
|
|
10820
|
+
`Extract content first: pandoc "${filePath}" -t plain > "${filePath}.txt"`,
|
|
10821
|
+
`Then read the extracted .txt file.`
|
|
10822
|
+
].join("\n")
|
|
10823
|
+
};
|
|
10824
|
+
}
|
|
10825
|
+
function handleCsv(filePath, content) {
|
|
10826
|
+
if (content.length < FILE_TYPE_THRESHOLDS.csv) return { shouldBlock: false, message: "" };
|
|
10827
|
+
const lines = content.split("\n").filter((l) => l.trim());
|
|
10828
|
+
const headers = lines[0] ?? "";
|
|
10829
|
+
const sampleRows = lines.slice(1, 4);
|
|
10830
|
+
const sep = filePath.endsWith(".tsv") ? " " : ",";
|
|
10831
|
+
const colCount = headers.split(sep).length;
|
|
10832
|
+
return {
|
|
10833
|
+
shouldBlock: true,
|
|
10834
|
+
message: [
|
|
10835
|
+
`Large CSV file (${formatBytes(content.length)}, ~${lines.length.toLocaleString()} rows, ${colCount} columns).`,
|
|
10836
|
+
`Columns: ${headers}`,
|
|
10837
|
+
`Sample rows:
|
|
10838
|
+
${sampleRows.join("\n")}`,
|
|
10839
|
+
`Use Read with offset/limit to sample rows, or query with DuckDB: duckdb -c "SELECT * FROM '${filePath}' LIMIT 10"`
|
|
10840
|
+
].join("\n")
|
|
10841
|
+
};
|
|
10842
|
+
}
|
|
10843
|
+
function handleGenericLarge(filePath, contentLength) {
|
|
10844
|
+
if (contentLength < FILE_TYPE_THRESHOLDS.generic) return { shouldBlock: false, message: "" };
|
|
10845
|
+
return {
|
|
10846
|
+
shouldBlock: true,
|
|
10847
|
+
message: `Large file (${formatBytes(contentLength)}). Use Read with offset and limit parameters to read specific line ranges rather than loading the entire file.`
|
|
10848
|
+
};
|
|
10849
|
+
}
|
|
10850
|
+
function dispatchFileTypeHandler(filePath, content, contentLengthHint) {
|
|
10851
|
+
const ext = filePath.split(".").pop()?.toLowerCase() ?? "";
|
|
10852
|
+
if (["md", "mdx", "markdown", "rst"].includes(ext)) return null;
|
|
10853
|
+
const effectiveLength = contentLengthHint ?? content.length;
|
|
10854
|
+
if (ext === "pdf") return handlePdf(filePath, effectiveLength);
|
|
10855
|
+
if (["html", "htm", "xhtml"].includes(ext)) return handleHtml(filePath, content);
|
|
10856
|
+
if (["txt", "log", "out", "err", "trace"].includes(ext)) return handleTxt(filePath, content);
|
|
10857
|
+
if (["docx", "xlsx", "pptx", "odt", "ods", "ott", "odp"].includes(ext)) return handleOfficeBinary(filePath);
|
|
10858
|
+
if (ext === "csv" || ext === "tsv") return handleCsv(filePath, content);
|
|
10859
|
+
return handleGenericLarge(filePath, effectiveLength);
|
|
10860
|
+
}
|
|
10861
|
+
|
|
10385
10862
|
// src/hooks_read.ts
|
|
10863
|
+
function isTsConfigFile(basename6) {
|
|
10864
|
+
const lower = basename6.toLowerCase();
|
|
10865
|
+
return /^tsconfig(\..+)?\.json$/i.test(lower) || lower === "jsconfig.json";
|
|
10866
|
+
}
|
|
10386
10867
|
var LARGE_FILE_BYTES = 100 * 1024;
|
|
10868
|
+
function isNodeModulesPath(path14) {
|
|
10869
|
+
const isWindows = process.platform === "win32";
|
|
10870
|
+
const check = isWindows ? path14.toLowerCase() : path14;
|
|
10871
|
+
return check.includes("/node_modules/") || check.includes("\\node_modules\\");
|
|
10872
|
+
}
|
|
10873
|
+
function _isDocFile(filePath) {
|
|
10874
|
+
const lower = filePath.toLowerCase();
|
|
10875
|
+
return lower.endsWith(".md") || lower.endsWith(".mdx") || lower.endsWith(".markdown") || lower.endsWith(".rst");
|
|
10876
|
+
}
|
|
10387
10877
|
function statSize(absPath) {
|
|
10388
10878
|
try {
|
|
10389
10879
|
return fs4.statSync(absPath).size;
|
|
@@ -10395,41 +10885,124 @@ function preReadHandler(event) {
|
|
|
10395
10885
|
const filePath = getFilePath(event);
|
|
10396
10886
|
if (filePath === void 0) return passOutput();
|
|
10397
10887
|
const normalized = normalizePath(filePath);
|
|
10888
|
+
if (isNodeModulesPath(normalized)) {
|
|
10889
|
+
return denyOutput(
|
|
10890
|
+
"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"
|
|
10891
|
+
);
|
|
10892
|
+
}
|
|
10893
|
+
const basename6 = path6.basename(normalized);
|
|
10894
|
+
if (isLockFile(basename6)) {
|
|
10895
|
+
return denyOutput(
|
|
10896
|
+
'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.'
|
|
10897
|
+
);
|
|
10898
|
+
}
|
|
10899
|
+
if (normalized.toLowerCase().endsWith(".tsbuildinfo")) {
|
|
10900
|
+
return denyOutput(
|
|
10901
|
+
"This is a TypeScript incremental build cache file. You don't need to read it directly."
|
|
10902
|
+
);
|
|
10903
|
+
}
|
|
10904
|
+
if (isInBuildDir(normalized) || isGeneratedFile(normalized)) {
|
|
10905
|
+
return denyOutput(
|
|
10906
|
+
"Generated/build artifact \u2014 read the source file instead."
|
|
10907
|
+
);
|
|
10908
|
+
}
|
|
10909
|
+
const manifestHint = buildPackageManifestHint({ file_path: normalized });
|
|
10910
|
+
if (manifestHint) {
|
|
10911
|
+
recordFileRead(normalized);
|
|
10912
|
+
return contextOutput(manifestHint.text);
|
|
10913
|
+
}
|
|
10914
|
+
if (isTsConfigFile(basename6) && wasFileReadThisSession(normalized)) {
|
|
10915
|
+
recordFileRead(normalized);
|
|
10916
|
+
return contextOutput(
|
|
10917
|
+
"Already read " + basename6 + '. Use `token-goat section "' + normalized + '::compilerOptions"` to extract compiler options, or `token-goat config-get ' + normalized + " compilerOptions.target` for a single value."
|
|
10918
|
+
);
|
|
10919
|
+
}
|
|
10920
|
+
if (isManifestFile(basename6) && wasFileReadThisSession(normalized)) {
|
|
10921
|
+
recordFileRead(normalized);
|
|
10922
|
+
return contextOutput(
|
|
10923
|
+
"You've already read " + basename6 + '. Use `token-goat section "' + normalized + '::<field>"` or `token-goat config-get ' + normalized + " <key>` to extract just the value you need."
|
|
10924
|
+
);
|
|
10925
|
+
}
|
|
10926
|
+
const isMarkdown = /\.(md|mdx|markdown|rst)$/i.test(basename6);
|
|
10927
|
+
if (isMarkdown) {
|
|
10928
|
+
let fileContent = null;
|
|
10929
|
+
try {
|
|
10930
|
+
const sz = statSize(normalized);
|
|
10931
|
+
if (sz !== null && sz >= MARKDOWN_SIZE_THRESHOLD) {
|
|
10932
|
+
fileContent = fs4.readFileSync(normalized, "utf8");
|
|
10933
|
+
}
|
|
10934
|
+
} catch {
|
|
10935
|
+
}
|
|
10936
|
+
if (fileContent !== null) {
|
|
10937
|
+
const headings = extractMarkdownHeadings(fileContent);
|
|
10938
|
+
if (headings.length >= 3) {
|
|
10939
|
+
recordFileRead(normalized);
|
|
10940
|
+
const hintText = formatHeadingTree(headings, normalized);
|
|
10941
|
+
const wellKnown = getWellKnownSections(basename6);
|
|
10942
|
+
const wellKnownText = wellKnown.length > 0 ? "\nQuick access: " + wellKnown.map((s) => 'token-goat section "' + normalized + "::" + s + '"').join(" | ") : "";
|
|
10943
|
+
const changelogExtra = basename6.toLowerCase() === "changelog.md" ? extractChangelogVersionHint(fileContent, normalized) : "";
|
|
10944
|
+
return denyOutput(hintText + wellKnownText + changelogExtra);
|
|
10945
|
+
}
|
|
10946
|
+
}
|
|
10947
|
+
}
|
|
10398
10948
|
if (wasFileReadThisSession(normalized)) {
|
|
10399
10949
|
const entry = getSessionFiles().get(normalized);
|
|
10400
10950
|
const reads = entry?.readCount ?? 1;
|
|
10401
10951
|
const plural = reads === 1 ? "read" : "reads";
|
|
10402
10952
|
recordFileRead(normalized);
|
|
10953
|
+
const hint = _isDocFile(normalized) ? 'Use `token-goat section "' + normalized + '::SectionName"` to read one section.' : "Use token-goat read/section/symbol to re-read surgically.";
|
|
10403
10954
|
return contextOutput(
|
|
10404
|
-
|
|
10955
|
+
"Note: " + normalized + " was already read this session (" + reads + " " + plural + "). " + hint
|
|
10405
10956
|
);
|
|
10406
10957
|
}
|
|
10407
10958
|
const size = statSize(normalized);
|
|
10408
10959
|
if (size !== null && size >= LARGE_FILE_BYTES) {
|
|
10409
10960
|
const kb = Math.round(size / 1024);
|
|
10410
10961
|
recordFileRead(normalized);
|
|
10962
|
+
const hint = _isDocFile(normalized) ? 'Use `token-goat section "' + normalized + '::SectionName"` to read one section.' : "Consider token-goat skeleton or token-goat section.";
|
|
10411
10963
|
return contextOutput(
|
|
10412
|
-
|
|
10964
|
+
"Note: " + normalized + " is large (" + kb + "kb). " + hint
|
|
10413
10965
|
);
|
|
10414
10966
|
}
|
|
10967
|
+
const fileTypeExt = path6.extname(normalized).slice(1).toLowerCase();
|
|
10968
|
+
const binaryExts = /* @__PURE__ */ new Set(["pdf", "docx", "xlsx", "pptx", "odt", "ods", "ott", "odp"]);
|
|
10969
|
+
const textTypeExts = /* @__PURE__ */ new Set(["html", "htm", "xhtml", "txt", "log", "out", "err", "trace", "csv", "tsv"]);
|
|
10970
|
+
const fileStatSize = size ?? statSize(normalized) ?? 0;
|
|
10971
|
+
const isKnownFileType = binaryExts.has(fileTypeExt) || textTypeExts.has(fileTypeExt);
|
|
10972
|
+
if (isKnownFileType || fileStatSize >= FILE_TYPE_THRESHOLDS.generic) {
|
|
10973
|
+
let ftContent = "";
|
|
10974
|
+
if (!binaryExts.has(fileTypeExt)) {
|
|
10975
|
+
try {
|
|
10976
|
+
ftContent = fs4.readFileSync(normalized, "utf8");
|
|
10977
|
+
} catch {
|
|
10978
|
+
}
|
|
10979
|
+
}
|
|
10980
|
+
const ftResult = dispatchFileTypeHandler(normalized, ftContent, fileStatSize);
|
|
10981
|
+
if (ftResult?.shouldBlock) {
|
|
10982
|
+
recordFileRead(normalized);
|
|
10983
|
+
return denyOutput(ftResult.message);
|
|
10984
|
+
}
|
|
10985
|
+
}
|
|
10415
10986
|
recordFileRead(normalized);
|
|
10416
10987
|
return passOutput();
|
|
10417
10988
|
}
|
|
10418
10989
|
registerHook("pre_tool_use", preReadHandler, { toolName: "Read" });
|
|
10990
|
+
registerHook("pre_tool_use", preReadHandler, { toolName: "Grep" });
|
|
10419
10991
|
|
|
10420
10992
|
// src/hooks_edit.ts
|
|
10421
10993
|
init_define_import_meta_env();
|
|
10994
|
+
import * as path8 from "node:path";
|
|
10422
10995
|
|
|
10423
10996
|
// src/hooks_index.ts
|
|
10424
10997
|
init_define_import_meta_env();
|
|
10425
10998
|
import * as fs5 from "node:fs";
|
|
10426
|
-
import * as
|
|
10999
|
+
import * as path7 from "node:path";
|
|
10427
11000
|
function dirtyQueuePath() {
|
|
10428
|
-
return
|
|
11001
|
+
return path7.join(dataDir(), "queue", "dirty.txt");
|
|
10429
11002
|
}
|
|
10430
11003
|
function appendDirtyPath(normalizedPath) {
|
|
10431
11004
|
const queuePath = dirtyQueuePath();
|
|
10432
|
-
fs5.mkdirSync(
|
|
11005
|
+
fs5.mkdirSync(path7.dirname(queuePath), { recursive: true });
|
|
10433
11006
|
fs5.appendFileSync(queuePath, `${normalizedPath}
|
|
10434
11007
|
`);
|
|
10435
11008
|
}
|
|
@@ -10460,9 +11033,9 @@ function clearDirtyQueue() {
|
|
|
10460
11033
|
function preCompactIndexHandler(_event) {
|
|
10461
11034
|
const paths = getDirtyPaths();
|
|
10462
11035
|
if (paths.length > 0) {
|
|
10463
|
-
const sidecar =
|
|
11036
|
+
const sidecar = path7.join(dataDir(), "queue", "pending.txt");
|
|
10464
11037
|
try {
|
|
10465
|
-
fs5.mkdirSync(
|
|
11038
|
+
fs5.mkdirSync(path7.dirname(sidecar), { recursive: true });
|
|
10466
11039
|
atomicWriteBytes(sidecar, Buffer.from(`${paths.join("\n")}
|
|
10467
11040
|
`, "utf8"));
|
|
10468
11041
|
} catch {
|
|
@@ -10480,6 +11053,12 @@ function postEditHandler(event) {
|
|
|
10480
11053
|
const normalized = normalizePath(filePath);
|
|
10481
11054
|
recordFileEdit(normalized);
|
|
10482
11055
|
appendDirtyPath(normalized);
|
|
11056
|
+
const editedBasename = path8.basename(normalized);
|
|
11057
|
+
if (/\.(md|mdx|markdown|rst)$/i.test(editedBasename)) {
|
|
11058
|
+
return contextOutput(
|
|
11059
|
+
editedBasename + ' was edited. Use `token-goat section "' + normalized + '::HeadingName"` to re-read a specific section rather than the full file.'
|
|
11060
|
+
);
|
|
11061
|
+
}
|
|
10483
11062
|
return passOutput();
|
|
10484
11063
|
}
|
|
10485
11064
|
registerHook("post_tool_use", postEditHandler, { toolName: "Write" });
|
|
@@ -10748,10 +11327,263 @@ function postSkillHandler(event) {
|
|
|
10748
11327
|
registerHook("pre_tool_use", preSkillHandler, { toolName: "Skill" });
|
|
10749
11328
|
registerHook("post_tool_use", postSkillHandler, { toolName: "Skill" });
|
|
10750
11329
|
|
|
10751
|
-
// src/
|
|
11330
|
+
// src/hooks_bash.ts
|
|
10752
11331
|
init_define_import_meta_env();
|
|
11332
|
+
|
|
11333
|
+
// src/fingerprint.ts
|
|
11334
|
+
init_define_import_meta_env();
|
|
11335
|
+
import { createHash } from "node:crypto";
|
|
10753
11336
|
import * as fs6 from "node:fs";
|
|
10754
|
-
|
|
11337
|
+
function fingerprintContent(content) {
|
|
11338
|
+
const hash = createHash("sha256");
|
|
11339
|
+
hash.update(typeof content === "string" ? Buffer.from(content, "utf-8") : content);
|
|
11340
|
+
return hash.digest("hex");
|
|
11341
|
+
}
|
|
11342
|
+
function fingerprintFile(filePath) {
|
|
11343
|
+
let data;
|
|
11344
|
+
try {
|
|
11345
|
+
data = fs6.readFileSync(filePath);
|
|
11346
|
+
} catch {
|
|
11347
|
+
return null;
|
|
11348
|
+
}
|
|
11349
|
+
return fingerprintContent(data);
|
|
11350
|
+
}
|
|
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
|
+
|
|
11501
|
+
// src/hooks_bash.ts
|
|
11502
|
+
function extractCommand(event) {
|
|
11503
|
+
const cmd = event.toolInput["command"];
|
|
11504
|
+
return typeof cmd === "string" && cmd.trim() !== "" ? cmd.trim() : void 0;
|
|
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
|
+
}
|
|
11510
|
+
function isTscCommand(cmd) {
|
|
11511
|
+
return /^\s*tsc(\s|$)/i.test(cmd);
|
|
11512
|
+
}
|
|
11513
|
+
function isDevServerCommand(cmd) {
|
|
11514
|
+
return /^\s*(vite\s+dev|next\s+dev|nuxt\s+dev)\b/i.test(cmd);
|
|
11515
|
+
}
|
|
11516
|
+
function buildRecallHint(cmd, outputId) {
|
|
11517
|
+
const cmdPreview = cmd.length > 60 ? cmd.slice(0, 57) + "..." : cmd;
|
|
11518
|
+
if (isTscCommand(cmd)) {
|
|
11519
|
+
return "Output from a prior `" + cmdPreview + "` run is cached. Use `token-goat bash-output " + outputId + ' --grep "error TS"` to filter TypeScript errors, or `--grep "Cannot find"` for missing module errors.';
|
|
11520
|
+
}
|
|
11521
|
+
if (isDevServerCommand(cmd)) {
|
|
11522
|
+
return "Dev server output cached (`" + cmdPreview + "`). Use `token-goat bash-output " + outputId + ' --tail 20` to see the latest output, or `--grep "error\\|warn"` to filter issues.';
|
|
11523
|
+
}
|
|
11524
|
+
return "Output from a prior `" + cmdPreview + "` run is cached. Use `token-goat bash-output " + outputId + "` (or `--tail 50`, `--grep ERROR`) to re-inspect it without re-running.";
|
|
11525
|
+
}
|
|
11526
|
+
function preBashHandler(event) {
|
|
11527
|
+
const cmd = extractCommand(event);
|
|
11528
|
+
if (cmd === void 0) return passOutput();
|
|
11529
|
+
const monitoringHint = getMonitoringRecallHint(cmd);
|
|
11530
|
+
if (monitoringHint !== null) {
|
|
11531
|
+
const monCmdHash = fingerprintContent(cmd).slice(0, 16);
|
|
11532
|
+
const monOutputId = getBashOutputId(monCmdHash);
|
|
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
|
+
}
|
|
11540
|
+
const cmdSummary = cmd.length > 60 ? cmd.slice(0, 57) + "..." : cmd;
|
|
11541
|
+
return contextOutput(
|
|
11542
|
+
"Prior output from `" + cmdSummary + "` is cached.\nUse `token-goat bash-output " + monOutputId + " " + monitoringHint + "` to re-inspect without re-running."
|
|
11543
|
+
);
|
|
11544
|
+
}
|
|
11545
|
+
}
|
|
11546
|
+
if (!isBuildCommand(cmd)) return passOutput();
|
|
11547
|
+
const cmdHash = fingerprintContent(cmd).slice(0, 16);
|
|
11548
|
+
const outputId = getBashOutputId(cmdHash);
|
|
11549
|
+
if (outputId === null) return passOutput();
|
|
11550
|
+
return contextOutput(buildRecallHint(cmd, outputId));
|
|
11551
|
+
}
|
|
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" });
|
|
11582
|
+
|
|
11583
|
+
// src/image_shrink.ts
|
|
11584
|
+
init_define_import_meta_env();
|
|
11585
|
+
import * as fs8 from "node:fs";
|
|
11586
|
+
import * as path9 from "node:path";
|
|
10755
11587
|
var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
10756
11588
|
".png",
|
|
10757
11589
|
".jpg",
|
|
@@ -10778,7 +11610,7 @@ async function loadSharp() {
|
|
|
10778
11610
|
return _sharpCache;
|
|
10779
11611
|
}
|
|
10780
11612
|
function isImagePath(p) {
|
|
10781
|
-
return IMAGE_EXTENSIONS.has(
|
|
11613
|
+
return IMAGE_EXTENSIONS.has(path9.extname(p).toLowerCase());
|
|
10782
11614
|
}
|
|
10783
11615
|
async function shrinkImage(input, opts) {
|
|
10784
11616
|
const maxDimension = opts?.maxDimension ?? DEFAULT_MAX_DIMENSION;
|
|
@@ -10816,7 +11648,7 @@ async function shrinkImage(input, opts) {
|
|
|
10816
11648
|
}
|
|
10817
11649
|
function statSize2(absPath) {
|
|
10818
11650
|
try {
|
|
10819
|
-
const st =
|
|
11651
|
+
const st = fs8.statSync(absPath);
|
|
10820
11652
|
return st.isFile() ? st.size : null;
|
|
10821
11653
|
} catch {
|
|
10822
11654
|
return null;
|
|
@@ -10830,7 +11662,7 @@ async function preReadImageHandler(event) {
|
|
|
10830
11662
|
if (size === null || size < DEFAULT_SIZE_THRESHOLD_BYTES) return passOutput();
|
|
10831
11663
|
let input;
|
|
10832
11664
|
try {
|
|
10833
|
-
input =
|
|
11665
|
+
input = fs8.readFileSync(filePath);
|
|
10834
11666
|
} catch {
|
|
10835
11667
|
return passOutput();
|
|
10836
11668
|
}
|
|
@@ -10839,7 +11671,7 @@ async function preReadImageHandler(event) {
|
|
|
10839
11671
|
const saved = result.originalBytes - result.shrunkBytes;
|
|
10840
11672
|
const pct = Math.round(saved / result.originalBytes * 100);
|
|
10841
11673
|
const dataUrl = `data:image/${result.format};base64,${result.data.toString("base64")}`;
|
|
10842
|
-
const summary = `token-goat shrank ${
|
|
11674
|
+
const summary = `token-goat shrank ${path9.basename(filePath)}: ${Math.round(result.originalBytes / 1024)}kb -> ${Math.round(result.shrunkBytes / 1024)}kb (${pct}% smaller, ${result.width}x${result.height} ${result.format}).`;
|
|
10843
11675
|
return contextOutput(`${summary}
|
|
10844
11676
|
${dataUrl}`);
|
|
10845
11677
|
}
|
|
@@ -10848,7 +11680,7 @@ registerHook("pre_tool_use", preReadImageHandler, { toolName: "Read" });
|
|
|
10848
11680
|
// src/relay.ts
|
|
10849
11681
|
var DEFAULT_STDIN_TIMEOUT_MS = 5e3;
|
|
10850
11682
|
function readStdinJson(timeoutMs = DEFAULT_STDIN_TIMEOUT_MS) {
|
|
10851
|
-
return new Promise((
|
|
11683
|
+
return new Promise((resolve5, reject) => {
|
|
10852
11684
|
const chunks = [];
|
|
10853
11685
|
let settled = false;
|
|
10854
11686
|
const finish = (fn) => {
|
|
@@ -10874,7 +11706,7 @@ function readStdinJson(timeoutMs = DEFAULT_STDIN_TIMEOUT_MS) {
|
|
|
10874
11706
|
return;
|
|
10875
11707
|
}
|
|
10876
11708
|
try {
|
|
10877
|
-
|
|
11709
|
+
resolve5(JSON.parse(text));
|
|
10878
11710
|
} catch (err2) {
|
|
10879
11711
|
reject(err2 instanceof Error ? err2 : new Error(String(err2)));
|
|
10880
11712
|
}
|
|
@@ -10918,7 +11750,7 @@ async function relay(eventName) {
|
|
|
10918
11750
|
|
|
10919
11751
|
// src/section_reader.ts
|
|
10920
11752
|
init_define_import_meta_env();
|
|
10921
|
-
import { readFileSync as
|
|
11753
|
+
import { readFileSync as readFileSync5 } from "node:fs";
|
|
10922
11754
|
function parseHeadingSpec(spec) {
|
|
10923
11755
|
const m = /^(.*?)#(\d+)$/.exec(spec);
|
|
10924
11756
|
if (m !== null && m[1] !== void 0 && m[2] !== void 0) {
|
|
@@ -11000,7 +11832,7 @@ function sectionEndIndex(headers, headerPos, totalLines) {
|
|
|
11000
11832
|
function readSection(filePath, headingSpec) {
|
|
11001
11833
|
let text;
|
|
11002
11834
|
try {
|
|
11003
|
-
text =
|
|
11835
|
+
text = readFileSync5(filePath, "utf-8");
|
|
11004
11836
|
} catch {
|
|
11005
11837
|
return null;
|
|
11006
11838
|
}
|
|
@@ -11037,9 +11869,9 @@ function readSection(filePath, headingSpec) {
|
|
|
11037
11869
|
|
|
11038
11870
|
// src/install.ts
|
|
11039
11871
|
init_define_import_meta_env();
|
|
11040
|
-
import * as
|
|
11872
|
+
import * as fs9 from "node:fs";
|
|
11041
11873
|
import * as os2 from "node:os";
|
|
11042
|
-
import * as
|
|
11874
|
+
import * as path10 from "node:path";
|
|
11043
11875
|
var HOOK_EVENT_MAP = [
|
|
11044
11876
|
["PreToolUse", "pre_tool_use"],
|
|
11045
11877
|
["PostToolUse", "post_tool_use"],
|
|
@@ -11050,13 +11882,13 @@ function hookCommand(eventArg) {
|
|
|
11050
11882
|
return `token-goat hook ${eventArg}`;
|
|
11051
11883
|
}
|
|
11052
11884
|
function settingsPath(scope) {
|
|
11053
|
-
const base = scope === "user" ?
|
|
11054
|
-
return
|
|
11885
|
+
const base = scope === "user" ? path10.join(os2.homedir(), ".claude") : path10.join(process.cwd(), ".claude");
|
|
11886
|
+
return path10.join(base, "settings.json");
|
|
11055
11887
|
}
|
|
11056
11888
|
function readSettings(p) {
|
|
11057
11889
|
let raw;
|
|
11058
11890
|
try {
|
|
11059
|
-
raw =
|
|
11891
|
+
raw = fs9.readFileSync(p, "utf8");
|
|
11060
11892
|
} catch {
|
|
11061
11893
|
return {};
|
|
11062
11894
|
}
|
|
@@ -11096,7 +11928,7 @@ function installHooks(scope = "user") {
|
|
|
11096
11928
|
return { scope, settingsPath: p, alreadyInstalled: true };
|
|
11097
11929
|
}
|
|
11098
11930
|
settings.hooks = hooks;
|
|
11099
|
-
|
|
11931
|
+
fs9.mkdirSync(path10.dirname(p), { recursive: true });
|
|
11100
11932
|
atomicWriteText(p, `${JSON.stringify(settings, null, 2)}
|
|
11101
11933
|
`);
|
|
11102
11934
|
return { scope, settingsPath: p, alreadyInstalled: false };
|
|
@@ -11135,7 +11967,7 @@ function uninstallHooks(scope = "user") {
|
|
|
11135
11967
|
} else {
|
|
11136
11968
|
settings.hooks = hooks;
|
|
11137
11969
|
}
|
|
11138
|
-
|
|
11970
|
+
fs9.mkdirSync(path10.dirname(p), { recursive: true });
|
|
11139
11971
|
atomicWriteText(p, `${JSON.stringify(settings, null, 2)}
|
|
11140
11972
|
`);
|
|
11141
11973
|
return true;
|
|
@@ -11144,42 +11976,21 @@ function uninstallHooks(scope = "user") {
|
|
|
11144
11976
|
// src/worker.ts
|
|
11145
11977
|
init_define_import_meta_env();
|
|
11146
11978
|
import { spawn } from "node:child_process";
|
|
11147
|
-
import * as
|
|
11148
|
-
import * as
|
|
11979
|
+
import * as fs10 from "node:fs";
|
|
11980
|
+
import * as path11 from "node:path";
|
|
11149
11981
|
import { Worker, isMainThread, parentPort, workerData } from "node:worker_threads";
|
|
11150
11982
|
import { fileURLToPath } from "node:url";
|
|
11151
|
-
|
|
11152
|
-
// src/fingerprint.ts
|
|
11153
|
-
init_define_import_meta_env();
|
|
11154
|
-
import { createHash } from "node:crypto";
|
|
11155
|
-
import * as fs8 from "node:fs";
|
|
11156
|
-
function fingerprintContent(content) {
|
|
11157
|
-
const hash = createHash("sha256");
|
|
11158
|
-
hash.update(typeof content === "string" ? Buffer.from(content, "utf-8") : content);
|
|
11159
|
-
return hash.digest("hex");
|
|
11160
|
-
}
|
|
11161
|
-
function fingerprintFile(filePath) {
|
|
11162
|
-
let data;
|
|
11163
|
-
try {
|
|
11164
|
-
data = fs8.readFileSync(filePath);
|
|
11165
|
-
} catch {
|
|
11166
|
-
return null;
|
|
11167
|
-
}
|
|
11168
|
-
return fingerprintContent(data);
|
|
11169
|
-
}
|
|
11170
|
-
|
|
11171
|
-
// src/worker.ts
|
|
11172
11983
|
var DEFAULT_POLL_INTERVAL_MS = 2e3;
|
|
11173
11984
|
function dirtyQueuePathFor(dir) {
|
|
11174
|
-
return
|
|
11985
|
+
return path11.join(dir, "queue", "dirty.txt");
|
|
11175
11986
|
}
|
|
11176
11987
|
function workerPidPath(dir = dataDir()) {
|
|
11177
|
-
return
|
|
11988
|
+
return path11.join(dir, "worker.pid");
|
|
11178
11989
|
}
|
|
11179
11990
|
function getDirtyPathsFor(dir) {
|
|
11180
11991
|
let raw;
|
|
11181
11992
|
try {
|
|
11182
|
-
raw =
|
|
11993
|
+
raw = fs10.readFileSync(dirtyQueuePathFor(dir), "utf8");
|
|
11183
11994
|
} catch {
|
|
11184
11995
|
return [];
|
|
11185
11996
|
}
|
|
@@ -11195,7 +12006,7 @@ function getDirtyPathsFor(dir) {
|
|
|
11195
12006
|
}
|
|
11196
12007
|
function clearDirtyQueueFor(dir) {
|
|
11197
12008
|
try {
|
|
11198
|
-
|
|
12009
|
+
fs10.rmSync(dirtyQueuePathFor(dir), { force: true });
|
|
11199
12010
|
} catch {
|
|
11200
12011
|
}
|
|
11201
12012
|
}
|
|
@@ -11205,7 +12016,7 @@ function processDirtyBatch(paths, index = (absPath) => {
|
|
|
11205
12016
|
}) {
|
|
11206
12017
|
let indexed = 0;
|
|
11207
12018
|
for (const p of paths) {
|
|
11208
|
-
if (!
|
|
12019
|
+
if (!fs10.existsSync(p)) continue;
|
|
11209
12020
|
const sha = fingerprintFile(p);
|
|
11210
12021
|
if (sha === null) continue;
|
|
11211
12022
|
index(p, sha);
|
|
@@ -11230,7 +12041,7 @@ function pidAlive(pid) {
|
|
|
11230
12041
|
}
|
|
11231
12042
|
function readPidFile(dir) {
|
|
11232
12043
|
try {
|
|
11233
|
-
const raw =
|
|
12044
|
+
const raw = fs10.readFileSync(workerPidPath(dir), "utf8").trim();
|
|
11234
12045
|
if (!/^\d+$/.test(raw)) return null;
|
|
11235
12046
|
return parseInt(raw, 10);
|
|
11236
12047
|
} catch {
|
|
@@ -11253,7 +12064,7 @@ function stopWorker(dir = dataDir()) {
|
|
|
11253
12064
|
}
|
|
11254
12065
|
}
|
|
11255
12066
|
try {
|
|
11256
|
-
|
|
12067
|
+
fs10.rmSync(workerPidPath(dir), { force: true });
|
|
11257
12068
|
} catch {
|
|
11258
12069
|
}
|
|
11259
12070
|
return alive;
|
|
@@ -11261,7 +12072,7 @@ function stopWorker(dir = dataDir()) {
|
|
|
11261
12072
|
function startDetachedWorker(opts) {
|
|
11262
12073
|
const pollIntervalMs = opts?.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
11263
12074
|
const dir = opts?.dataDir ?? dataDir();
|
|
11264
|
-
|
|
12075
|
+
fs10.mkdirSync(dir, { recursive: true });
|
|
11265
12076
|
const child = spawn(
|
|
11266
12077
|
process.execPath,
|
|
11267
12078
|
[fileURLToPath(import.meta.url), "--worker-daemon"],
|
|
@@ -11280,7 +12091,7 @@ function startDetachedWorker(opts) {
|
|
|
11280
12091
|
if (pid === void 0) {
|
|
11281
12092
|
throw new Error("startDetachedWorker: spawn produced no pid");
|
|
11282
12093
|
}
|
|
11283
|
-
|
|
12094
|
+
fs10.writeFileSync(workerPidPath(dir), `${pid}
|
|
11284
12095
|
`);
|
|
11285
12096
|
child.unref();
|
|
11286
12097
|
return pid;
|
|
@@ -11292,7 +12103,7 @@ async function runWorkerLoop(dir, pollIntervalMs, shouldStop = () => false) {
|
|
|
11292
12103
|
} catch {
|
|
11293
12104
|
}
|
|
11294
12105
|
if (shouldStop()) break;
|
|
11295
|
-
await new Promise((
|
|
12106
|
+
await new Promise((resolve5) => setTimeout(resolve5, pollIntervalMs));
|
|
11296
12107
|
}
|
|
11297
12108
|
}
|
|
11298
12109
|
function workerEntry() {
|
|
@@ -11317,24 +12128,10 @@ function workerEntry() {
|
|
|
11317
12128
|
}
|
|
11318
12129
|
workerEntry();
|
|
11319
12130
|
|
|
11320
|
-
// src/bash_output_cache.ts
|
|
11321
|
-
init_define_import_meta_env();
|
|
11322
|
-
var _byId = /* @__PURE__ */ new Map();
|
|
11323
|
-
var _globsByKey = /* @__PURE__ */ new Map();
|
|
11324
|
-
var _grepsByKey = /* @__PURE__ */ new Map();
|
|
11325
|
-
function getBashOutput(id) {
|
|
11326
|
-
return _byId.get(id) ?? null;
|
|
11327
|
-
}
|
|
11328
|
-
registerReset(() => {
|
|
11329
|
-
_byId = /* @__PURE__ */ new Map();
|
|
11330
|
-
_globsByKey = /* @__PURE__ */ new Map();
|
|
11331
|
-
_grepsByKey = /* @__PURE__ */ new Map();
|
|
11332
|
-
});
|
|
11333
|
-
|
|
11334
12131
|
// src/skill_cache.ts
|
|
11335
12132
|
init_define_import_meta_env();
|
|
11336
|
-
import * as
|
|
11337
|
-
import { resolve as
|
|
12133
|
+
import * as fs11 from "fs/promises";
|
|
12134
|
+
import { resolve as resolve4 } from "path";
|
|
11338
12135
|
var COMPACT_END_MARKER = "<!-- COMPACT_END -->";
|
|
11339
12136
|
var _skillOutputsDirOverride = null;
|
|
11340
12137
|
registerReset(() => {
|
|
@@ -11342,11 +12139,11 @@ registerReset(() => {
|
|
|
11342
12139
|
});
|
|
11343
12140
|
function skillOutputsDir() {
|
|
11344
12141
|
if (_skillOutputsDirOverride) return _skillOutputsDirOverride;
|
|
11345
|
-
return
|
|
12142
|
+
return resolve4(dataDir(), "skills");
|
|
11346
12143
|
}
|
|
11347
12144
|
async function ensureSkillsDir() {
|
|
11348
12145
|
try {
|
|
11349
|
-
await
|
|
12146
|
+
await fs11.mkdir(skillOutputsDir(), { recursive: true });
|
|
11350
12147
|
} catch {
|
|
11351
12148
|
}
|
|
11352
12149
|
}
|
|
@@ -11388,14 +12185,14 @@ function extractCompactFromMarker(body) {
|
|
|
11388
12185
|
async function listOutputs() {
|
|
11389
12186
|
try {
|
|
11390
12187
|
const dir = skillOutputsDir();
|
|
11391
|
-
const entries = await
|
|
12188
|
+
const entries = await fs11.readdir(dir, { withFileTypes: true });
|
|
11392
12189
|
const metas = [];
|
|
11393
12190
|
for (const entry of entries) {
|
|
11394
12191
|
if (!entry.isFile() || !entry.name.endsWith(".meta")) {
|
|
11395
12192
|
continue;
|
|
11396
12193
|
}
|
|
11397
12194
|
try {
|
|
11398
|
-
const content = await
|
|
12195
|
+
const content = await fs11.readFile(resolve4(dir, entry.name), "utf-8");
|
|
11399
12196
|
const meta = JSON.parse(content);
|
|
11400
12197
|
metas.push(meta);
|
|
11401
12198
|
} catch {
|
|
@@ -11420,7 +12217,7 @@ async function storeCompact(sessionId, skillName, compactText, sourceSha) {
|
|
|
11420
12217
|
text = `<!-- source_sha: ${sourceSha.slice(0, 12)} -->
|
|
11421
12218
|
${text}`;
|
|
11422
12219
|
}
|
|
11423
|
-
await atomicWriteText(
|
|
12220
|
+
await atomicWriteText(resolve4(dir, fileId), text);
|
|
11424
12221
|
} catch {
|
|
11425
12222
|
}
|
|
11426
12223
|
}
|
|
@@ -11440,13 +12237,13 @@ async function listSkills(sessionId) {
|
|
|
11440
12237
|
const compactFileId = `${safeSession}-${meta.skillName.replace(":", "_")}-compact`;
|
|
11441
12238
|
let compactLen = 0;
|
|
11442
12239
|
try {
|
|
11443
|
-
const
|
|
11444
|
-
compactLen =
|
|
12240
|
+
const stat3 = await fs11.stat(resolve4(dir, compactFileId));
|
|
12241
|
+
compactLen = stat3.size;
|
|
11445
12242
|
} catch {
|
|
11446
12243
|
compactLen = 0;
|
|
11447
12244
|
}
|
|
11448
12245
|
const hasMarker = extractCompactFromMarker(
|
|
11449
|
-
await
|
|
12246
|
+
await fs11.readFile(resolve4(dir, `${meta.outputId}.txt`), "utf-8").catch(() => "")
|
|
11450
12247
|
) !== null;
|
|
11451
12248
|
results.push({
|
|
11452
12249
|
name: meta.skillName,
|
|
@@ -11478,7 +12275,7 @@ async function getSkillFilePath(skillName) {
|
|
|
11478
12275
|
|
|
11479
12276
|
// src/config.ts
|
|
11480
12277
|
init_define_import_meta_env();
|
|
11481
|
-
import * as
|
|
12278
|
+
import * as fs12 from "node:fs";
|
|
11482
12279
|
|
|
11483
12280
|
// node_modules/smol-toml/dist/index.js
|
|
11484
12281
|
init_define_import_meta_env();
|
|
@@ -12463,7 +13260,7 @@ function loadConfig() {
|
|
|
12463
13260
|
const p = configPath();
|
|
12464
13261
|
let currentMtime = 0;
|
|
12465
13262
|
try {
|
|
12466
|
-
currentMtime =
|
|
13263
|
+
currentMtime = fs12.statSync(p).mtimeMs;
|
|
12467
13264
|
} catch {
|
|
12468
13265
|
}
|
|
12469
13266
|
const envFp = configEnvFingerprint();
|
|
@@ -12473,7 +13270,7 @@ function loadConfig() {
|
|
|
12473
13270
|
let raw = {};
|
|
12474
13271
|
if (currentMtime !== 0) {
|
|
12475
13272
|
try {
|
|
12476
|
-
const text =
|
|
13273
|
+
const text = fs12.readFileSync(p, "utf8");
|
|
12477
13274
|
raw = parse(text);
|
|
12478
13275
|
} catch {
|
|
12479
13276
|
}
|
|
@@ -12683,7 +13480,686 @@ function _buildConfig(raw) {
|
|
|
12683
13480
|
|
|
12684
13481
|
// src/stats.ts
|
|
12685
13482
|
init_define_import_meta_env();
|
|
12686
|
-
import * as
|
|
13483
|
+
import * as path12 from "node:path";
|
|
13484
|
+
|
|
13485
|
+
// src/render/stats_renderer.ts
|
|
13486
|
+
init_define_import_meta_env();
|
|
13487
|
+
|
|
13488
|
+
// src/render/ansi.ts
|
|
13489
|
+
init_define_import_meta_env();
|
|
13490
|
+
function _colorStream(isatty) {
|
|
13491
|
+
if (process.env["NO_COLOR"]) return false;
|
|
13492
|
+
return isatty;
|
|
13493
|
+
}
|
|
13494
|
+
function colorStdout() {
|
|
13495
|
+
return _colorStream(process.stdout.isTTY === true);
|
|
13496
|
+
}
|
|
13497
|
+
var USE_COLOR = colorStdout();
|
|
13498
|
+
var _E = "\x1B";
|
|
13499
|
+
var RESET = `${_E}[0m`;
|
|
13500
|
+
var _ANSI_ESCAPE_RE = /\x1B\[[0-?]*[ -/]*[@-~]|\x1B\].*?(?:\x07|\x1B\\)|\x1B[PX^_].*?\x1B\\|\x1B[@-Z\\\-_]/gs;
|
|
13501
|
+
var _PUA_RE = /[\u{E000}-\u{F8FF}\u{F0000}-\u{FFFDD}]/gu;
|
|
13502
|
+
function stripAnsi(s) {
|
|
13503
|
+
if (!s.includes("\x1B")) {
|
|
13504
|
+
return s;
|
|
13505
|
+
}
|
|
13506
|
+
const text = s.replace(_ANSI_ESCAPE_RE, "");
|
|
13507
|
+
return text.replace(_PUA_RE, "");
|
|
13508
|
+
}
|
|
13509
|
+
function fg(r, g, b) {
|
|
13510
|
+
return `${_E}[38;2;${r};${g};${b}m`;
|
|
13511
|
+
}
|
|
13512
|
+
function vlen(s) {
|
|
13513
|
+
return stripAnsi(s).length;
|
|
13514
|
+
}
|
|
13515
|
+
function padR(s, w) {
|
|
13516
|
+
return s + " ".repeat(Math.max(0, w - vlen(s)));
|
|
13517
|
+
}
|
|
13518
|
+
function padL(s, w) {
|
|
13519
|
+
return " ".repeat(Math.max(0, w - vlen(s))) + s;
|
|
13520
|
+
}
|
|
13521
|
+
function lerpRgb(a, b, t) {
|
|
13522
|
+
return [
|
|
13523
|
+
Math.round(a[0] + (b[0] - a[0]) * t),
|
|
13524
|
+
Math.round(a[1] + (b[1] - a[1]) * t),
|
|
13525
|
+
Math.round(a[2] + (b[2] - a[2]) * t)
|
|
13526
|
+
];
|
|
13527
|
+
}
|
|
13528
|
+
var C = {
|
|
13529
|
+
TEXT_PRIMARY: [201, 209, 217],
|
|
13530
|
+
TEXT_BRIGHT: [240, 246, 252],
|
|
13531
|
+
TEXT_MUTED: [125, 133, 144],
|
|
13532
|
+
TEXT_DIM: [72, 79, 88],
|
|
13533
|
+
BG_TILE: [22, 27, 34],
|
|
13534
|
+
TRACK: [28, 35, 41],
|
|
13535
|
+
GREEN1: [31, 77, 44],
|
|
13536
|
+
GREEN2: [46, 160, 67],
|
|
13537
|
+
GREEN3: [63, 185, 80],
|
|
13538
|
+
GREEN4: [86, 211, 100],
|
|
13539
|
+
GREEN5: [126, 231, 135],
|
|
13540
|
+
BLUE: [88, 166, 255],
|
|
13541
|
+
PURPLE: [188, 140, 255],
|
|
13542
|
+
TEAL: [138, 212, 255],
|
|
13543
|
+
ORANGE: [235, 165, 80],
|
|
13544
|
+
YELLOW: [240, 215, 80],
|
|
13545
|
+
RED: [200, 60, 60]
|
|
13546
|
+
};
|
|
13547
|
+
|
|
13548
|
+
// src/render/stats_renderer.ts
|
|
13549
|
+
var _STATS_MESSAGES_FALLBACK = {
|
|
13550
|
+
bytesModeOnlyNote: "tracks bytes, not vision tokens",
|
|
13551
|
+
sessionHintSplitNote: "session_hint shows realized savings; session_hint_overhead shows injected hint cost",
|
|
13552
|
+
insights: {
|
|
13553
|
+
biggestSaver: "Biggest saver ",
|
|
13554
|
+
mostActive: "Most active ",
|
|
13555
|
+
tokenLeader: "Token leader "
|
|
13556
|
+
}
|
|
13557
|
+
};
|
|
13558
|
+
var _STATS_MESSAGES = _STATS_MESSAGES_FALLBACK;
|
|
13559
|
+
var _TERM_W = process.stdout.columns || 100;
|
|
13560
|
+
var _CONTENT_W = Math.min(Math.max(_TERM_W, 80), 140);
|
|
13561
|
+
var _M = " ";
|
|
13562
|
+
var _COL_NAME = 18;
|
|
13563
|
+
var _COL_DATA = 10;
|
|
13564
|
+
var _COL_TOKENS = 12;
|
|
13565
|
+
var _COL_SHARE = 6;
|
|
13566
|
+
var _COL_EVENTS = 6;
|
|
13567
|
+
var _COLS_FIXED = _COL_NAME + 1 + 2 + _COL_DATA + 2 + _COL_TOKENS + 2 + _COL_SHARE + 2 + _COL_EVENTS;
|
|
13568
|
+
var _BAR_W = Math.max(16, _CONTENT_W - _M.length * 2 - _COLS_FIXED);
|
|
13569
|
+
var _RULE = _M + fg(...C.TEXT_DIM) + "\u2500".repeat(_CONTENT_W - _M.length * 2) + RESET;
|
|
13570
|
+
var _BYTE_TIERS = [
|
|
13571
|
+
{ threshold: 1e15, divisor: 1e15, unit: "PB", color: C.PURPLE },
|
|
13572
|
+
{ threshold: 1e12, divisor: 1e12, unit: "TB", color: C.BLUE },
|
|
13573
|
+
{ threshold: 1e9, divisor: 1e9, unit: "GB", color: C.TEAL },
|
|
13574
|
+
{ threshold: 1e6, divisor: 1e6, unit: "MB", color: C.GREEN4 },
|
|
13575
|
+
{ threshold: 1e3, divisor: 1e3, unit: "KB", color: C.TEXT_MUTED },
|
|
13576
|
+
{ threshold: 0, divisor: 1, unit: "B", color: C.TEXT_DIM }
|
|
13577
|
+
];
|
|
13578
|
+
var _TOKEN_TIERS = [
|
|
13579
|
+
{ threshold: 1e12, divisor: 1e12, unit: "Tt", color: C.GREEN5 },
|
|
13580
|
+
{ threshold: 1e9, divisor: 1e9, unit: "Gt", color: C.TEAL },
|
|
13581
|
+
{ threshold: 1e6, divisor: 1e6, unit: "Mt", color: C.PURPLE },
|
|
13582
|
+
{ threshold: 1e3, divisor: 1e3, unit: "kt", color: C.BLUE },
|
|
13583
|
+
{ threshold: 0, divisor: 1, unit: "t", color: C.TEXT_DIM }
|
|
13584
|
+
];
|
|
13585
|
+
function _fmtMagnitude(n, tiers, zeroLabel) {
|
|
13586
|
+
if (zeroLabel !== void 0 && n === 0) {
|
|
13587
|
+
return `${fg(...C.TEXT_DIM)}${zeroLabel}${RESET}`;
|
|
13588
|
+
}
|
|
13589
|
+
if (n < 0) {
|
|
13590
|
+
const a = -n;
|
|
13591
|
+
const color = C.TEXT_DIM;
|
|
13592
|
+
for (const tier of tiers) {
|
|
13593
|
+
if (a >= tier.threshold && tier.threshold > 0) {
|
|
13594
|
+
return `${fg(...color)}-${(a / tier.divisor).toLocaleString("en", { maximumFractionDigits: 1 })} ${tier.unit}${RESET}`;
|
|
13595
|
+
}
|
|
13596
|
+
}
|
|
13597
|
+
const lastTier2 = tiers[tiers.length - 1];
|
|
13598
|
+
if (lastTier2) {
|
|
13599
|
+
return `${fg(...color)}-${a} ${lastTier2.unit}${RESET}`;
|
|
13600
|
+
}
|
|
13601
|
+
return `${fg(...color)}-${a}${RESET}`;
|
|
13602
|
+
}
|
|
13603
|
+
for (const tier of tiers) {
|
|
13604
|
+
if (n >= tier.threshold && tier.threshold > 0) {
|
|
13605
|
+
return `${fg(...tier.color)}${(n / tier.divisor).toLocaleString("en", { maximumFractionDigits: 1 })} ${tier.unit}${RESET}`;
|
|
13606
|
+
}
|
|
13607
|
+
}
|
|
13608
|
+
const lastTier = tiers[tiers.length - 1];
|
|
13609
|
+
if (lastTier) {
|
|
13610
|
+
return `${fg(...lastTier.color)}${n} ${lastTier.unit}${RESET}`;
|
|
13611
|
+
}
|
|
13612
|
+
return `${n}${RESET}`;
|
|
13613
|
+
}
|
|
13614
|
+
function _fmtBytes(n) {
|
|
13615
|
+
return _fmtMagnitude(n, _BYTE_TIERS);
|
|
13616
|
+
}
|
|
13617
|
+
function _fmtTokens(n) {
|
|
13618
|
+
return _fmtMagnitude(n, _TOKEN_TIERS, "0 t");
|
|
13619
|
+
}
|
|
13620
|
+
function _fmtPct(fraction) {
|
|
13621
|
+
return `${(fraction * 100).toFixed(1)}%`;
|
|
13622
|
+
}
|
|
13623
|
+
function _fmtDelta(delta) {
|
|
13624
|
+
if (!delta && delta !== 0) {
|
|
13625
|
+
return "";
|
|
13626
|
+
}
|
|
13627
|
+
const up = (delta ?? 0) >= 0;
|
|
13628
|
+
const color = up ? C.GREEN5 : C.RED;
|
|
13629
|
+
const arrow = up ? "\u2191" : "\u2193";
|
|
13630
|
+
return ` ${fg(...color)}${arrow} ${Math.round(Math.abs(delta ?? 0))}%${RESET}`;
|
|
13631
|
+
}
|
|
13632
|
+
var _EIGHTHS = ["\u258F", "\u258E", "\u258D", "\u258C", "\u258B", "\u258A", "\u2589"];
|
|
13633
|
+
var _BLOCK = "\u2588";
|
|
13634
|
+
var _TRACK = "\u2591";
|
|
13635
|
+
var _GRADIENT = [C.GREEN1, C.GREEN2, C.GREEN3, C.GREEN4, C.GREEN5];
|
|
13636
|
+
function _distribute(total, n) {
|
|
13637
|
+
if (total <= 0 || n <= 0) {
|
|
13638
|
+
return Array(Math.max(0, n)).fill(0);
|
|
13639
|
+
}
|
|
13640
|
+
const base = Math.floor(total / n);
|
|
13641
|
+
const rem = total % n;
|
|
13642
|
+
return Array.from({ length: n }, (_, i) => base + (i >= n - rem ? 1 : 0));
|
|
13643
|
+
}
|
|
13644
|
+
function _renderBar(fraction, width = _BAR_W) {
|
|
13645
|
+
const f = Math.max(0, Math.min(1, fraction));
|
|
13646
|
+
const raw = f * width;
|
|
13647
|
+
let nFull = Math.floor(raw);
|
|
13648
|
+
const eighths = Math.round((raw - nFull) * 8);
|
|
13649
|
+
if (eighths >= 8) {
|
|
13650
|
+
nFull += 1;
|
|
13651
|
+
}
|
|
13652
|
+
const hasPartial = eighths > 0 && eighths < 8;
|
|
13653
|
+
const nTrack = Math.max(0, width - nFull - (hasPartial ? 1 : 0));
|
|
13654
|
+
const counts = _distribute(nFull, _GRADIENT.length);
|
|
13655
|
+
let bar = counts.map((count, i) => count > 0 ? `${fg(_GRADIENT[i][0], _GRADIENT[i][1], _GRADIENT[i][2])}${_BLOCK.repeat(count)}` : "").join("");
|
|
13656
|
+
if (hasPartial) {
|
|
13657
|
+
const lastGrad = _GRADIENT[_GRADIENT.length - 1];
|
|
13658
|
+
bar += `${fg(lastGrad[0], lastGrad[1], lastGrad[2])}${_EIGHTHS[eighths - 1]}`;
|
|
13659
|
+
}
|
|
13660
|
+
if (nTrack > 0) {
|
|
13661
|
+
bar += `${fg(...C.TRACK)}${_TRACK.repeat(nTrack)}`;
|
|
13662
|
+
}
|
|
13663
|
+
return bar + RESET;
|
|
13664
|
+
}
|
|
13665
|
+
var _SPARK = "\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588";
|
|
13666
|
+
function _resample(vals, length) {
|
|
13667
|
+
if (vals.length === 0) {
|
|
13668
|
+
return Array(length).fill(0);
|
|
13669
|
+
}
|
|
13670
|
+
if (vals.length === length) {
|
|
13671
|
+
return [...vals];
|
|
13672
|
+
}
|
|
13673
|
+
const result = [];
|
|
13674
|
+
for (let i = 0; i < length; i++) {
|
|
13675
|
+
const src = i / (length - 1 || 1) * (vals.length - 1);
|
|
13676
|
+
const lo = Math.floor(src);
|
|
13677
|
+
const hi = Math.min(vals.length - 1, lo + 1);
|
|
13678
|
+
const t = src - lo;
|
|
13679
|
+
const loVal = vals[lo] ?? 0;
|
|
13680
|
+
const hiVal = vals[hi] ?? 0;
|
|
13681
|
+
result.push(loVal * (1 - t) + hiVal * t);
|
|
13682
|
+
}
|
|
13683
|
+
return result;
|
|
13684
|
+
}
|
|
13685
|
+
function _renderSparkline(values, width = 8) {
|
|
13686
|
+
const pts = _resample(values, width);
|
|
13687
|
+
const hi = pts.length > 0 ? Math.max(...pts) : 1;
|
|
13688
|
+
const lo = pts.length > 0 ? Math.min(...pts) : 0;
|
|
13689
|
+
const span = hi - lo || 1;
|
|
13690
|
+
const chars = [];
|
|
13691
|
+
for (let i = 0; i < pts.length; i++) {
|
|
13692
|
+
const v = pts[i];
|
|
13693
|
+
if (v === void 0) continue;
|
|
13694
|
+
const idx = Math.min(7, Math.floor((v - lo) / span * 8));
|
|
13695
|
+
const color = lerpRgb(C.GREEN1, C.GREEN5, i / (width - 1 || 1));
|
|
13696
|
+
chars.push(`${fg(color[0], color[1], color[2])}${_SPARK[idx]}`);
|
|
13697
|
+
}
|
|
13698
|
+
return chars.join("") + RESET;
|
|
13699
|
+
}
|
|
13700
|
+
function _tokenOrByteShare(itemTokens, itemBytes, totalTokens, totalBytes) {
|
|
13701
|
+
if (totalTokens > 0) {
|
|
13702
|
+
return itemTokens / totalTokens;
|
|
13703
|
+
}
|
|
13704
|
+
if (totalBytes > 0) {
|
|
13705
|
+
return itemBytes / totalBytes;
|
|
13706
|
+
}
|
|
13707
|
+
return 0;
|
|
13708
|
+
}
|
|
13709
|
+
function _barFraction(itemBytes, grossBytes) {
|
|
13710
|
+
return itemBytes > 0 ? itemBytes / grossBytes : 0;
|
|
13711
|
+
}
|
|
13712
|
+
function _computeShareDenominators(items) {
|
|
13713
|
+
let grossBytesSum = 0;
|
|
13714
|
+
let shareByteSum = 0;
|
|
13715
|
+
let shareTokensSum = 0;
|
|
13716
|
+
for (const item of items) {
|
|
13717
|
+
if (item.bytes > 0) {
|
|
13718
|
+
grossBytesSum += item.bytes;
|
|
13719
|
+
}
|
|
13720
|
+
shareByteSum += Math.abs(item.bytes);
|
|
13721
|
+
shareTokensSum += Math.abs(item.tokens);
|
|
13722
|
+
}
|
|
13723
|
+
return {
|
|
13724
|
+
grossBytes: Math.max(grossBytesSum, 1),
|
|
13725
|
+
shareBytesDenom: Math.max(shareByteSum, 1),
|
|
13726
|
+
shareTokensDenom: shareTokensSum
|
|
13727
|
+
};
|
|
13728
|
+
}
|
|
13729
|
+
function _absShare(itemBytes, itemTokens, shareBytesDenom, shareTokensDenom) {
|
|
13730
|
+
if (shareTokensDenom === 0) {
|
|
13731
|
+
return itemBytes / shareBytesDenom;
|
|
13732
|
+
}
|
|
13733
|
+
return itemTokens / shareTokensDenom;
|
|
13734
|
+
}
|
|
13735
|
+
function _sectionHeader(title, subtitle = "") {
|
|
13736
|
+
const sub = subtitle ? ` ${fg(...C.TEXT_MUTED)}${subtitle}${RESET}` : "";
|
|
13737
|
+
return [
|
|
13738
|
+
"",
|
|
13739
|
+
`${_M}${fg(...C.TEXT_BRIGHT)}${title}${RESET}${sub}`,
|
|
13740
|
+
_RULE
|
|
13741
|
+
];
|
|
13742
|
+
}
|
|
13743
|
+
function _tableHeader(firstColLabel) {
|
|
13744
|
+
return [
|
|
13745
|
+
_M,
|
|
13746
|
+
padR(`${fg(...C.TEXT_DIM)}${firstColLabel}${RESET}`, _COL_NAME),
|
|
13747
|
+
" ",
|
|
13748
|
+
padR(`${fg(...C.TEXT_DIM)}savings${RESET}`, _BAR_W),
|
|
13749
|
+
" ",
|
|
13750
|
+
padL(`${fg(...C.TEXT_DIM)}data saved${RESET}`, _COL_DATA),
|
|
13751
|
+
" ",
|
|
13752
|
+
padL(`${fg(...C.TEXT_DIM)}tokens saved${RESET}`, _COL_TOKENS),
|
|
13753
|
+
" ",
|
|
13754
|
+
padL(`${fg(...C.TEXT_DIM)}share${RESET}`, _COL_SHARE),
|
|
13755
|
+
" ",
|
|
13756
|
+
padL(`${fg(...C.TEXT_DIM)}events${RESET}`, _COL_EVENTS)
|
|
13757
|
+
].join("");
|
|
13758
|
+
}
|
|
13759
|
+
function _tableRow({
|
|
13760
|
+
name,
|
|
13761
|
+
fraction,
|
|
13762
|
+
bytes,
|
|
13763
|
+
tokens,
|
|
13764
|
+
events,
|
|
13765
|
+
share,
|
|
13766
|
+
bytesModeOnly = false,
|
|
13767
|
+
namePrefix = "",
|
|
13768
|
+
nameColor = C.TEXT_PRIMARY
|
|
13769
|
+
}) {
|
|
13770
|
+
const prefixW = vlen(namePrefix);
|
|
13771
|
+
const maxName = _COL_NAME - prefixW;
|
|
13772
|
+
const truncated = name.length > maxName ? name.slice(0, maxName - 1) + "\u2026" : name;
|
|
13773
|
+
const nameStr = padR(`${namePrefix}${fg(...nameColor)}${truncated}${RESET}`, _COL_NAME);
|
|
13774
|
+
const dataStr = padL(_fmtBytes(bytes), _COL_DATA);
|
|
13775
|
+
const tokStr = bytesModeOnly ? padL(`${fg(...C.TEXT_DIM)}\u2014${RESET}`, _COL_TOKENS) : padL(_fmtTokens(tokens), _COL_TOKENS);
|
|
13776
|
+
const sharePct = share * 100;
|
|
13777
|
+
let shareColor;
|
|
13778
|
+
if (sharePct < 0) {
|
|
13779
|
+
shareColor = C.RED;
|
|
13780
|
+
} else if (sharePct >= 50) {
|
|
13781
|
+
shareColor = C.GREEN5;
|
|
13782
|
+
} else if (sharePct >= 10) {
|
|
13783
|
+
shareColor = C.TEXT_PRIMARY;
|
|
13784
|
+
} else {
|
|
13785
|
+
shareColor = C.TEXT_MUTED;
|
|
13786
|
+
}
|
|
13787
|
+
const shareStr = padL(`${fg(...shareColor)}${_fmtPct(share)}${RESET}`, _COL_SHARE);
|
|
13788
|
+
const evStr = padL(`${fg(...C.TEXT_PRIMARY)}${events.toLocaleString()}${RESET}`, _COL_EVENTS);
|
|
13789
|
+
return [_M, nameStr, " ", _renderBar(fraction), " ", dataStr, " ", tokStr, " ", shareStr, " ", evStr].join("");
|
|
13790
|
+
}
|
|
13791
|
+
function _renderKpiSection(stats) {
|
|
13792
|
+
const totals = stats.totals;
|
|
13793
|
+
const colW = Math.floor((_CONTENT_W - _M.length * 2) / 3);
|
|
13794
|
+
function card(label, value, delta, spark2) {
|
|
13795
|
+
return [
|
|
13796
|
+
padR(`${fg(C.TEXT_MUTED[0], C.TEXT_MUTED[1], C.TEXT_MUTED[2])}${label}${RESET}`, colW),
|
|
13797
|
+
padR(`${fg(C.TEXT_BRIGHT[0], C.TEXT_BRIGHT[1], C.TEXT_BRIGHT[2])}${value}${RESET}${delta}`, colW),
|
|
13798
|
+
spark2 !== null ? padR(spark2, colW) : padR("", colW)
|
|
13799
|
+
];
|
|
13800
|
+
}
|
|
13801
|
+
const spark = totals.sparklines;
|
|
13802
|
+
const c1 = card(
|
|
13803
|
+
"events",
|
|
13804
|
+
`${totals.events.toLocaleString()}`,
|
|
13805
|
+
_fmtDelta(totals.events_delta ?? null),
|
|
13806
|
+
spark ? _renderSparkline(spark.events) : null
|
|
13807
|
+
);
|
|
13808
|
+
const c2 = card(
|
|
13809
|
+
"data saved",
|
|
13810
|
+
_fmtBytes(totals.bytes),
|
|
13811
|
+
_fmtDelta(totals.bytes_delta ?? null),
|
|
13812
|
+
spark ? _renderSparkline(spark.bytes) : null
|
|
13813
|
+
);
|
|
13814
|
+
const c3 = card(
|
|
13815
|
+
"tokens saved",
|
|
13816
|
+
_fmtTokens(totals.tokens),
|
|
13817
|
+
_fmtDelta(totals.tokens_delta ?? null),
|
|
13818
|
+
spark ? _renderSparkline(spark.tokens) : null
|
|
13819
|
+
);
|
|
13820
|
+
const border = fg(C.TEXT_DIM[0], C.TEXT_DIM[1], C.TEXT_DIM[2]);
|
|
13821
|
+
const frameBar = "\u2500".repeat(colW * 3 + 2);
|
|
13822
|
+
function framed(content) {
|
|
13823
|
+
return `${_M}${border}\u2502${RESET} ${content} ${border}\u2502${RESET}`;
|
|
13824
|
+
}
|
|
13825
|
+
const lines = [
|
|
13826
|
+
"",
|
|
13827
|
+
`${_M}${border}\u256D${frameBar}\u256E${RESET}`,
|
|
13828
|
+
framed(c1[0] + c2[0] + c3[0]),
|
|
13829
|
+
framed(c1[1] + c2[1] + c3[1])
|
|
13830
|
+
];
|
|
13831
|
+
if (spark) {
|
|
13832
|
+
lines.push(framed(c1[2] + c2[2] + c3[2]));
|
|
13833
|
+
}
|
|
13834
|
+
lines.push(`${_M}${border}\u2570${frameBar}\u256F${RESET}`);
|
|
13835
|
+
return lines;
|
|
13836
|
+
}
|
|
13837
|
+
var _KIND_GROUPS = [
|
|
13838
|
+
{
|
|
13839
|
+
label: "Read savings",
|
|
13840
|
+
members: /* @__PURE__ */ new Set([
|
|
13841
|
+
"read_replacement",
|
|
13842
|
+
"section_replacement",
|
|
13843
|
+
"symbol_read",
|
|
13844
|
+
"section_read",
|
|
13845
|
+
"stub_view",
|
|
13846
|
+
"outline",
|
|
13847
|
+
"exports"
|
|
13848
|
+
])
|
|
13849
|
+
},
|
|
13850
|
+
{ label: "Lookups", members: /* @__PURE__ */ new Set(["symbol_lookup", "semantic_search", "map_lookup"]) },
|
|
13851
|
+
{
|
|
13852
|
+
label: "Images",
|
|
13853
|
+
members: /* @__PURE__ */ new Set(["image_shrink", "gdrive_image", "webfetch_image", "image_shrink_skipped"])
|
|
13854
|
+
},
|
|
13855
|
+
{
|
|
13856
|
+
label: "Hints",
|
|
13857
|
+
members: /* @__PURE__ */ new Set([
|
|
13858
|
+
"session_hint",
|
|
13859
|
+
"session_hint_overhead",
|
|
13860
|
+
"read_dedup_hint",
|
|
13861
|
+
"grep_dedup_hint",
|
|
13862
|
+
"diff_hint",
|
|
13863
|
+
"predictive_prefetch_hit",
|
|
13864
|
+
"read_partial_overlap_hint"
|
|
13865
|
+
])
|
|
13866
|
+
},
|
|
13867
|
+
{
|
|
13868
|
+
label: "Bash",
|
|
13869
|
+
members: /* @__PURE__ */ new Set([
|
|
13870
|
+
"bash_dedup_hint",
|
|
13871
|
+
"bash_output_cached",
|
|
13872
|
+
"bash_output_recall",
|
|
13873
|
+
"bash_output_recall_miss",
|
|
13874
|
+
"bash_dedup_stale",
|
|
13875
|
+
"bash_range_read_hint",
|
|
13876
|
+
"bash_streak_hint",
|
|
13877
|
+
"bash_poll_hint",
|
|
13878
|
+
"env_probe_cache_hit",
|
|
13879
|
+
"git_diff_scope_hint",
|
|
13880
|
+
"dep_list_cache_hit",
|
|
13881
|
+
"bash_read_equiv_already_read",
|
|
13882
|
+
"bash_grep_result_cache_hit",
|
|
13883
|
+
"git_diff_context_trimmed"
|
|
13884
|
+
])
|
|
13885
|
+
},
|
|
13886
|
+
{
|
|
13887
|
+
label: "Web",
|
|
13888
|
+
members: /* @__PURE__ */ new Set([
|
|
13889
|
+
"web_dedup_hint",
|
|
13890
|
+
"web_output_cached",
|
|
13891
|
+
"web_output_recall",
|
|
13892
|
+
"web_output_recall_miss",
|
|
13893
|
+
"web_dedup_stale"
|
|
13894
|
+
])
|
|
13895
|
+
},
|
|
13896
|
+
{
|
|
13897
|
+
label: "Compact / Skills",
|
|
13898
|
+
members: /* @__PURE__ */ new Set([
|
|
13899
|
+
"compact_manifest",
|
|
13900
|
+
"compact_assist",
|
|
13901
|
+
"compact_recovery",
|
|
13902
|
+
"skill_body_recall",
|
|
13903
|
+
"skill_compact_served",
|
|
13904
|
+
"skill_cached",
|
|
13905
|
+
"resume_packet",
|
|
13906
|
+
"decision_log"
|
|
13907
|
+
])
|
|
13908
|
+
}
|
|
13909
|
+
];
|
|
13910
|
+
function _kindGroupLabel(kind) {
|
|
13911
|
+
if (kind.startsWith("bash_compress:")) {
|
|
13912
|
+
return "Bash";
|
|
13913
|
+
}
|
|
13914
|
+
for (const group of _KIND_GROUPS) {
|
|
13915
|
+
if (group.members.has(kind)) {
|
|
13916
|
+
return group.label;
|
|
13917
|
+
}
|
|
13918
|
+
}
|
|
13919
|
+
return "Other";
|
|
13920
|
+
}
|
|
13921
|
+
function _groupSeparator(label) {
|
|
13922
|
+
return `${_M} ${fg(...C.TEXT_DIM)}${label}${RESET}`;
|
|
13923
|
+
}
|
|
13924
|
+
function _renderByKindSection(stats) {
|
|
13925
|
+
if (stats.by_kind.length === 0) {
|
|
13926
|
+
return [];
|
|
13927
|
+
}
|
|
13928
|
+
const lines = [..._sectionHeader("By kind"), _tableHeader("name")];
|
|
13929
|
+
const { grossBytes, shareBytesDenom, shareTokensDenom } = _computeShareDenominators(stats.by_kind);
|
|
13930
|
+
const kindNames = new Set(stats.by_kind.map((k) => k.kind));
|
|
13931
|
+
const bytesModeKinds = stats.by_kind.filter((k) => k.bytes_mode_only).map((k) => k.kind);
|
|
13932
|
+
function share(k) {
|
|
13933
|
+
if (k.bytes_mode_only) {
|
|
13934
|
+
return k.bytes / shareBytesDenom;
|
|
13935
|
+
}
|
|
13936
|
+
return _absShare(k.bytes, k.tokens, shareBytesDenom, shareTokensDenom);
|
|
13937
|
+
}
|
|
13938
|
+
const byGroup = /* @__PURE__ */ new Map();
|
|
13939
|
+
for (const k of stats.by_kind) {
|
|
13940
|
+
const grp = _kindGroupLabel(k.kind);
|
|
13941
|
+
if (!byGroup.has(grp)) {
|
|
13942
|
+
byGroup.set(grp, []);
|
|
13943
|
+
}
|
|
13944
|
+
byGroup.get(grp).push(k);
|
|
13945
|
+
}
|
|
13946
|
+
for (const grpKinds of byGroup.values()) {
|
|
13947
|
+
grpKinds.sort((a, b) => share(b) - share(a));
|
|
13948
|
+
}
|
|
13949
|
+
let firstGroup = true;
|
|
13950
|
+
for (const group of _KIND_GROUPS) {
|
|
13951
|
+
const groupKinds = byGroup.get(group.label);
|
|
13952
|
+
if (!groupKinds || groupKinds.length === 0) {
|
|
13953
|
+
continue;
|
|
13954
|
+
}
|
|
13955
|
+
if (!firstGroup) {
|
|
13956
|
+
lines.push("");
|
|
13957
|
+
}
|
|
13958
|
+
firstGroup = false;
|
|
13959
|
+
lines.push(_groupSeparator(group.label));
|
|
13960
|
+
for (const k of groupKinds) {
|
|
13961
|
+
const s = share(k);
|
|
13962
|
+
lines.push(
|
|
13963
|
+
_tableRow({
|
|
13964
|
+
name: k.kind,
|
|
13965
|
+
fraction: _barFraction(k.bytes, grossBytes),
|
|
13966
|
+
bytes: k.bytes,
|
|
13967
|
+
tokens: k.tokens,
|
|
13968
|
+
events: k.events,
|
|
13969
|
+
share: s,
|
|
13970
|
+
bytesModeOnly: k.bytes_mode_only ?? false
|
|
13971
|
+
})
|
|
13972
|
+
);
|
|
13973
|
+
}
|
|
13974
|
+
}
|
|
13975
|
+
if (bytesModeKinds.length > 0) {
|
|
13976
|
+
const names = bytesModeKinds.join(", ");
|
|
13977
|
+
lines.push(`${_M}${fg(...C.TEXT_DIM)}i ${names} ${_STATS_MESSAGES.bytesModeOnlyNote}${RESET}`);
|
|
13978
|
+
}
|
|
13979
|
+
if (kindNames.has("session_hint") && kindNames.has("session_hint_overhead")) {
|
|
13980
|
+
lines.push(`${_M}${fg(...C.TEXT_DIM)}i ${_STATS_MESSAGES.sessionHintSplitNote}${RESET}`);
|
|
13981
|
+
}
|
|
13982
|
+
return lines;
|
|
13983
|
+
}
|
|
13984
|
+
var _SOURCE_COLORS = {
|
|
13985
|
+
image: C.PURPLE,
|
|
13986
|
+
hint: C.BLUE,
|
|
13987
|
+
read: C.GREEN4,
|
|
13988
|
+
compact: C.TEAL,
|
|
13989
|
+
bash: C.ORANGE,
|
|
13990
|
+
web: C.YELLOW,
|
|
13991
|
+
other: C.TEXT_MUTED
|
|
13992
|
+
};
|
|
13993
|
+
function _sourceColor(source) {
|
|
13994
|
+
const color = _SOURCE_COLORS[source];
|
|
13995
|
+
return color || C.TEXT_MUTED;
|
|
13996
|
+
}
|
|
13997
|
+
function _renderBySourceSection(stats) {
|
|
13998
|
+
if (!stats.by_source || stats.by_source.length === 0) {
|
|
13999
|
+
return [];
|
|
14000
|
+
}
|
|
14001
|
+
const lines = [..._sectionHeader("By source"), _tableHeader("source")];
|
|
14002
|
+
const { grossBytes, shareBytesDenom, shareTokensDenom } = _computeShareDenominators(stats.by_source);
|
|
14003
|
+
function share(s) {
|
|
14004
|
+
return _absShare(s.bytes, s.tokens, shareBytesDenom, shareTokensDenom);
|
|
14005
|
+
}
|
|
14006
|
+
for (const s of [...stats.by_source].sort((a, b) => share(b) - share(a))) {
|
|
14007
|
+
const s_val = share(s);
|
|
14008
|
+
const color = _sourceColor(s.source);
|
|
14009
|
+
lines.push(
|
|
14010
|
+
_tableRow({
|
|
14011
|
+
name: s.source,
|
|
14012
|
+
fraction: _barFraction(s.bytes, grossBytes),
|
|
14013
|
+
bytes: s.bytes,
|
|
14014
|
+
tokens: s.tokens,
|
|
14015
|
+
events: s.events,
|
|
14016
|
+
share: s_val,
|
|
14017
|
+
namePrefix: `${fg(...color)}\u25CF${RESET} `,
|
|
14018
|
+
nameColor: C.TEXT_PRIMARY
|
|
14019
|
+
})
|
|
14020
|
+
);
|
|
14021
|
+
}
|
|
14022
|
+
return lines;
|
|
14023
|
+
}
|
|
14024
|
+
function _renderByCommandSection(stats) {
|
|
14025
|
+
if (!stats.by_command || stats.by_command.length === 0) {
|
|
14026
|
+
return [];
|
|
14027
|
+
}
|
|
14028
|
+
const lines = [..._sectionHeader("By command"), _tableHeader("command")];
|
|
14029
|
+
const { grossBytes, shareBytesDenom, shareTokensDenom } = _computeShareDenominators(stats.by_command);
|
|
14030
|
+
function share(c) {
|
|
14031
|
+
return _absShare(c.bytes, c.tokens, shareBytesDenom, shareTokensDenom);
|
|
14032
|
+
}
|
|
14033
|
+
for (const c of [...stats.by_command].sort((a, b) => share(b) - share(a))) {
|
|
14034
|
+
const s_val = share(c);
|
|
14035
|
+
lines.push(
|
|
14036
|
+
_tableRow({
|
|
14037
|
+
name: c.command,
|
|
14038
|
+
fraction: _barFraction(c.bytes, grossBytes),
|
|
14039
|
+
bytes: c.bytes,
|
|
14040
|
+
tokens: c.tokens,
|
|
14041
|
+
events: c.events,
|
|
14042
|
+
share: s_val,
|
|
14043
|
+
nameColor: C.TEXT_PRIMARY
|
|
14044
|
+
})
|
|
14045
|
+
);
|
|
14046
|
+
}
|
|
14047
|
+
return lines;
|
|
14048
|
+
}
|
|
14049
|
+
function _renderByDaySection(stats) {
|
|
14050
|
+
if (stats.by_day.length === 0) {
|
|
14051
|
+
return [];
|
|
14052
|
+
}
|
|
14053
|
+
const lines = [..._sectionHeader("By day"), _tableHeader("date")];
|
|
14054
|
+
function share(d) {
|
|
14055
|
+
return _tokenOrByteShare(d.tokens, d.bytes, stats.totals.tokens, stats.totals.bytes);
|
|
14056
|
+
}
|
|
14057
|
+
for (const d of [...stats.by_day].sort((a, b) => b.date.localeCompare(a.date))) {
|
|
14058
|
+
const s = share(d);
|
|
14059
|
+
lines.push(
|
|
14060
|
+
_tableRow({
|
|
14061
|
+
name: d.date,
|
|
14062
|
+
fraction: s,
|
|
14063
|
+
bytes: d.bytes,
|
|
14064
|
+
tokens: d.tokens,
|
|
14065
|
+
events: d.events,
|
|
14066
|
+
share: s
|
|
14067
|
+
})
|
|
14068
|
+
);
|
|
14069
|
+
}
|
|
14070
|
+
return lines;
|
|
14071
|
+
}
|
|
14072
|
+
var _PROJECT_COLORS = [C.PURPLE, C.TEAL, C.BLUE, C.GREEN4, C.TEXT_MUTED];
|
|
14073
|
+
function _hashColor(hashStr) {
|
|
14074
|
+
let n = 0;
|
|
14075
|
+
for (const c of hashStr) {
|
|
14076
|
+
n += c.charCodeAt(0);
|
|
14077
|
+
}
|
|
14078
|
+
return _PROJECT_COLORS[n % _PROJECT_COLORS.length] ?? C.TEXT_MUTED;
|
|
14079
|
+
}
|
|
14080
|
+
function _renderByProjectSection(stats) {
|
|
14081
|
+
if (stats.by_project.length === 0) {
|
|
14082
|
+
return [];
|
|
14083
|
+
}
|
|
14084
|
+
const projectTotalBytes = stats.by_project.reduce((s, p) => s + p.bytes, 0);
|
|
14085
|
+
const projectTotalTokens = stats.by_project.reduce((s, p) => s + p.tokens, 0);
|
|
14086
|
+
const lines = [..._sectionHeader(`By project (top ${stats.by_project.length})`), _tableHeader("project")];
|
|
14087
|
+
function share(p) {
|
|
14088
|
+
return _tokenOrByteShare(p.tokens, p.bytes, projectTotalTokens, projectTotalBytes);
|
|
14089
|
+
}
|
|
14090
|
+
for (const p of [...stats.by_project].sort((a, b) => share(b) - share(a))) {
|
|
14091
|
+
const s = share(p);
|
|
14092
|
+
const color = _hashColor(p.hash);
|
|
14093
|
+
lines.push(
|
|
14094
|
+
_tableRow({
|
|
14095
|
+
name: p.project,
|
|
14096
|
+
fraction: s,
|
|
14097
|
+
bytes: p.bytes,
|
|
14098
|
+
tokens: p.tokens,
|
|
14099
|
+
events: p.events,
|
|
14100
|
+
share: s,
|
|
14101
|
+
namePrefix: `${fg(...color)}\u25CF${RESET} `,
|
|
14102
|
+
nameColor: C.TEXT_PRIMARY
|
|
14103
|
+
})
|
|
14104
|
+
);
|
|
14105
|
+
lines.push(`${_M} ${fg(...C.TEXT_DIM)}\u2514\u2500 ${p.hash} ${stripAnsi(p.path)}${RESET}`);
|
|
14106
|
+
}
|
|
14107
|
+
return lines;
|
|
14108
|
+
}
|
|
14109
|
+
function _renderInsightsSection(stats) {
|
|
14110
|
+
const lines = [..._sectionHeader("Insights")];
|
|
14111
|
+
const bullet = `${fg(...C.GREEN3)}\u25B8${RESET}`;
|
|
14112
|
+
function dim(s) {
|
|
14113
|
+
return `${fg(...C.TEXT_MUTED)}${s}${RESET}`;
|
|
14114
|
+
}
|
|
14115
|
+
const topKind = stats.by_kind.reduce((max, k) => k.bytes > (max?.bytes || -Infinity) ? k : max, stats.by_kind[0]);
|
|
14116
|
+
if (topKind) {
|
|
14117
|
+
const share = stats.totals.bytes > 0 ? topKind.bytes / stats.totals.bytes : 0;
|
|
14118
|
+
lines.push(
|
|
14119
|
+
`${_M}${bullet} ${dim(_STATS_MESSAGES.insights.biggestSaver)}${fg(...C.TEXT_PRIMARY)}${topKind.kind}${RESET}${dim(" \u2014 ")}${fg(...C.GREEN5)}${_fmtPct(share)}${RESET}${dim(` of saved data across ${topKind.events.toLocaleString()} events`)}`
|
|
14120
|
+
);
|
|
14121
|
+
}
|
|
14122
|
+
const topDay = stats.by_day.reduce((max, d) => d.events > (max?.events || -Infinity) ? d : max, stats.by_day[0]);
|
|
14123
|
+
if (topDay) {
|
|
14124
|
+
lines.push(
|
|
14125
|
+
`${_M}${bullet} ${dim(_STATS_MESSAGES.insights.mostActive)}${fg(...C.TEXT_PRIMARY)}${topDay.date}${RESET}${dim(" \u2014 ")}${topDay.events.toLocaleString()} events, ${_fmtBytes(topDay.bytes)}${dim(" saved")}`
|
|
14126
|
+
);
|
|
14127
|
+
}
|
|
14128
|
+
const tokenKinds = stats.by_kind.filter((k) => !k.bytes_mode_only);
|
|
14129
|
+
const topToken = tokenKinds.reduce((max, k) => k.tokens > (max?.tokens || -Infinity) ? k : max, tokenKinds[0]);
|
|
14130
|
+
if (topToken) {
|
|
14131
|
+
lines.push(
|
|
14132
|
+
`${_M}${bullet} ${dim(_STATS_MESSAGES.insights.tokenLeader)}${fg(...C.TEXT_PRIMARY)}${topToken.kind}${RESET}${dim(" \u2014 ")}${_fmtTokens(topToken.tokens)}${dim(` saved in ${topToken.events.toLocaleString()} events`)}`
|
|
14133
|
+
);
|
|
14134
|
+
}
|
|
14135
|
+
return lines;
|
|
14136
|
+
}
|
|
14137
|
+
function _renderHeader(stats) {
|
|
14138
|
+
let line = `${_M}${fg(...C.TEXT_BRIGHT)}token-goat${RESET}`;
|
|
14139
|
+
if (stats.version) {
|
|
14140
|
+
line += ` ${fg(...C.TEXT_MUTED)}v${stats.version}${RESET}`;
|
|
14141
|
+
}
|
|
14142
|
+
if (stats.window_label) {
|
|
14143
|
+
line += ` ${fg(...C.TEXT_DIM)}\xB7 ${stats.window_label}${RESET}`;
|
|
14144
|
+
}
|
|
14145
|
+
return [line];
|
|
14146
|
+
}
|
|
14147
|
+
function renderStats(stats) {
|
|
14148
|
+
const sections = [
|
|
14149
|
+
_renderHeader(stats),
|
|
14150
|
+
_renderKpiSection(stats),
|
|
14151
|
+
_renderByKindSection(stats),
|
|
14152
|
+
_renderBySourceSection(stats),
|
|
14153
|
+
_renderByCommandSection(stats),
|
|
14154
|
+
_renderByDaySection(stats),
|
|
14155
|
+
_renderByProjectSection(stats),
|
|
14156
|
+
_renderInsightsSection(stats),
|
|
14157
|
+
[""]
|
|
14158
|
+
];
|
|
14159
|
+
return sections.flatMap((s) => s).join("\n");
|
|
14160
|
+
}
|
|
14161
|
+
|
|
14162
|
+
// src/stats.ts
|
|
12687
14163
|
var SOURCE_IMAGE = "image";
|
|
12688
14164
|
var SOURCE_HINT = "hint";
|
|
12689
14165
|
var SOURCE_READ = "read";
|
|
@@ -12692,6 +14168,7 @@ var SOURCE_WEB = "web";
|
|
|
12692
14168
|
var SOURCE_MCP = "mcp";
|
|
12693
14169
|
var SOURCE_SKILL = "skill";
|
|
12694
14170
|
var SOURCE_OTHER = "other";
|
|
14171
|
+
var _BYTES_MODE_ONLY_KINDS = /* @__PURE__ */ new Set(["webfetch_image", "gdrive_image"]);
|
|
12695
14172
|
var KIND_TO_SOURCE = {
|
|
12696
14173
|
image_shrink: SOURCE_IMAGE,
|
|
12697
14174
|
image_shrink_cache_hit: SOURCE_IMAGE,
|
|
@@ -12734,7 +14211,15 @@ var COMMAND_KINDS = {
|
|
|
12734
14211
|
skeleton: /* @__PURE__ */ new Set(["stub_view"]),
|
|
12735
14212
|
refs: /* @__PURE__ */ new Set(["symbol_read"]),
|
|
12736
14213
|
map: /* @__PURE__ */ new Set(["map_lookup"]),
|
|
12737
|
-
changed: /* @__PURE__ */ new Set(["changed_lookup"])
|
|
14214
|
+
changed: /* @__PURE__ */ new Set(["changed_lookup"]),
|
|
14215
|
+
npm: /* @__PURE__ */ new Set([
|
|
14216
|
+
"bash_compress:npm_install",
|
|
14217
|
+
"bash_compress:npm_ci",
|
|
14218
|
+
"bash_compress:npm_audit",
|
|
14219
|
+
"bash_compress:npm_ls",
|
|
14220
|
+
"bash_compress:npm_outdated",
|
|
14221
|
+
"bash_compress:npx"
|
|
14222
|
+
])
|
|
12738
14223
|
};
|
|
12739
14224
|
var OVERHEAD_SUFFIX = "_overhead";
|
|
12740
14225
|
function kindToSource(kind) {
|
|
@@ -12759,7 +14244,7 @@ function incBucket(bucket, bytesSaved, tokensSaved) {
|
|
|
12759
14244
|
bucket.tokens_saved += tokensSaved;
|
|
12760
14245
|
}
|
|
12761
14246
|
function getGlobalDb() {
|
|
12762
|
-
const dbPath =
|
|
14247
|
+
const dbPath = path12.join(dataDir(), "global.db");
|
|
12763
14248
|
return getDb(dbPath);
|
|
12764
14249
|
}
|
|
12765
14250
|
function summarize(windowDays = 30, testDb) {
|
|
@@ -12833,12 +14318,7 @@ function summarize(windowDays = 30, testDb) {
|
|
|
12833
14318
|
window_days: windowDays
|
|
12834
14319
|
};
|
|
12835
14320
|
}
|
|
12836
|
-
function
|
|
12837
|
-
const summary = summarize(opts?.windowDays ?? 30);
|
|
12838
|
-
if (summary.total_events === 0) {
|
|
12839
|
-
console.log("No stats recorded yet.");
|
|
12840
|
-
return;
|
|
12841
|
-
}
|
|
14321
|
+
function _plainTextStats(summary) {
|
|
12842
14322
|
const fmtBytes = (n) => {
|
|
12843
14323
|
if (n < 1024) return `${n}B`;
|
|
12844
14324
|
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)}KB`;
|
|
@@ -12878,11 +14358,71 @@ function renderStats(opts) {
|
|
|
12878
14358
|
}
|
|
12879
14359
|
console.log(lines.join("\n"));
|
|
12880
14360
|
}
|
|
14361
|
+
function renderStats2(opts) {
|
|
14362
|
+
const windowDays = opts?.windowDays ?? 30;
|
|
14363
|
+
const summary = summarize(windowDays);
|
|
14364
|
+
if (summary.total_events === 0) {
|
|
14365
|
+
console.log("No stats recorded yet.");
|
|
14366
|
+
return;
|
|
14367
|
+
}
|
|
14368
|
+
const useTty = process.stdout.isTTY === true && !process.env["NO_COLOR"];
|
|
14369
|
+
if (!useTty) {
|
|
14370
|
+
_plainTextStats(summary);
|
|
14371
|
+
return;
|
|
14372
|
+
}
|
|
14373
|
+
const now = /* @__PURE__ */ new Date();
|
|
14374
|
+
const periodStart = windowDays > 0 ? new Date(now.getTime() - windowDays * 24 * 60 * 60 * 1e3) : /* @__PURE__ */ new Date(0);
|
|
14375
|
+
const sparkDays = [...summary.by_day].reverse().slice(-30);
|
|
14376
|
+
const sparklines = sparkDays.length > 1 ? {
|
|
14377
|
+
events: sparkDays.map((d) => d.events),
|
|
14378
|
+
bytes: sparkDays.map((d) => d.bytes_saved),
|
|
14379
|
+
tokens: sparkDays.map((d) => d.tokens_saved)
|
|
14380
|
+
} : null;
|
|
14381
|
+
const statsData = {
|
|
14382
|
+
period_start: periodStart,
|
|
14383
|
+
period_end: now,
|
|
14384
|
+
version: VERSION,
|
|
14385
|
+
window_label: windowDays > 0 ? `last ${windowDays} days` : "all time",
|
|
14386
|
+
totals: {
|
|
14387
|
+
events: summary.total_events,
|
|
14388
|
+
bytes: summary.total_bytes_saved,
|
|
14389
|
+
tokens: summary.total_tokens_saved,
|
|
14390
|
+
sparklines
|
|
14391
|
+
},
|
|
14392
|
+
by_kind: Object.entries(summary.by_kind).map(([kind, bucket]) => ({
|
|
14393
|
+
kind,
|
|
14394
|
+
bytes: bucket.bytes_saved,
|
|
14395
|
+
tokens: bucket.tokens_saved,
|
|
14396
|
+
events: bucket.events,
|
|
14397
|
+
bytes_mode_only: _BYTES_MODE_ONLY_KINDS.has(kind)
|
|
14398
|
+
})).sort((a, b) => b.bytes - a.bytes),
|
|
14399
|
+
by_day: summary.by_day.map((d) => ({
|
|
14400
|
+
date: d.date,
|
|
14401
|
+
bytes: d.bytes_saved,
|
|
14402
|
+
tokens: d.tokens_saved,
|
|
14403
|
+
events: d.events
|
|
14404
|
+
})),
|
|
14405
|
+
by_project: [],
|
|
14406
|
+
by_source: Object.entries(summary.by_source).filter(([, b]) => b.events > 0).map(([source, bucket]) => ({
|
|
14407
|
+
source,
|
|
14408
|
+
bytes: bucket.bytes_saved,
|
|
14409
|
+
tokens: bucket.tokens_saved,
|
|
14410
|
+
events: bucket.events
|
|
14411
|
+
})).sort((a, b) => b.bytes - a.bytes),
|
|
14412
|
+
by_command: summary.by_command.map((c) => ({
|
|
14413
|
+
command: c.command,
|
|
14414
|
+
bytes: c.bytes_saved,
|
|
14415
|
+
tokens: c.tokens_saved,
|
|
14416
|
+
events: c.events
|
|
14417
|
+
}))
|
|
14418
|
+
};
|
|
14419
|
+
process.stdout.write(renderStats(statsData) + "\n");
|
|
14420
|
+
}
|
|
12881
14421
|
|
|
12882
14422
|
// src/cli_doctor.ts
|
|
12883
14423
|
init_define_import_meta_env();
|
|
12884
|
-
import * as
|
|
12885
|
-
import * as
|
|
14424
|
+
import * as fs13 from "fs";
|
|
14425
|
+
import * as path13 from "path";
|
|
12886
14426
|
import { execSync } from "child_process";
|
|
12887
14427
|
function checkWorkerRunning() {
|
|
12888
14428
|
try {
|
|
@@ -12893,8 +14433,8 @@ function checkWorkerRunning() {
|
|
|
12893
14433
|
}
|
|
12894
14434
|
}
|
|
12895
14435
|
function checkDbExists(dataDir2) {
|
|
12896
|
-
const dbPath =
|
|
12897
|
-
if (!
|
|
14436
|
+
const dbPath = path13.join(dataDir2, "index.db");
|
|
14437
|
+
if (!fs13.existsSync(dbPath)) {
|
|
12898
14438
|
return {
|
|
12899
14439
|
name: "Database",
|
|
12900
14440
|
status: "warn",
|
|
@@ -12904,7 +14444,7 @@ function checkDbExists(dataDir2) {
|
|
|
12904
14444
|
return {
|
|
12905
14445
|
name: "Database",
|
|
12906
14446
|
status: "ok",
|
|
12907
|
-
message: `index.db exists (${Math.round(
|
|
14447
|
+
message: `index.db exists (${Math.round(fs13.statSync(dbPath).size / 1024)} KB)`
|
|
12908
14448
|
};
|
|
12909
14449
|
}
|
|
12910
14450
|
function checkInstall() {
|
|
@@ -12924,7 +14464,7 @@ function checkInstall() {
|
|
|
12924
14464
|
}
|
|
12925
14465
|
}
|
|
12926
14466
|
function checkConfigValid(configPath2) {
|
|
12927
|
-
if (!
|
|
14467
|
+
if (!fs13.existsSync(configPath2)) {
|
|
12928
14468
|
return {
|
|
12929
14469
|
name: "Config",
|
|
12930
14470
|
status: "warn",
|
|
@@ -12932,7 +14472,7 @@ function checkConfigValid(configPath2) {
|
|
|
12932
14472
|
};
|
|
12933
14473
|
}
|
|
12934
14474
|
try {
|
|
12935
|
-
const content =
|
|
14475
|
+
const content = fs13.readFileSync(configPath2, "utf-8");
|
|
12936
14476
|
JSON.parse(content);
|
|
12937
14477
|
return {
|
|
12938
14478
|
name: "Config",
|
|
@@ -12966,9 +14506,9 @@ function runDoctor(dataDir2, configPath2) {
|
|
|
12966
14506
|
results.push(checkInstall());
|
|
12967
14507
|
results.push(checkWorkerRunning() ? { name: "Worker", status: "ok", message: "running" } : { name: "Worker", status: "warn", message: "not running" });
|
|
12968
14508
|
const homeDir = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
12969
|
-
const actualDataDir = dataDir2 ||
|
|
14509
|
+
const actualDataDir = dataDir2 || path13.join(homeDir, ".token-goat");
|
|
12970
14510
|
results.push(checkDbExists(actualDataDir));
|
|
12971
|
-
const actualConfigPath = configPath2 ||
|
|
14511
|
+
const actualConfigPath = configPath2 || path13.join(actualDataDir, "config.json");
|
|
12972
14512
|
results.push(checkConfigValid(actualConfigPath));
|
|
12973
14513
|
results.push(checkDiskSpace(actualDataDir));
|
|
12974
14514
|
return results;
|
|
@@ -13258,7 +14798,7 @@ function cmdWorkerStatus() {
|
|
|
13258
14798
|
out(isWorkerRunning() ? "Worker is running." : "Worker is not running.");
|
|
13259
14799
|
}
|
|
13260
14800
|
function cmdStats() {
|
|
13261
|
-
|
|
14801
|
+
renderStats2({ windowDays: 30 });
|
|
13262
14802
|
}
|
|
13263
14803
|
function cmdDoctor() {
|
|
13264
14804
|
const code = runDoctorAndExit();
|
|
@@ -13266,14 +14806,12 @@ function cmdDoctor() {
|
|
|
13266
14806
|
throw new CliError("doctor checks failed");
|
|
13267
14807
|
}
|
|
13268
14808
|
}
|
|
13269
|
-
function
|
|
13270
|
-
const entry = getBashOutput(id);
|
|
13271
|
-
if (entry === null) {
|
|
13272
|
-
throw new CliError(`no cached bash output for id: ${id}`);
|
|
13273
|
-
}
|
|
13274
|
-
let content = entry.output;
|
|
14809
|
+
function _applyFiltersAndPrint(content, opts) {
|
|
13275
14810
|
if (opts.grep !== void 0) {
|
|
13276
|
-
|
|
14811
|
+
let pattern = opts.grep;
|
|
14812
|
+
if (pattern.startsWith("-E ") || pattern.startsWith("--extended-regexp ")) {
|
|
14813
|
+
pattern = pattern.replace(/^(?:-E\s+|--extended-regexp\s+)/, "");
|
|
14814
|
+
}
|
|
13277
14815
|
try {
|
|
13278
14816
|
const re = new RegExp(pattern);
|
|
13279
14817
|
content = content.split(/\r?\n/).filter((line) => re.test(line)).join("\n");
|
|
@@ -13294,12 +14832,32 @@ function cmdBashOutput(id, opts) {
|
|
|
13294
14832
|
}
|
|
13295
14833
|
out(result.join("\n"));
|
|
13296
14834
|
}
|
|
14835
|
+
function cmdBashOutput(id, opts) {
|
|
14836
|
+
if (opts.file !== void 0) {
|
|
14837
|
+
let content;
|
|
14838
|
+
try {
|
|
14839
|
+
content = fs14.readFileSync(opts.file, "utf-8");
|
|
14840
|
+
} catch {
|
|
14841
|
+
throw new CliError(`cannot read file: ${opts.file}`);
|
|
14842
|
+
}
|
|
14843
|
+
_applyFiltersAndPrint(content, opts);
|
|
14844
|
+
return;
|
|
14845
|
+
}
|
|
14846
|
+
if (id === void 0) {
|
|
14847
|
+
throw new CliError("provide an <id> or --file <path>");
|
|
14848
|
+
}
|
|
14849
|
+
const entry = getBashOutput(id);
|
|
14850
|
+
if (entry === null) {
|
|
14851
|
+
throw new CliError(`no cached bash output for id: ${id}`);
|
|
14852
|
+
}
|
|
14853
|
+
_applyFiltersAndPrint(entry.output, opts);
|
|
14854
|
+
}
|
|
13297
14855
|
async function cmdSkillBody(name, opts) {
|
|
13298
14856
|
const filePath = await getSkillFilePath(name);
|
|
13299
14857
|
if (filePath === null) {
|
|
13300
14858
|
throw new CliError(`skill '${name}' not found`);
|
|
13301
14859
|
}
|
|
13302
|
-
const body =
|
|
14860
|
+
const body = fs14.readFileSync(filePath, "utf-8");
|
|
13303
14861
|
if (opts.compact === true) {
|
|
13304
14862
|
const lines = body.split("\n");
|
|
13305
14863
|
const end = lines.findIndex((l) => l.includes("COMPACT_END"));
|
|
@@ -13317,7 +14875,7 @@ async function cmdSkillCompact(name) {
|
|
|
13317
14875
|
if (filePath === null) {
|
|
13318
14876
|
throw new CliError(`skill '${name}' not found`);
|
|
13319
14877
|
}
|
|
13320
|
-
const body =
|
|
14878
|
+
const body = fs14.readFileSync(filePath, "utf-8");
|
|
13321
14879
|
const sessionFiles = getSessionFiles();
|
|
13322
14880
|
const sessionId = Array.from(sessionFiles.keys())[0] ?? "default";
|
|
13323
14881
|
await storeCompact(sessionId, name, body);
|
|
@@ -13445,7 +15003,7 @@ function buildProgram() {
|
|
|
13445
15003
|
worker.command("status").description("check if the indexer is running").action(guard(cmdWorkerStatus));
|
|
13446
15004
|
program2.command("stats").description("show session statistics").action(guard(cmdStats));
|
|
13447
15005
|
program2.command("doctor").description("diagnose token-goat health").action(guard(cmdDoctor));
|
|
13448
|
-
program2.command("bash-output
|
|
15006
|
+
program2.command("bash-output [id]").description("retrieve cached bash output by ID or file").option("--head <n>", "show first N lines").option("--tail <n>", "show last N lines").option("--grep <pattern>", "filter lines matching regex").option("--file <path>", "read from raw output file instead of cache").action(guard(cmdBashOutput));
|
|
13449
15007
|
program2.command("skill-body <name>").description("retrieve a skill's cached body").option("-c, --compact", "print compact slice instead of full body").action(guard(cmdSkillBody));
|
|
13450
15008
|
program2.command("skill-compact <name>").description("regenerate and cache compact slice for a skill").action(guard(cmdSkillCompact));
|
|
13451
15009
|
program2.command("skill-list").description("list all cached skills with token counts").option("-j, --json", "output as JSON").option("--session-id <id>", "filter by session").action(guard(cmdSkillList));
|