context-compress 2026.3.3 → 2026.3.13
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 +14 -4
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +22 -1
- package/dist/cli/doctor.js.map +1 -1
- package/dist/config.d.ts +3 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +97 -4
- package/dist/config.js.map +1 -1
- package/dist/executor.d.ts +11 -0
- package/dist/executor.d.ts.map +1 -1
- package/dist/executor.js +104 -3
- package/dist/executor.js.map +1 -1
- package/dist/hooks/pretooluse.js +12 -6
- package/dist/hooks/pretooluse.js.map +1 -1
- package/dist/network.d.ts +5 -0
- package/dist/network.d.ts.map +1 -0
- package/dist/network.js +42 -0
- package/dist/network.js.map +1 -0
- package/dist/runtime/languages/go.js +3 -3
- package/dist/runtime/languages/go.js.map +1 -1
- package/dist/runtime/languages/javascript.js +1 -1
- package/dist/runtime/languages/javascript.js.map +1 -1
- package/dist/runtime/languages/r.js +1 -1
- package/dist/runtime/languages/rust.js +2 -2
- package/dist/runtime/languages/rust.js.map +1 -1
- package/dist/runtime/languages/typescript.js +1 -1
- package/dist/runtime/languages/typescript.js.map +1 -1
- package/dist/server.bundle.mjs +460 -120
- package/dist/server.bundle.mjs.map +4 -4
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +112 -31
- package/dist/server.js.map +1 -1
- package/dist/stats.d.ts.map +1 -1
- package/dist/stats.js +27 -4
- package/dist/stats.js.map +1 -1
- package/dist/store.d.ts +14 -0
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +41 -10
- package/dist/store.js.map +1 -1
- package/hooks/pretooluse.mjs +10 -5
- package/package.json +6 -4
package/dist/server.bundle.mjs
CHANGED
|
@@ -6800,85 +6800,8 @@ var require_dist = __commonJS({
|
|
|
6800
6800
|
|
|
6801
6801
|
// src/config.ts
|
|
6802
6802
|
import { readFileSync } from "node:fs";
|
|
6803
|
+
import { homedir } from "node:os";
|
|
6803
6804
|
import { join } from "node:path";
|
|
6804
|
-
var DEFAULTS = {
|
|
6805
|
-
passthroughEnvVars: [],
|
|
6806
|
-
debug: false,
|
|
6807
|
-
blockCurl: true,
|
|
6808
|
-
blockWebFetch: true,
|
|
6809
|
-
nudgeOnRead: true,
|
|
6810
|
-
nudgeOnGrep: true,
|
|
6811
|
-
intentSearchThreshold: 5e3,
|
|
6812
|
-
maxOutputBytes: 102400,
|
|
6813
|
-
hardCapBytes: 100 * 1024 * 1024,
|
|
6814
|
-
searchMaxBytes: 40960,
|
|
6815
|
-
batchMaxBytes: 81920,
|
|
6816
|
-
searchLimit: 3,
|
|
6817
|
-
searchWindowMs: 6e4,
|
|
6818
|
-
searchReduceAfter: 3,
|
|
6819
|
-
searchBlockAfter: 8
|
|
6820
|
-
};
|
|
6821
|
-
function loadFileConfig(projectDir2) {
|
|
6822
|
-
const paths = [
|
|
6823
|
-
projectDir2 && join(projectDir2, ".context-compress.json"),
|
|
6824
|
-
join(process.env.HOME ?? "~", ".context-compress.json")
|
|
6825
|
-
].filter(Boolean);
|
|
6826
|
-
for (const p of paths) {
|
|
6827
|
-
try {
|
|
6828
|
-
const raw = readFileSync(p, "utf-8");
|
|
6829
|
-
return JSON.parse(raw);
|
|
6830
|
-
} catch {
|
|
6831
|
-
}
|
|
6832
|
-
}
|
|
6833
|
-
return {};
|
|
6834
|
-
}
|
|
6835
|
-
function loadEnvConfig() {
|
|
6836
|
-
const partial2 = {};
|
|
6837
|
-
if (process.env.CONTEXT_COMPRESS_DEBUG === "1") {
|
|
6838
|
-
partial2.debug = true;
|
|
6839
|
-
}
|
|
6840
|
-
if (process.env.CONTEXT_COMPRESS_PASSTHROUGH_ENV) {
|
|
6841
|
-
partial2.passthroughEnvVars = process.env.CONTEXT_COMPRESS_PASSTHROUGH_ENV.split(",").map((s) => s.trim()).filter(Boolean);
|
|
6842
|
-
}
|
|
6843
|
-
if (process.env.CONTEXT_COMPRESS_BLOCK_CURL !== void 0) {
|
|
6844
|
-
partial2.blockCurl = process.env.CONTEXT_COMPRESS_BLOCK_CURL !== "0";
|
|
6845
|
-
}
|
|
6846
|
-
if (process.env.CONTEXT_COMPRESS_BLOCK_WEBFETCH !== void 0) {
|
|
6847
|
-
partial2.blockWebFetch = process.env.CONTEXT_COMPRESS_BLOCK_WEBFETCH !== "0";
|
|
6848
|
-
}
|
|
6849
|
-
if (process.env.CONTEXT_COMPRESS_NUDGE_READ !== void 0) {
|
|
6850
|
-
partial2.nudgeOnRead = process.env.CONTEXT_COMPRESS_NUDGE_READ !== "0";
|
|
6851
|
-
}
|
|
6852
|
-
if (process.env.CONTEXT_COMPRESS_NUDGE_GREP !== void 0) {
|
|
6853
|
-
partial2.nudgeOnGrep = process.env.CONTEXT_COMPRESS_NUDGE_GREP !== "0";
|
|
6854
|
-
}
|
|
6855
|
-
return partial2;
|
|
6856
|
-
}
|
|
6857
|
-
var _config = null;
|
|
6858
|
-
function loadConfig(projectDir2) {
|
|
6859
|
-
if (_config) return _config;
|
|
6860
|
-
const fileConfig = loadFileConfig(projectDir2);
|
|
6861
|
-
const envConfig = loadEnvConfig();
|
|
6862
|
-
_config = { ...DEFAULTS, ...fileConfig, ...envConfig };
|
|
6863
|
-
return _config;
|
|
6864
|
-
}
|
|
6865
|
-
function getConfig() {
|
|
6866
|
-
if (!_config) return loadConfig();
|
|
6867
|
-
return _config;
|
|
6868
|
-
}
|
|
6869
|
-
|
|
6870
|
-
// src/logger.ts
|
|
6871
|
-
function debug(...args) {
|
|
6872
|
-
if (getConfig().debug) {
|
|
6873
|
-
process.stderr.write(`[context-compress] ${args.map(String).join(" ")}
|
|
6874
|
-
`);
|
|
6875
|
-
}
|
|
6876
|
-
}
|
|
6877
|
-
|
|
6878
|
-
// src/server.ts
|
|
6879
|
-
import { readFileSync as readFileSync2 } from "node:fs";
|
|
6880
|
-
import { dirname, join as join4, resolve } from "node:path";
|
|
6881
|
-
import { fileURLToPath } from "node:url";
|
|
6882
6805
|
|
|
6883
6806
|
// node_modules/zod/v3/external.js
|
|
6884
6807
|
var external_exports = {};
|
|
@@ -10921,6 +10844,163 @@ var coerce = {
|
|
|
10921
10844
|
};
|
|
10922
10845
|
var NEVER = INVALID;
|
|
10923
10846
|
|
|
10847
|
+
// src/config.ts
|
|
10848
|
+
var DEFAULTS = {
|
|
10849
|
+
passthroughEnvVars: [],
|
|
10850
|
+
debug: false,
|
|
10851
|
+
blockCurl: true,
|
|
10852
|
+
blockWebFetch: true,
|
|
10853
|
+
nudgeOnRead: true,
|
|
10854
|
+
nudgeOnGrep: true,
|
|
10855
|
+
intentSearchThreshold: 5e3,
|
|
10856
|
+
maxOutputBytes: 102400,
|
|
10857
|
+
hardCapBytes: 100 * 1024 * 1024,
|
|
10858
|
+
searchMaxBytes: 40960,
|
|
10859
|
+
batchMaxBytes: 81920,
|
|
10860
|
+
searchLimit: 3,
|
|
10861
|
+
searchWindowMs: 6e4,
|
|
10862
|
+
searchReduceAfter: 3,
|
|
10863
|
+
searchBlockAfter: 8,
|
|
10864
|
+
compressionLevel: "normal"
|
|
10865
|
+
};
|
|
10866
|
+
var LEVEL_OVERRIDES = {
|
|
10867
|
+
normal: {},
|
|
10868
|
+
compact: {
|
|
10869
|
+
maxOutputBytes: 51200,
|
|
10870
|
+
searchMaxBytes: 20480,
|
|
10871
|
+
batchMaxBytes: 40960,
|
|
10872
|
+
searchLimit: 2,
|
|
10873
|
+
intentSearchThreshold: 3e3
|
|
10874
|
+
},
|
|
10875
|
+
ultra: {
|
|
10876
|
+
maxOutputBytes: 25600,
|
|
10877
|
+
searchMaxBytes: 10240,
|
|
10878
|
+
batchMaxBytes: 20480,
|
|
10879
|
+
searchLimit: 1,
|
|
10880
|
+
intentSearchThreshold: 2e3
|
|
10881
|
+
}
|
|
10882
|
+
};
|
|
10883
|
+
var ConfigSchema = external_exports.object({
|
|
10884
|
+
passthroughEnvVars: external_exports.array(external_exports.string()).optional(),
|
|
10885
|
+
debug: external_exports.boolean().optional(),
|
|
10886
|
+
blockCurl: external_exports.boolean().optional(),
|
|
10887
|
+
blockWebFetch: external_exports.boolean().optional(),
|
|
10888
|
+
nudgeOnRead: external_exports.boolean().optional(),
|
|
10889
|
+
nudgeOnGrep: external_exports.boolean().optional(),
|
|
10890
|
+
intentSearchThreshold: external_exports.number().int().positive().optional(),
|
|
10891
|
+
maxOutputBytes: external_exports.number().int().positive().optional(),
|
|
10892
|
+
hardCapBytes: external_exports.number().int().positive().optional(),
|
|
10893
|
+
searchMaxBytes: external_exports.number().int().positive().optional(),
|
|
10894
|
+
batchMaxBytes: external_exports.number().int().positive().optional(),
|
|
10895
|
+
searchLimit: external_exports.number().int().positive().optional(),
|
|
10896
|
+
searchWindowMs: external_exports.number().int().positive().optional(),
|
|
10897
|
+
searchReduceAfter: external_exports.number().int().nonnegative().optional(),
|
|
10898
|
+
searchBlockAfter: external_exports.number().int().positive().optional(),
|
|
10899
|
+
compressionLevel: external_exports.enum(["normal", "compact", "ultra"]).optional()
|
|
10900
|
+
});
|
|
10901
|
+
function parseIntEnv(key) {
|
|
10902
|
+
const val = process.env[key];
|
|
10903
|
+
if (val === void 0) return void 0;
|
|
10904
|
+
const n = Number.parseInt(val, 10);
|
|
10905
|
+
return Number.isNaN(n) ? void 0 : n;
|
|
10906
|
+
}
|
|
10907
|
+
function loadFileConfig(projectDir2) {
|
|
10908
|
+
const paths = [
|
|
10909
|
+
projectDir2 && join(projectDir2, ".context-compress.json"),
|
|
10910
|
+
join(homedir(), ".context-compress.json")
|
|
10911
|
+
].filter(Boolean);
|
|
10912
|
+
for (const p of paths) {
|
|
10913
|
+
try {
|
|
10914
|
+
const raw = readFileSync(p, "utf-8");
|
|
10915
|
+
const parsed = JSON.parse(raw);
|
|
10916
|
+
const result = ConfigSchema.safeParse(parsed);
|
|
10917
|
+
if (result.success) {
|
|
10918
|
+
return result.data;
|
|
10919
|
+
}
|
|
10920
|
+
return {};
|
|
10921
|
+
} catch {
|
|
10922
|
+
}
|
|
10923
|
+
}
|
|
10924
|
+
return {};
|
|
10925
|
+
}
|
|
10926
|
+
function loadEnvConfig() {
|
|
10927
|
+
const partial2 = {};
|
|
10928
|
+
if (process.env.CONTEXT_COMPRESS_DEBUG === "1") {
|
|
10929
|
+
partial2.debug = true;
|
|
10930
|
+
}
|
|
10931
|
+
if (process.env.CONTEXT_COMPRESS_PASSTHROUGH_ENV) {
|
|
10932
|
+
partial2.passthroughEnvVars = process.env.CONTEXT_COMPRESS_PASSTHROUGH_ENV.split(",").map((s) => s.trim()).filter(Boolean);
|
|
10933
|
+
}
|
|
10934
|
+
if (process.env.CONTEXT_COMPRESS_BLOCK_CURL !== void 0) {
|
|
10935
|
+
partial2.blockCurl = process.env.CONTEXT_COMPRESS_BLOCK_CURL !== "0";
|
|
10936
|
+
}
|
|
10937
|
+
if (process.env.CONTEXT_COMPRESS_BLOCK_WEBFETCH !== void 0) {
|
|
10938
|
+
partial2.blockWebFetch = process.env.CONTEXT_COMPRESS_BLOCK_WEBFETCH !== "0";
|
|
10939
|
+
}
|
|
10940
|
+
if (process.env.CONTEXT_COMPRESS_NUDGE_READ !== void 0) {
|
|
10941
|
+
partial2.nudgeOnRead = process.env.CONTEXT_COMPRESS_NUDGE_READ !== "0";
|
|
10942
|
+
}
|
|
10943
|
+
if (process.env.CONTEXT_COMPRESS_NUDGE_GREP !== void 0) {
|
|
10944
|
+
partial2.nudgeOnGrep = process.env.CONTEXT_COMPRESS_NUDGE_GREP !== "0";
|
|
10945
|
+
}
|
|
10946
|
+
const maxOutput = parseIntEnv("CONTEXT_COMPRESS_MAX_OUTPUT_BYTES");
|
|
10947
|
+
if (maxOutput !== void 0) partial2.maxOutputBytes = maxOutput;
|
|
10948
|
+
const hardCap = parseIntEnv("CONTEXT_COMPRESS_HARD_CAP_BYTES");
|
|
10949
|
+
if (hardCap !== void 0) partial2.hardCapBytes = hardCap;
|
|
10950
|
+
const searchMax = parseIntEnv("CONTEXT_COMPRESS_SEARCH_MAX_BYTES");
|
|
10951
|
+
if (searchMax !== void 0) partial2.searchMaxBytes = searchMax;
|
|
10952
|
+
const batchMax = parseIntEnv("CONTEXT_COMPRESS_BATCH_MAX_BYTES");
|
|
10953
|
+
if (batchMax !== void 0) partial2.batchMaxBytes = batchMax;
|
|
10954
|
+
const searchLimit = parseIntEnv("CONTEXT_COMPRESS_SEARCH_LIMIT");
|
|
10955
|
+
if (searchLimit !== void 0) partial2.searchLimit = searchLimit;
|
|
10956
|
+
const searchWindow = parseIntEnv("CONTEXT_COMPRESS_SEARCH_WINDOW_MS");
|
|
10957
|
+
if (searchWindow !== void 0) partial2.searchWindowMs = searchWindow;
|
|
10958
|
+
const searchReduce = parseIntEnv("CONTEXT_COMPRESS_SEARCH_REDUCE_AFTER");
|
|
10959
|
+
if (searchReduce !== void 0) partial2.searchReduceAfter = searchReduce;
|
|
10960
|
+
const searchBlock = parseIntEnv("CONTEXT_COMPRESS_SEARCH_BLOCK_AFTER");
|
|
10961
|
+
if (searchBlock !== void 0) partial2.searchBlockAfter = searchBlock;
|
|
10962
|
+
const intentThreshold = parseIntEnv("CONTEXT_COMPRESS_INTENT_SEARCH_THRESHOLD");
|
|
10963
|
+
if (intentThreshold !== void 0) partial2.intentSearchThreshold = intentThreshold;
|
|
10964
|
+
const level = process.env.CONTEXT_COMPRESS_LEVEL;
|
|
10965
|
+
if (level === "normal" || level === "compact" || level === "ultra") {
|
|
10966
|
+
partial2.compressionLevel = level;
|
|
10967
|
+
}
|
|
10968
|
+
return partial2;
|
|
10969
|
+
}
|
|
10970
|
+
var _config = null;
|
|
10971
|
+
function loadConfig(projectDir2) {
|
|
10972
|
+
if (_config) return _config;
|
|
10973
|
+
const fileConfig = loadFileConfig(projectDir2);
|
|
10974
|
+
const envConfig = loadEnvConfig();
|
|
10975
|
+
const merged = { ...DEFAULTS, ...fileConfig, ...envConfig };
|
|
10976
|
+
const levelOverrides = LEVEL_OVERRIDES[merged.compressionLevel];
|
|
10977
|
+
for (const [key, value] of Object.entries(levelOverrides)) {
|
|
10978
|
+
const k = key;
|
|
10979
|
+
if (!(k in fileConfig) && !(k in envConfig)) {
|
|
10980
|
+
merged[k] = value;
|
|
10981
|
+
}
|
|
10982
|
+
}
|
|
10983
|
+
_config = merged;
|
|
10984
|
+
return _config;
|
|
10985
|
+
}
|
|
10986
|
+
function getConfig() {
|
|
10987
|
+
if (!_config) return loadConfig();
|
|
10988
|
+
return _config;
|
|
10989
|
+
}
|
|
10990
|
+
|
|
10991
|
+
// src/logger.ts
|
|
10992
|
+
function debug(...args) {
|
|
10993
|
+
if (getConfig().debug) {
|
|
10994
|
+
process.stderr.write(`[context-compress] ${args.map(String).join(" ")}
|
|
10995
|
+
`);
|
|
10996
|
+
}
|
|
10997
|
+
}
|
|
10998
|
+
|
|
10999
|
+
// src/server.ts
|
|
11000
|
+
import { readFileSync as readFileSync2, statSync } from "node:fs";
|
|
11001
|
+
import { dirname, join as join4, resolve } from "node:path";
|
|
11002
|
+
import { fileURLToPath } from "node:url";
|
|
11003
|
+
|
|
10924
11004
|
// node_modules/zod/v4/core/core.js
|
|
10925
11005
|
var NEVER2 = Object.freeze({
|
|
10926
11006
|
status: "aborted"
|
|
@@ -21073,7 +21153,6 @@ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
|
21073
21153
|
import { tmpdir } from "node:os";
|
|
21074
21154
|
import { join as join2 } from "node:path";
|
|
21075
21155
|
var DEFAULT_TIMEOUT = 3e4;
|
|
21076
|
-
var DEFAULT_HARD_CAP = 100 * 1024 * 1024;
|
|
21077
21156
|
var SAFE_ENV_KEYS = [
|
|
21078
21157
|
"PATH",
|
|
21079
21158
|
"HOME",
|
|
@@ -21126,6 +21205,86 @@ function killProcessTree(pid) {
|
|
|
21126
21205
|
}
|
|
21127
21206
|
}
|
|
21128
21207
|
}
|
|
21208
|
+
function deduplicateLines(output) {
|
|
21209
|
+
const lines = output.split("\n");
|
|
21210
|
+
if (lines.length < 3) return output;
|
|
21211
|
+
const result = [];
|
|
21212
|
+
let prevLine = lines[0];
|
|
21213
|
+
let count = 1;
|
|
21214
|
+
for (let i = 1; i < lines.length; i++) {
|
|
21215
|
+
if (lines[i] === prevLine && prevLine.trim().length > 0) {
|
|
21216
|
+
count++;
|
|
21217
|
+
} else {
|
|
21218
|
+
if (count > 2) {
|
|
21219
|
+
result.push(prevLine);
|
|
21220
|
+
result.push(` ... (\xD7${count} identical lines)`);
|
|
21221
|
+
} else {
|
|
21222
|
+
for (let j = 0; j < count; j++) result.push(prevLine);
|
|
21223
|
+
}
|
|
21224
|
+
prevLine = lines[i];
|
|
21225
|
+
count = 1;
|
|
21226
|
+
}
|
|
21227
|
+
}
|
|
21228
|
+
if (count > 2) {
|
|
21229
|
+
result.push(prevLine);
|
|
21230
|
+
result.push(` ... (\xD7${count} identical lines)`);
|
|
21231
|
+
} else {
|
|
21232
|
+
for (let j = 0; j < count; j++) result.push(prevLine);
|
|
21233
|
+
}
|
|
21234
|
+
return result.join("\n");
|
|
21235
|
+
}
|
|
21236
|
+
function groupErrorLines(output) {
|
|
21237
|
+
const lines = output.split("\n");
|
|
21238
|
+
if (lines.length < 5) return output;
|
|
21239
|
+
const ERROR_RE = /^(.*?(?:error|warning|Error|Warning|ERR|WARN)[:\s])\s*(.+?)(?:\s+(?:at|in|on)\s+(?:line\s+)?(\d+))?$/i;
|
|
21240
|
+
const errorGroups = /* @__PURE__ */ new Map();
|
|
21241
|
+
const resultLines = [];
|
|
21242
|
+
let groupedCount = 0;
|
|
21243
|
+
for (const line of lines) {
|
|
21244
|
+
const match = line.match(ERROR_RE);
|
|
21245
|
+
if (match) {
|
|
21246
|
+
const prefix = match[1].trim();
|
|
21247
|
+
const msg = match[2].trim();
|
|
21248
|
+
const key = `${prefix}|${msg}`;
|
|
21249
|
+
const existing = errorGroups.get(key);
|
|
21250
|
+
if (existing) {
|
|
21251
|
+
existing.count++;
|
|
21252
|
+
if (match[3]) existing.locations.push(match[3]);
|
|
21253
|
+
groupedCount++;
|
|
21254
|
+
continue;
|
|
21255
|
+
}
|
|
21256
|
+
errorGroups.set(key, {
|
|
21257
|
+
message: `${prefix} ${msg}`,
|
|
21258
|
+
locations: match[3] ? [match[3]] : [],
|
|
21259
|
+
count: 1
|
|
21260
|
+
});
|
|
21261
|
+
groupedCount++;
|
|
21262
|
+
continue;
|
|
21263
|
+
}
|
|
21264
|
+
resultLines.push(line);
|
|
21265
|
+
}
|
|
21266
|
+
if (groupedCount < 4 || errorGroups.size === groupedCount) return output;
|
|
21267
|
+
const grouped = [];
|
|
21268
|
+
for (const [, group] of errorGroups) {
|
|
21269
|
+
if (group.count === 1) {
|
|
21270
|
+
grouped.push(
|
|
21271
|
+
group.message + (group.locations.length ? ` at line ${group.locations[0]}` : "")
|
|
21272
|
+
);
|
|
21273
|
+
} else {
|
|
21274
|
+
let line = `${group.message} (\xD7${group.count})`;
|
|
21275
|
+
if (group.locations.length > 0) {
|
|
21276
|
+
line += ` [lines: ${group.locations.join(", ")}]`;
|
|
21277
|
+
}
|
|
21278
|
+
grouped.push(line);
|
|
21279
|
+
}
|
|
21280
|
+
}
|
|
21281
|
+
if (grouped.length > 0) {
|
|
21282
|
+
resultLines.push("");
|
|
21283
|
+
resultLines.push(`\u2500\u2500 Grouped errors/warnings (${groupedCount} \u2192 ${errorGroups.size}) \u2500\u2500`);
|
|
21284
|
+
resultLines.push(...grouped);
|
|
21285
|
+
}
|
|
21286
|
+
return resultLines.join("\n");
|
|
21287
|
+
}
|
|
21129
21288
|
function smartTruncate(output, maxBytes) {
|
|
21130
21289
|
if (Buffer.byteLength(output) <= maxBytes) return output;
|
|
21131
21290
|
const lines = output.split("\n");
|
|
@@ -21231,7 +21390,7 @@ var SubprocessExecutor = class {
|
|
|
21231
21390
|
plugin.needsShell
|
|
21232
21391
|
);
|
|
21233
21392
|
} finally {
|
|
21234
|
-
setTimeout(() => this.cleanupTempDir(tmpDir), 100);
|
|
21393
|
+
setTimeout(() => this.cleanupTempDir(tmpDir), 100).unref();
|
|
21235
21394
|
}
|
|
21236
21395
|
}
|
|
21237
21396
|
/**
|
|
@@ -21317,6 +21476,8 @@ var SubprocessExecutor = class {
|
|
|
21317
21476
|
stdout += `
|
|
21318
21477
|
[output capped at ${formatBytes(hardCap)} \u2014 process killed]`;
|
|
21319
21478
|
}
|
|
21479
|
+
stdout = deduplicateLines(stdout);
|
|
21480
|
+
stdout = groupErrorLines(stdout);
|
|
21320
21481
|
const truncated = Buffer.byteLength(stdout) > maxOutput;
|
|
21321
21482
|
if (truncated) {
|
|
21322
21483
|
stdout = smartTruncate(stdout, maxOutput);
|
|
@@ -21354,6 +21515,25 @@ async function __cm_main(){${code}}
|
|
|
21354
21515
|
__cm_main().then(()=>{${epilogue}}).catch(e=>{console.error(e);${epilogue}process.exit(1)});`;
|
|
21355
21516
|
}
|
|
21356
21517
|
|
|
21518
|
+
// src/network.ts
|
|
21519
|
+
function isPrivateHost(hostname2) {
|
|
21520
|
+
const h = hostname2.startsWith("[") && hostname2.endsWith("]") ? hostname2.slice(1, -1) : hostname2;
|
|
21521
|
+
const lower = h.toLowerCase();
|
|
21522
|
+
if (lower === "localhost" || lower === "0.0.0.0") return true;
|
|
21523
|
+
if (/^127\./.test(h)) return true;
|
|
21524
|
+
if (/^10\./.test(h)) return true;
|
|
21525
|
+
if (/^172\.(1[6-9]|2\d|3[01])\./.test(h)) return true;
|
|
21526
|
+
if (/^192\.168\./.test(h)) return true;
|
|
21527
|
+
if (/^169\.254\./.test(h)) return true;
|
|
21528
|
+
if (/^100\.(6[4-9]|[7-9]\d|1[01]\d|12[0-7])\./.test(h)) return true;
|
|
21529
|
+
if (lower === "::1") return true;
|
|
21530
|
+
const mappedMatch = lower.match(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/);
|
|
21531
|
+
if (mappedMatch) return isPrivateHost(mappedMatch[1]);
|
|
21532
|
+
if (/^fe[89ab]/i.test(h)) return true;
|
|
21533
|
+
if (/^f[cd]/i.test(h)) return true;
|
|
21534
|
+
return false;
|
|
21535
|
+
}
|
|
21536
|
+
|
|
21357
21537
|
// src/runtime/index.ts
|
|
21358
21538
|
import { exec } from "node:child_process";
|
|
21359
21539
|
import { promisify } from "node:util";
|
|
@@ -21385,8 +21565,8 @@ var goPlugin = {
|
|
|
21385
21565
|
return [runtime, "run", filePath];
|
|
21386
21566
|
},
|
|
21387
21567
|
preprocessCode(code) {
|
|
21388
|
-
if (
|
|
21389
|
-
const hasImport =
|
|
21568
|
+
if (!/^package\s/m.test(code)) {
|
|
21569
|
+
const hasImport = /^import\s/m.test(code);
|
|
21390
21570
|
if (hasImport) {
|
|
21391
21571
|
return `package main
|
|
21392
21572
|
|
|
@@ -21405,7 +21585,7 @@ _ = fmt.Sprintf("")
|
|
|
21405
21585
|
},
|
|
21406
21586
|
wrapWithFileContent(code, filePath) {
|
|
21407
21587
|
const escaped = JSON.stringify(filePath);
|
|
21408
|
-
const hasPackage =
|
|
21588
|
+
const hasPackage = /^package\s/m.test(code);
|
|
21409
21589
|
if (hasPackage) {
|
|
21410
21590
|
return code.replace(
|
|
21411
21591
|
/(package\s+\w+\n)/,
|
|
@@ -21446,8 +21626,9 @@ var javascriptPlugin = {
|
|
|
21446
21626
|
},
|
|
21447
21627
|
wrapWithFileContent(code, filePath) {
|
|
21448
21628
|
const escaped = JSON.stringify(filePath);
|
|
21449
|
-
return `const
|
|
21450
|
-
const
|
|
21629
|
+
return `const {readFileSync: __cm_readFileSync} = await import("node:fs");
|
|
21630
|
+
const FILE_CONTENT_PATH = ${escaped};
|
|
21631
|
+
const FILE_CONTENT = __cm_readFileSync(FILE_CONTENT_PATH, "utf-8");
|
|
21451
21632
|
${code}`;
|
|
21452
21633
|
}
|
|
21453
21634
|
};
|
|
@@ -21519,7 +21700,7 @@ ${code}`;
|
|
|
21519
21700
|
// src/runtime/languages/r.ts
|
|
21520
21701
|
var rPlugin = {
|
|
21521
21702
|
language: "r",
|
|
21522
|
-
runtimeCandidates: ["Rscript", "
|
|
21703
|
+
runtimeCandidates: ["Rscript", "R"],
|
|
21523
21704
|
fileExtension: ".R",
|
|
21524
21705
|
buildCommand(runtime, filePath) {
|
|
21525
21706
|
return [runtime, filePath];
|
|
@@ -21562,7 +21743,7 @@ var rustPlugin = {
|
|
|
21562
21743
|
return [runtime, srcPath, "-o", binPath];
|
|
21563
21744
|
},
|
|
21564
21745
|
preprocessCode(code) {
|
|
21565
|
-
if (
|
|
21746
|
+
if (!/^fn\s+main\s*\(/m.test(code)) {
|
|
21566
21747
|
return `fn main() {
|
|
21567
21748
|
${code}
|
|
21568
21749
|
}`;
|
|
@@ -21575,7 +21756,7 @@ ${code}
|
|
|
21575
21756
|
let file_content_path = ${escaped};
|
|
21576
21757
|
let file_content = fs::read_to_string(file_content_path).unwrap();
|
|
21577
21758
|
`;
|
|
21578
|
-
if (
|
|
21759
|
+
if (/^fn\s+main\s*\(/m.test(code)) {
|
|
21579
21760
|
return code.replace(/fn main\s*\(\s*\)\s*\{/, `fn main() {
|
|
21580
21761
|
${preamble}`);
|
|
21581
21762
|
}
|
|
@@ -21611,8 +21792,9 @@ var typescriptPlugin = {
|
|
|
21611
21792
|
},
|
|
21612
21793
|
wrapWithFileContent(code, filePath) {
|
|
21613
21794
|
const escaped = JSON.stringify(filePath);
|
|
21614
|
-
return `const
|
|
21615
|
-
const
|
|
21795
|
+
return `const {readFileSync: __cm_readFileSync} = await import("node:fs");
|
|
21796
|
+
const FILE_CONTENT_PATH = ${escaped};
|
|
21797
|
+
const FILE_CONTENT = __cm_readFileSync(FILE_CONTENT_PATH, "utf-8");
|
|
21616
21798
|
${code}`;
|
|
21617
21799
|
},
|
|
21618
21800
|
// tsx and ts-node may be .cmd shims on Windows
|
|
@@ -21668,6 +21850,16 @@ function hasBun(runtimes) {
|
|
|
21668
21850
|
}
|
|
21669
21851
|
|
|
21670
21852
|
// src/stats.ts
|
|
21853
|
+
var BAR_WIDTH = 20;
|
|
21854
|
+
function asciiBar(ratio, width = BAR_WIDTH) {
|
|
21855
|
+
const filled = Math.round(ratio * width);
|
|
21856
|
+
const empty = width - filled;
|
|
21857
|
+
return `[${"\u2588".repeat(filled)}${"\u2591".repeat(empty)}] ${(ratio * 100).toFixed(0)}%`;
|
|
21858
|
+
}
|
|
21859
|
+
function tokenCost(tokens) {
|
|
21860
|
+
const cost = tokens / 1e6 * 3;
|
|
21861
|
+
return cost >= 0.01 ? `~$${cost.toFixed(2)}` : "<$0.01";
|
|
21862
|
+
}
|
|
21671
21863
|
var SessionTracker = class {
|
|
21672
21864
|
stats = {
|
|
21673
21865
|
calls: {},
|
|
@@ -21701,6 +21893,7 @@ var SessionTracker = class {
|
|
|
21701
21893
|
const savingsRatio = totalReturned > 0 ? totalProcessed / totalReturned : 1;
|
|
21702
21894
|
const reductionPct = totalProcessed > 0 ? ((1 - totalReturned / totalProcessed) * 100).toFixed(1) : "0.0";
|
|
21703
21895
|
const estTokens = Math.round(totalReturned / 4);
|
|
21896
|
+
const estTokensAvoided = Math.round(keptOut / 4);
|
|
21704
21897
|
const lines = [];
|
|
21705
21898
|
lines.push("## Session Statistics\n");
|
|
21706
21899
|
lines.push("| Metric | Value |");
|
|
@@ -21710,18 +21903,31 @@ var SessionTracker = class {
|
|
|
21710
21903
|
lines.push(`| Total data processed | ${formatBytes(totalProcessed)} |`);
|
|
21711
21904
|
lines.push(`| Kept in sandbox | ${formatBytes(keptOut)} |`);
|
|
21712
21905
|
lines.push(`| Context consumed | ${formatBytes(totalReturned)} |`);
|
|
21713
|
-
lines.push(`| Est. tokens | ~${estTokens.toLocaleString()} |`);
|
|
21906
|
+
lines.push(`| Est. tokens used | ~${estTokens.toLocaleString()} (${tokenCost(estTokens)}) |`);
|
|
21907
|
+
lines.push(
|
|
21908
|
+
`| Est. tokens saved | ~${estTokensAvoided.toLocaleString()} (${tokenCost(estTokensAvoided)}) |`
|
|
21909
|
+
);
|
|
21714
21910
|
lines.push(
|
|
21715
21911
|
`| **Savings ratio** | **${savingsRatio.toFixed(1)}x** (${reductionPct}% reduction) |`
|
|
21716
21912
|
);
|
|
21913
|
+
if (totalProcessed > 0) {
|
|
21914
|
+
const savingsBar = asciiBar(keptOut / totalProcessed);
|
|
21915
|
+
lines.push(`
|
|
21916
|
+
**Context savings:** ${savingsBar}`);
|
|
21917
|
+
lines.push(
|
|
21918
|
+
` Sandbox: ${formatBytes(keptOut)} kept out | Context: ${formatBytes(totalReturned)} entered`
|
|
21919
|
+
);
|
|
21920
|
+
}
|
|
21717
21921
|
if (totalCalls > 0) {
|
|
21718
21922
|
lines.push("\n## Per-Tool Breakdown\n");
|
|
21719
|
-
|
|
21720
|
-
lines.push("|------|-------|--------------|-------------|");
|
|
21923
|
+
const maxBytes = Math.max(...Object.values(snap.bytesReturned));
|
|
21721
21924
|
for (const [name, calls] of Object.entries(snap.calls)) {
|
|
21722
21925
|
const bytes = snap.bytesReturned[name] ?? 0;
|
|
21926
|
+
const tokens = Math.round(bytes / 4);
|
|
21927
|
+
const barRatio = maxBytes > 0 ? bytes / maxBytes : 0;
|
|
21928
|
+
const bar = "\u2588".repeat(Math.max(1, Math.round(barRatio * 15)));
|
|
21723
21929
|
lines.push(
|
|
21724
|
-
|
|
21930
|
+
` ${name.padEnd(16)} ${String(calls).padStart(3)} calls ${bar} ${formatBytes(bytes)} (~${tokens.toLocaleString()} tok)`
|
|
21725
21931
|
);
|
|
21726
21932
|
}
|
|
21727
21933
|
}
|
|
@@ -21917,7 +22123,7 @@ var WORD_SPLIT_RE = /[^\p{L}\p{N}_-]+/u;
|
|
|
21917
22123
|
function sanitizeQuery(raw) {
|
|
21918
22124
|
const q = raw.replace(FTS_SPECIAL_RE, " ").replace(FTS_OPERATORS_RE, " ").trim();
|
|
21919
22125
|
const words = q.split(/\s+/).filter((w) => w.length >= 2).map((w) => `"${w}"`);
|
|
21920
|
-
return words.length > 0 ? words.join(" OR ") :
|
|
22126
|
+
return words.length > 0 ? words.join(" OR ") : "";
|
|
21921
22127
|
}
|
|
21922
22128
|
function levenshtein(a, b) {
|
|
21923
22129
|
if (a.length === 0) return b.length;
|
|
@@ -21937,6 +22143,11 @@ function levenshtein(a, b) {
|
|
|
21937
22143
|
var ContentStore = class {
|
|
21938
22144
|
db;
|
|
21939
22145
|
hasTrigramTable = false;
|
|
22146
|
+
// Cached prepared statements (initialized in initSchema, always available after constructor)
|
|
22147
|
+
insertSourceStmt;
|
|
22148
|
+
insertChunkStmt;
|
|
22149
|
+
vocabCountStmt;
|
|
22150
|
+
vocabInsertStmt;
|
|
21940
22151
|
constructor(dbPath) {
|
|
21941
22152
|
const path = dbPath ?? join3(tmpdir2(), `context-compress-${process.pid}.db`);
|
|
21942
22153
|
this.db = new Database(path);
|
|
@@ -21966,6 +22177,14 @@ var ContentStore = class {
|
|
|
21966
22177
|
word TEXT PRIMARY KEY
|
|
21967
22178
|
);
|
|
21968
22179
|
`);
|
|
22180
|
+
this.insertSourceStmt = this.db.prepare(
|
|
22181
|
+
"INSERT INTO sources (label, chunk_count, code_chunk_count) VALUES (?, ?, ?)"
|
|
22182
|
+
);
|
|
22183
|
+
this.insertChunkStmt = this.db.prepare(
|
|
22184
|
+
"INSERT INTO chunks (title, content, source_id, content_type) VALUES (?, ?, ?, ?)"
|
|
22185
|
+
);
|
|
22186
|
+
this.vocabCountStmt = this.db.prepare("SELECT COUNT(*) as cnt FROM vocabulary");
|
|
22187
|
+
this.vocabInsertStmt = this.db.prepare("INSERT OR IGNORE INTO vocabulary (word) VALUES (?)");
|
|
21969
22188
|
}
|
|
21970
22189
|
/** Lazily create trigram table only when porter search returns 0 results */
|
|
21971
22190
|
ensureTrigramTable() {
|
|
@@ -21991,12 +22210,8 @@ var ContentStore = class {
|
|
|
21991
22210
|
index(content, label) {
|
|
21992
22211
|
const isMarkdown = HEADING_RE.test(content) || content.includes("```") || content.includes("---");
|
|
21993
22212
|
const chunks = isMarkdown ? chunkMarkdown(content) : chunkPlainText(content);
|
|
21994
|
-
const insertSource = this.
|
|
21995
|
-
|
|
21996
|
-
);
|
|
21997
|
-
const insertChunk = this.db.prepare(
|
|
21998
|
-
"INSERT INTO chunks (title, content, source_id, content_type) VALUES (?, ?, ?, ?)"
|
|
21999
|
-
);
|
|
22213
|
+
const insertSource = this.insertSourceStmt;
|
|
22214
|
+
const insertChunk = this.insertChunkStmt;
|
|
22000
22215
|
const insertTrigram = this.hasTrigramTable ? this.db.prepare(
|
|
22001
22216
|
"INSERT INTO chunks_trigram (title, content, source_id, content_type) VALUES (?, ?, ?, ?)"
|
|
22002
22217
|
) : null;
|
|
@@ -22028,6 +22243,9 @@ var ContentStore = class {
|
|
|
22028
22243
|
search(query, options) {
|
|
22029
22244
|
const limit = options?.limit ?? 3;
|
|
22030
22245
|
const sanitized = sanitizeQuery(query);
|
|
22246
|
+
if (!sanitized) {
|
|
22247
|
+
return { query, results: [] };
|
|
22248
|
+
}
|
|
22031
22249
|
let hits = this.porterSearch(sanitized, options?.source, limit);
|
|
22032
22250
|
if (hits.length > 0) {
|
|
22033
22251
|
return { query, results: hits };
|
|
@@ -22040,9 +22258,11 @@ var ContentStore = class {
|
|
|
22040
22258
|
const corrected = this.fuzzyCorrect(query);
|
|
22041
22259
|
if (corrected && corrected !== query) {
|
|
22042
22260
|
const correctedSanitized = sanitizeQuery(corrected);
|
|
22043
|
-
|
|
22044
|
-
|
|
22045
|
-
|
|
22261
|
+
if (correctedSanitized) {
|
|
22262
|
+
hits = this.porterSearch(correctedSanitized, options?.source, limit);
|
|
22263
|
+
if (hits.length > 0) {
|
|
22264
|
+
return { query, results: hits, corrected };
|
|
22265
|
+
}
|
|
22046
22266
|
}
|
|
22047
22267
|
}
|
|
22048
22268
|
return { query, results: [] };
|
|
@@ -22123,7 +22343,7 @@ var ContentStore = class {
|
|
|
22123
22343
|
const maxDist = word.length <= 4 ? 1 : word.length <= 12 ? 2 : 3;
|
|
22124
22344
|
const minLen = word.length - maxDist;
|
|
22125
22345
|
const maxLen = word.length + maxDist;
|
|
22126
|
-
const candidates = this.db.prepare("SELECT word FROM vocabulary WHERE length(word) BETWEEN ? AND ?").all(minLen, maxLen);
|
|
22346
|
+
const candidates = this.db.prepare("SELECT word FROM vocabulary WHERE length(word) BETWEEN ? AND ? LIMIT 500").all(minLen, maxLen);
|
|
22127
22347
|
let bestWord = word;
|
|
22128
22348
|
let bestDist = maxDist + 1;
|
|
22129
22349
|
for (const { word: candidate } of candidates) {
|
|
@@ -22142,11 +22362,11 @@ var ContentStore = class {
|
|
|
22142
22362
|
* Update vocabulary table from content (bounded to MAX_VOCABULARY).
|
|
22143
22363
|
*/
|
|
22144
22364
|
updateVocabulary(content) {
|
|
22145
|
-
const currentCount = this.
|
|
22365
|
+
const currentCount = this.vocabCountStmt.get().cnt;
|
|
22146
22366
|
if (currentCount >= MAX_VOCABULARY) return;
|
|
22147
22367
|
const words = content.split(WORD_SPLIT_RE).filter((w) => w.length >= 3 && !STOPWORDS.has(w.toLowerCase()));
|
|
22148
22368
|
const unique = new Set(words.map((w) => w.toLowerCase()));
|
|
22149
|
-
const insert = this.
|
|
22369
|
+
const insert = this.vocabInsertStmt;
|
|
22150
22370
|
let added = 0;
|
|
22151
22371
|
for (const word of unique) {
|
|
22152
22372
|
if (currentCount + added >= MAX_VOCABULARY) break;
|
|
@@ -22163,7 +22383,7 @@ var ContentStore = class {
|
|
|
22163
22383
|
).get(...sourceId ? [sourceId] : []).cnt;
|
|
22164
22384
|
if (totalChunks === 0) return [];
|
|
22165
22385
|
const filter = sourceId ? " WHERE source_id = ?" : "";
|
|
22166
|
-
const stmt = this.db.prepare(`SELECT content FROM chunks${filter}`);
|
|
22386
|
+
const stmt = this.db.prepare(`SELECT content FROM chunks${filter} LIMIT 500`);
|
|
22167
22387
|
const rows = sourceId ? stmt.all(sourceId) : stmt.all();
|
|
22168
22388
|
const docFreq = /* @__PURE__ */ new Map();
|
|
22169
22389
|
for (const row of rows) {
|
|
@@ -22188,6 +22408,21 @@ var ContentStore = class {
|
|
|
22188
22408
|
scored.sort((a, b) => b.score - a.score);
|
|
22189
22409
|
return scored.slice(0, 40).map((s) => s.word);
|
|
22190
22410
|
}
|
|
22411
|
+
/**
|
|
22412
|
+
* List all indexed sources with metadata.
|
|
22413
|
+
*/
|
|
22414
|
+
listSources() {
|
|
22415
|
+
const rows = this.db.prepare(
|
|
22416
|
+
"SELECT id, label, chunk_count, code_chunk_count, indexed_at FROM sources ORDER BY indexed_at DESC"
|
|
22417
|
+
).all();
|
|
22418
|
+
return rows.map((row) => ({
|
|
22419
|
+
id: row.id,
|
|
22420
|
+
label: row.label,
|
|
22421
|
+
chunkCount: row.chunk_count,
|
|
22422
|
+
codeChunks: row.code_chunk_count,
|
|
22423
|
+
indexedAt: row.indexed_at
|
|
22424
|
+
}));
|
|
22425
|
+
}
|
|
22191
22426
|
/**
|
|
22192
22427
|
* Get store statistics.
|
|
22193
22428
|
*/
|
|
@@ -22313,8 +22548,8 @@ function cleanupStaleDbs() {
|
|
|
22313
22548
|
return cleaned;
|
|
22314
22549
|
}
|
|
22315
22550
|
|
|
22316
|
-
// src/
|
|
22317
|
-
var
|
|
22551
|
+
// src/types.ts
|
|
22552
|
+
var ALL_LANGUAGES = [
|
|
22318
22553
|
"javascript",
|
|
22319
22554
|
"typescript",
|
|
22320
22555
|
"python",
|
|
@@ -22327,6 +22562,9 @@ var LANGUAGES = [
|
|
|
22327
22562
|
"r",
|
|
22328
22563
|
"elixir"
|
|
22329
22564
|
];
|
|
22565
|
+
|
|
22566
|
+
// src/server.ts
|
|
22567
|
+
var LANGUAGE_ENUM = ALL_LANGUAGES;
|
|
22330
22568
|
var projectDir = process.env.CLAUDE_PROJECT_DIR ?? process.cwd();
|
|
22331
22569
|
function isWithinProject(absPath) {
|
|
22332
22570
|
const normalized = resolve(absPath);
|
|
@@ -22342,6 +22580,18 @@ function getVersion() {
|
|
|
22342
22580
|
return "1.0.0";
|
|
22343
22581
|
}
|
|
22344
22582
|
}
|
|
22583
|
+
function compactLabel(normal, level) {
|
|
22584
|
+
if (level === "ultra") {
|
|
22585
|
+
return normal.replace(/\*\*/g, "").replace(/Use search\(queries: \[\.\.\.]\) to retrieve.*$/gm, "\u2192 search() for more").replace(/Searchable terms: .+$/gm, "");
|
|
22586
|
+
}
|
|
22587
|
+
if (level === "compact") {
|
|
22588
|
+
return normal.replace(
|
|
22589
|
+
/Use search\(queries: \[\.\.\.]\) to retrieve full content of any section\./,
|
|
22590
|
+
"\u2192 search() for details"
|
|
22591
|
+
);
|
|
22592
|
+
}
|
|
22593
|
+
return normal;
|
|
22594
|
+
}
|
|
22345
22595
|
async function createServer(config3) {
|
|
22346
22596
|
const version2 = getVersion();
|
|
22347
22597
|
debug("Version:", version2);
|
|
@@ -22352,6 +22602,15 @@ async function createServer(config3) {
|
|
|
22352
22602
|
const executor = new SubprocessExecutor(runtimes, config3);
|
|
22353
22603
|
const store = new ContentStore();
|
|
22354
22604
|
const tracker = new SessionTracker();
|
|
22605
|
+
const shutdown = () => {
|
|
22606
|
+
try {
|
|
22607
|
+
store.close();
|
|
22608
|
+
} catch {
|
|
22609
|
+
}
|
|
22610
|
+
};
|
|
22611
|
+
process.on("SIGINT", shutdown);
|
|
22612
|
+
process.on("SIGTERM", shutdown);
|
|
22613
|
+
process.on("beforeExit", shutdown);
|
|
22355
22614
|
const searchCalls = [];
|
|
22356
22615
|
const server2 = new McpServer({
|
|
22357
22616
|
name: "context-compress",
|
|
@@ -22359,11 +22618,11 @@ async function createServer(config3) {
|
|
|
22359
22618
|
});
|
|
22360
22619
|
server2.tool(
|
|
22361
22620
|
"execute",
|
|
22362
|
-
`Execute code in a sandboxed subprocess. Only stdout enters context \u2014 raw data stays in the subprocess. Use instead of bash/cat when output would exceed 20 lines. ${bunDetected ? "(Bun detected \u2014 JS/TS runs 3-5x faster) " : ""}Available: ${
|
|
22621
|
+
`Execute code in a sandboxed subprocess. Only stdout enters context \u2014 raw data stays in the subprocess. Use instead of bash/cat when output would exceed 20 lines. ${bunDetected ? "(Bun detected \u2014 JS/TS runs 3-5x faster) " : ""}Available: ${ALL_LANGUAGES.join(", ")}.
|
|
22363
22622
|
|
|
22364
22623
|
PREFER THIS OVER BASH for: API calls (gh, curl, aws), test runners (npm test, pytest), git queries (git log, git diff), data processing, and ANY CLI command that may produce large output. Bash should only be used for file mutations, git writes, and navigation.`,
|
|
22365
22624
|
{
|
|
22366
|
-
language: external_exports.enum(
|
|
22625
|
+
language: external_exports.enum(LANGUAGE_ENUM).describe("Runtime language"),
|
|
22367
22626
|
code: external_exports.string().describe(
|
|
22368
22627
|
"Source code to execute. Use console.log (JS/TS), print (Python/Ruby/Perl/R), echo (Shell), echo (PHP), fmt.Println (Go), or IO.puts (Elixir) to output a summary to context."
|
|
22369
22628
|
),
|
|
@@ -22398,13 +22657,13 @@ ${result.stderr}`;
|
|
|
22398
22657
|
filtered += ` - **${hit.title}**: ${hit.snippet.slice(0, 200)}
|
|
22399
22658
|
`;
|
|
22400
22659
|
}
|
|
22401
|
-
if (terms.length > 0) {
|
|
22660
|
+
if (terms.length > 0 && config3.compressionLevel !== "ultra") {
|
|
22402
22661
|
filtered += `
|
|
22403
22662
|
Searchable terms: ${terms.join(", ")}
|
|
22404
22663
|
`;
|
|
22405
22664
|
}
|
|
22406
22665
|
filtered += "\nUse search(queries: [...]) to retrieve full content of any section.";
|
|
22407
|
-
output = filtered;
|
|
22666
|
+
output = compactLabel(filtered, config3.compressionLevel);
|
|
22408
22667
|
}
|
|
22409
22668
|
const responseBytes = Buffer.byteLength(output);
|
|
22410
22669
|
tracker.trackCall("execute", responseBytes);
|
|
@@ -22416,7 +22675,7 @@ Searchable terms: ${terms.join(", ")}
|
|
|
22416
22675
|
"Read a file and process it without loading contents into context. The file is read into a FILE_CONTENT variable inside the sandbox. Only your printed summary enters context.\n\nPREFER THIS OVER Read/cat for: log files, data files (CSV, JSON, XML), large source files for analysis, and any file where you need to extract specific information rather than read the entire content.",
|
|
22417
22676
|
{
|
|
22418
22677
|
path: external_exports.string().describe("Absolute file path or relative to project root"),
|
|
22419
|
-
language: external_exports.enum(
|
|
22678
|
+
language: external_exports.enum(LANGUAGE_ENUM).describe("Runtime language"),
|
|
22420
22679
|
code: external_exports.string().describe(
|
|
22421
22680
|
"Code to process FILE_CONTENT. Print summary via console.log/print/echo/IO.puts."
|
|
22422
22681
|
),
|
|
@@ -22462,13 +22721,13 @@ ${result.stderr}`;
|
|
|
22462
22721
|
filtered += ` - **${hit.title}**: ${hit.snippet.slice(0, 200)}
|
|
22463
22722
|
`;
|
|
22464
22723
|
}
|
|
22465
|
-
if (terms.length > 0) {
|
|
22724
|
+
if (terms.length > 0 && config3.compressionLevel !== "ultra") {
|
|
22466
22725
|
filtered += `
|
|
22467
22726
|
Searchable terms: ${terms.join(", ")}
|
|
22468
22727
|
`;
|
|
22469
22728
|
}
|
|
22470
22729
|
filtered += "\nUse search(queries: [...]) to retrieve full content of any section.";
|
|
22471
|
-
output = filtered;
|
|
22730
|
+
output = compactLabel(filtered, config3.compressionLevel);
|
|
22472
22731
|
}
|
|
22473
22732
|
const responseBytes = Buffer.byteLength(output);
|
|
22474
22733
|
tracker.trackCall("execute_file", responseBytes);
|
|
@@ -22498,6 +22757,17 @@ Searchable terms: ${terms.join(", ")}
|
|
|
22498
22757
|
]
|
|
22499
22758
|
};
|
|
22500
22759
|
}
|
|
22760
|
+
const fileStat = statSync(absPath);
|
|
22761
|
+
if (fileStat.size > 50 * 1024 * 1024) {
|
|
22762
|
+
return {
|
|
22763
|
+
content: [
|
|
22764
|
+
{
|
|
22765
|
+
type: "text",
|
|
22766
|
+
text: `Error: file "${filePath}" is too large (${(fileStat.size / 1024 / 1024).toFixed(1)}MB). Max 50MB.`
|
|
22767
|
+
}
|
|
22768
|
+
]
|
|
22769
|
+
};
|
|
22770
|
+
}
|
|
22501
22771
|
text = readFileSync2(absPath, "utf-8");
|
|
22502
22772
|
label = source ?? filePath;
|
|
22503
22773
|
} else if (content) {
|
|
@@ -22587,8 +22857,7 @@ ${hit.snippet}
|
|
|
22587
22857
|
content: [{ type: "text", text: "Error: only http/https URLs are allowed" }]
|
|
22588
22858
|
};
|
|
22589
22859
|
}
|
|
22590
|
-
|
|
22591
|
-
if (hostname2 === "localhost" || hostname2 === "127.0.0.1" || hostname2 === "::1" || hostname2 === "0.0.0.0" || hostname2.startsWith("10.") || hostname2.startsWith("172.16.") || hostname2.startsWith("192.168.") || hostname2.startsWith("169.254.")) {
|
|
22860
|
+
if (isPrivateHost(parsed.hostname)) {
|
|
22592
22861
|
return {
|
|
22593
22862
|
content: [
|
|
22594
22863
|
{ type: "text", text: "Error: internal/private URLs are not allowed" }
|
|
@@ -22729,7 +22998,7 @@ Searchable terms: ${terms.join(", ")}`;
|
|
|
22729
22998
|
);
|
|
22730
22999
|
server2.tool(
|
|
22731
23000
|
"stats",
|
|
22732
|
-
"Returns context consumption statistics for the current session. Shows total bytes returned to context, breakdown by tool, call counts, estimated token usage,
|
|
23001
|
+
"Returns context consumption statistics for the current session. Shows total bytes returned to context, breakdown by tool, call counts, estimated token usage, context savings ratio, and visual charts.",
|
|
22733
23002
|
{},
|
|
22734
23003
|
async () => {
|
|
22735
23004
|
const report = tracker.formatReport();
|
|
@@ -22737,6 +23006,77 @@ Searchable terms: ${terms.join(", ")}`;
|
|
|
22737
23006
|
return { content: [{ type: "text", text: report }] };
|
|
22738
23007
|
}
|
|
22739
23008
|
);
|
|
23009
|
+
server2.tool(
|
|
23010
|
+
"discover",
|
|
23011
|
+
"Shows what's in the knowledge base and suggests optimization opportunities. Lists all indexed sources, chunk counts, searchable terms, and recommends next actions. Use this to understand what data is available for search.",
|
|
23012
|
+
{},
|
|
23013
|
+
async () => {
|
|
23014
|
+
const storeStats = store.getStats();
|
|
23015
|
+
const snap = tracker.getSnapshot();
|
|
23016
|
+
const lines = [];
|
|
23017
|
+
lines.push("## Knowledge Base Discovery\n");
|
|
23018
|
+
if (storeStats.totalSources === 0) {
|
|
23019
|
+
lines.push("No content indexed yet. Use these tools to build the knowledge base:\n");
|
|
23020
|
+
lines.push("- `batch_execute` \u2014 run commands and auto-index output");
|
|
23021
|
+
lines.push("- `execute` with `intent` \u2014 auto-indexes large output");
|
|
23022
|
+
lines.push("- `index` \u2014 index documentation or files");
|
|
23023
|
+
lines.push("- `fetch_and_index` \u2014 fetch and index web pages");
|
|
23024
|
+
} else {
|
|
23025
|
+
lines.push("| Metric | Value |");
|
|
23026
|
+
lines.push("|--------|-------|");
|
|
23027
|
+
lines.push(`| Indexed sources | ${storeStats.totalSources} |`);
|
|
23028
|
+
lines.push(`| Total chunks | ${storeStats.totalChunks} |`);
|
|
23029
|
+
lines.push(`| Vocabulary size | ${storeStats.vocabularySize} |`);
|
|
23030
|
+
lines.push(
|
|
23031
|
+
`| Trigram index | ${storeStats.hasTrigramTable ? "active" : "lazy (not yet needed)"} |`
|
|
23032
|
+
);
|
|
23033
|
+
const sources = store.listSources();
|
|
23034
|
+
if (sources.length > 0) {
|
|
23035
|
+
lines.push("\n### Indexed Sources\n");
|
|
23036
|
+
for (const src of sources) {
|
|
23037
|
+
lines.push(
|
|
23038
|
+
`- **${src.label}** \u2014 ${src.chunkCount} chunks${src.codeChunks > 0 ? ` (${src.codeChunks} with code)` : ""}`
|
|
23039
|
+
);
|
|
23040
|
+
}
|
|
23041
|
+
}
|
|
23042
|
+
const terms = store.getDistinctiveTerms();
|
|
23043
|
+
if (terms.length > 0) {
|
|
23044
|
+
lines.push("\n### Top Searchable Terms\n");
|
|
23045
|
+
lines.push(terms.slice(0, 20).join(", "));
|
|
23046
|
+
}
|
|
23047
|
+
}
|
|
23048
|
+
lines.push("\n### Optimization Suggestions\n");
|
|
23049
|
+
const totalCalls = Object.values(snap.calls).reduce((a, b) => a + b, 0);
|
|
23050
|
+
if (totalCalls === 0) {
|
|
23051
|
+
lines.push("- Start by using `batch_execute` to run multiple commands at once");
|
|
23052
|
+
} else {
|
|
23053
|
+
const searchCalls2 = snap.calls.search ?? 0;
|
|
23054
|
+
const executeCalls = snap.calls.execute ?? 0;
|
|
23055
|
+
const batchCalls = snap.calls.batch_execute ?? 0;
|
|
23056
|
+
if (executeCalls > 3 && batchCalls === 0) {
|
|
23057
|
+
lines.push(
|
|
23058
|
+
"- **Use batch_execute** \u2014 you've made multiple execute calls that could be batched into one"
|
|
23059
|
+
);
|
|
23060
|
+
}
|
|
23061
|
+
if (searchCalls2 > 5) {
|
|
23062
|
+
lines.push("- **Batch your searches** \u2014 pass multiple queries in a single search() call");
|
|
23063
|
+
}
|
|
23064
|
+
if (storeStats.totalChunks > 50) {
|
|
23065
|
+
lines.push(
|
|
23066
|
+
"- **Use source filtering** \u2014 scope searches with `source` parameter for faster, targeted results"
|
|
23067
|
+
);
|
|
23068
|
+
}
|
|
23069
|
+
if (storeStats.totalSources === 0 && totalCalls > 2) {
|
|
23070
|
+
lines.push(
|
|
23071
|
+
"- **Index more content** \u2014 use `intent` parameter in execute calls to auto-index large output"
|
|
23072
|
+
);
|
|
23073
|
+
}
|
|
23074
|
+
}
|
|
23075
|
+
const output = lines.join("\n");
|
|
23076
|
+
tracker.trackCall("discover", Buffer.byteLength(output));
|
|
23077
|
+
return { content: [{ type: "text", text: output }] };
|
|
23078
|
+
}
|
|
23079
|
+
);
|
|
22740
23080
|
return {
|
|
22741
23081
|
async start() {
|
|
22742
23082
|
const transport = new StdioServerTransport();
|