context-compress 2026.3.21 → 2026.3.22

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.
@@ -11061,7 +11061,7 @@ function debug(...args) {
11061
11061
  }
11062
11062
 
11063
11063
  // src/server.ts
11064
- import { readFileSync as readFileSync2, realpathSync, statSync } from "node:fs";
11064
+ import { readFileSync as readFileSync3, realpathSync, statSync } from "node:fs";
11065
11065
  import { dirname, join as join4, resolve } from "node:path";
11066
11066
  import { fileURLToPath } from "node:url";
11067
11067
 
@@ -21217,6 +21217,129 @@ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
21217
21217
  import { tmpdir } from "node:os";
21218
21218
  import { join as join2 } from "node:path";
21219
21219
 
21220
+ // src/filters.ts
21221
+ function applyCommandFilter(code, stdout) {
21222
+ const cmd = code.trim().split(/\s+/)[0];
21223
+ const fullCmd = code.trim();
21224
+ if (cmd === "git") return filterGit(fullCmd, stdout);
21225
+ if (cmd === "npm" || cmd === "yarn" || cmd === "pnpm" || cmd === "bun")
21226
+ return filterPackageManager(fullCmd, stdout);
21227
+ if (fullCmd.includes("test") || fullCmd.includes("jest") || fullCmd.includes("vitest") || fullCmd.includes("pytest") || fullCmd.includes("cargo test")) {
21228
+ return filterTestOutput(stdout);
21229
+ }
21230
+ if (cmd === "cargo" || cmd === "make" || cmd === "gradle")
21231
+ return filterBuildOutput(fullCmd, stdout);
21232
+ if (cmd === "docker" || cmd === "kubectl") return filterContainerOutput(fullCmd, stdout);
21233
+ if (cmd === "ls" || cmd === "find" || cmd === "tree") return filterFileList(fullCmd, stdout);
21234
+ return { output: stdout, filtered: false };
21235
+ }
21236
+ function filterGit(cmd, stdout) {
21237
+ if (/git\s+(push|pull|fetch|clone)/.test(cmd)) {
21238
+ const lines = stdout.split("\n");
21239
+ const filtered = lines.filter(
21240
+ (l) => !l.startsWith("remote: Counting") && !l.startsWith("remote: Compressing") && !l.startsWith("remote: Total") && !l.includes("Unpacking objects:") && !l.includes("Receiving objects:") && !l.includes("Resolving deltas:") && !/^\s*\d+%/.test(l)
21241
+ );
21242
+ return { output: filtered.join("\n"), filtered: true };
21243
+ }
21244
+ if (/git\s+status/.test(cmd)) {
21245
+ const lines = stdout.split("\n");
21246
+ const filtered = lines.filter((l) => !l.startsWith(" (use ") && l.trim() !== "");
21247
+ return { output: filtered.join("\n"), filtered: true };
21248
+ }
21249
+ return { output: stdout, filtered: false };
21250
+ }
21251
+ function filterPackageManager(cmd, stdout) {
21252
+ if (/\b(install|add|i)\b/.test(cmd)) {
21253
+ const lines = stdout.split("\n");
21254
+ const filtered = lines.filter(
21255
+ (l) => !l.startsWith("npm warn") && !l.includes("packages are looking for funding") && !l.includes("run `npm fund`") && !l.startsWith("npm notice") && !/^[\s\u2502\u251C\u2514\u2500]+$/.test(l) && // tree-drawing characters
21256
+ !/^\s*$/.test(l)
21257
+ );
21258
+ return { output: filtered.join("\n"), filtered: true };
21259
+ }
21260
+ if (/\btest\b/.test(cmd)) {
21261
+ return filterTestOutput(stdout);
21262
+ }
21263
+ return { output: stdout, filtered: false };
21264
+ }
21265
+ var FAIL_MARKER_RE = /^\s*[\u2717\u2718\u00D7]\s/;
21266
+ var FAIL_WORD_RE = /\bFAIL\b/;
21267
+ var FAILED_RE = /\bfailed?\b/i;
21268
+ var ERROR_RE = /\bERROR\b/;
21269
+ var SUMMARY_RE = /^\s*(Tests?|Suites?|Test Suites)\s*:|^\s*(pass|fail|skip|pending|todo)\s|\b\d+\s+(passing|failing|pending|skipped)\b|^(ok|not ok)\s|^\u2139\s|^(PASS|FAIL)\s/i;
21270
+ function isFailMarker(line) {
21271
+ return FAIL_MARKER_RE.test(line) || FAIL_WORD_RE.test(line) || FAILED_RE.test(line) || ERROR_RE.test(line);
21272
+ }
21273
+ function isSummaryLine(line) {
21274
+ return SUMMARY_RE.test(line);
21275
+ }
21276
+ function filterTestOutput(stdout) {
21277
+ const lines = stdout.split("\n");
21278
+ const failures = [];
21279
+ const summary = [];
21280
+ let inFailure = false;
21281
+ let failCount = 0;
21282
+ for (const line of lines) {
21283
+ if (isFailMarker(line)) {
21284
+ inFailure = true;
21285
+ failCount++;
21286
+ }
21287
+ if (inFailure) {
21288
+ failures.push(line);
21289
+ if (line.trim() === "" && failures.length > 3) inFailure = false;
21290
+ }
21291
+ if (isSummaryLine(line)) {
21292
+ summary.push(line);
21293
+ }
21294
+ }
21295
+ if (failCount === 0 && summary.length > 0) {
21296
+ return { output: summary.join("\n"), filtered: true };
21297
+ }
21298
+ if (failures.length > 0) {
21299
+ const result = [...failures, "", ...summary].join("\n");
21300
+ return { output: result, filtered: true };
21301
+ }
21302
+ return { output: stdout, filtered: false };
21303
+ }
21304
+ function filterBuildOutput(cmd, stdout) {
21305
+ const lines = stdout.split("\n");
21306
+ const filtered = lines.filter(
21307
+ (l) => !l.includes("Downloading") && !l.includes("Downloaded") && !/Compiling\s+\d+\s+of\s+\d+/.test(l) && !l.includes("Blocking waiting for file lock") && !/^\s*$/.test(l)
21308
+ );
21309
+ return { output: filtered.join("\n"), filtered: filtered.length < lines.length };
21310
+ }
21311
+ function filterContainerOutput(cmd, stdout) {
21312
+ if (/docker\s+build/.test(cmd)) {
21313
+ const lines = stdout.split("\n");
21314
+ const filtered = lines.filter(
21315
+ (l) => !l.startsWith(" ---> ") && !l.startsWith("Sending build context")
21316
+ );
21317
+ return { output: filtered.join("\n"), filtered: true };
21318
+ }
21319
+ return { output: stdout, filtered: false };
21320
+ }
21321
+ function filterFileList(cmd, stdout) {
21322
+ const lines = stdout.split("\n").filter((l) => l.trim() !== "");
21323
+ if (lines.length <= 30) return { output: stdout, filtered: false };
21324
+ if (cmd.includes("-R") || cmd.startsWith("find")) {
21325
+ const dirs = /* @__PURE__ */ new Map();
21326
+ for (const line of lines) {
21327
+ const parts = line.split("/");
21328
+ const dir = parts.length > 1 ? parts.slice(0, -1).join("/") : ".";
21329
+ dirs.set(dir, (dirs.get(dir) ?? 0) + 1);
21330
+ }
21331
+ if (dirs.size > 5 && lines.length > 50) {
21332
+ const summary = Array.from(dirs.entries()).sort((a, b) => b[1] - a[1]).map(([dir, count]) => ` ${dir}/ (${count} files)`).join("\n");
21333
+ return {
21334
+ output: `${lines.length} files found:
21335
+ ${summary}`,
21336
+ filtered: true
21337
+ };
21338
+ }
21339
+ }
21340
+ return { output: stdout, filtered: false };
21341
+ }
21342
+
21220
21343
  // src/utils.ts
21221
21344
  function detectInjectionPatterns(content) {
21222
21345
  const warnings = [];
@@ -21265,6 +21388,10 @@ function formatBytes(bytes) {
21265
21388
 
21266
21389
  // src/executor.ts
21267
21390
  var DEFAULT_TIMEOUT = 3e4;
21391
+ var ANSI_RE = /\x1b\[[0-9;]*[a-zA-Z]/;
21392
+ function stripAnsi(str) {
21393
+ return str.replace(new RegExp(ANSI_RE.source, "g"), "");
21394
+ }
21268
21395
  var SAFE_ENV_KEYS = [
21269
21396
  "PATH",
21270
21397
  "HOME",
@@ -21317,6 +21444,21 @@ function killProcessTree(pid) {
21317
21444
  }
21318
21445
  }
21319
21446
  }
21447
+ function stripProgressLines(output) {
21448
+ const lines = output.split("\n");
21449
+ const filtered = lines.filter((l) => {
21450
+ const trimmed = l.trim();
21451
+ if (ANSI_RE.test(l) && trimmed.replace(new RegExp(ANSI_RE.source, "g"), "").trim() === "")
21452
+ return false;
21453
+ if (/^[\s\[│├└─═━▓░█▒▏▎▍▌▋▊▉\]>=#\-.\d%]+$/.test(trimmed) && trimmed.length > 3) return false;
21454
+ if (/^[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏\-\\|/]\s/.test(trimmed)) return false;
21455
+ if (/(?:downloading|uploading|fetching|resolving)\s+[\d.]+\s*[kmg]?b/i.test(trimmed))
21456
+ return false;
21457
+ if (/\d+\.?\d*\s*[kmg]?b\/s/i.test(trimmed) && /eta|remaining/i.test(trimmed)) return false;
21458
+ return true;
21459
+ });
21460
+ return filtered.join("\n");
21461
+ }
21320
21462
  function deduplicateLines(output) {
21321
21463
  const lines = output.split("\n");
21322
21464
  if (lines.length < 3) return output;
@@ -21348,12 +21490,12 @@ function deduplicateLines(output) {
21348
21490
  function groupErrorLines(output) {
21349
21491
  const lines = output.split("\n");
21350
21492
  if (lines.length < 5) return output;
21351
- const ERROR_RE = /^(.*?(?:error|warning|Error|Warning|ERR|WARN)[:\s])\s*(.+?)(?:\s+(?:at|in|on)\s+(?:line\s+)?(\d+))?$/i;
21493
+ const ERROR_RE2 = /^(.*?(?:error|warning|Error|Warning|ERR|WARN)[:\s])\s*(.+?)(?:\s+(?:at|in|on)\s+(?:line\s+)?(\d+))?$/i;
21352
21494
  const errorGroups = /* @__PURE__ */ new Map();
21353
21495
  const resultLines = [];
21354
21496
  let groupedCount = 0;
21355
21497
  for (const line of lines) {
21356
- const match = line.match(ERROR_RE);
21498
+ const match = line.match(ERROR_RE2);
21357
21499
  if (match) {
21358
21500
  const prefix = match[1].trim();
21359
21501
  const msg = match[2].trim();
@@ -21505,7 +21647,8 @@ var SubprocessExecutor = class {
21505
21647
  tmpDir,
21506
21648
  timeout,
21507
21649
  maxOutput,
21508
- plugin.needsShell
21650
+ plugin.needsShell,
21651
+ opts.language === "shell" ? opts.code : void 0
21509
21652
  );
21510
21653
  } finally {
21511
21654
  setTimeout(() => this.cleanupTempDir(tmpDir), 100).unref();
@@ -21532,7 +21675,7 @@ var SubprocessExecutor = class {
21532
21675
  }
21533
21676
  return this.execute({ ...opts, code });
21534
21677
  }
21535
- spawnAndCapture(cmd, args, cwd, timeout, maxOutput, useShell) {
21678
+ spawnAndCapture(cmd, args, cwd, timeout, maxOutput, useShell, shellCode) {
21536
21679
  return new Promise((resolve2) => {
21537
21680
  const hardCap = this.config.hardCapBytes;
21538
21681
  const stdoutChunks = [];
@@ -21597,7 +21740,15 @@ var SubprocessExecutor = class {
21597
21740
  stdout += `
21598
21741
  [output capped at ${formatBytes(hardCap)} \u2014 process killed]`;
21599
21742
  }
21743
+ if (shellCode && stdout) {
21744
+ const filtered = applyCommandFilter(shellCode, stdout);
21745
+ if (filtered.filtered) {
21746
+ stdout = filtered.output;
21747
+ }
21748
+ }
21749
+ stdout = stripAnsi(stdout);
21600
21750
  if (stdout.length > 1e4) {
21751
+ stdout = stripProgressLines(stdout);
21601
21752
  stdout = deduplicateLines(stdout);
21602
21753
  stdout = groupErrorLines(stdout);
21603
21754
  }
@@ -22013,6 +22164,7 @@ function hasBun(runtimes) {
22013
22164
  }
22014
22165
 
22015
22166
  // src/stats.ts
22167
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
22016
22168
  var BAR_WIDTH = 20;
22017
22169
  function asciiBar(ratio, width = BAR_WIDTH) {
22018
22170
  const filled = Math.round(ratio * width);
@@ -22032,6 +22184,10 @@ var SessionTracker = class {
22032
22184
  bytesSandboxed: 0,
22033
22185
  sessionStart: Date.now()
22034
22186
  };
22187
+ cumulativeFile;
22188
+ constructor(cumulativeFile) {
22189
+ this.cumulativeFile = cumulativeFile ?? null;
22190
+ }
22035
22191
  trackCall(toolName, responseBytes) {
22036
22192
  this.stats.calls[toolName] = (this.stats.calls[toolName] ?? 0) + 1;
22037
22193
  this.stats.bytesReturned[toolName] = (this.stats.bytesReturned[toolName] ?? 0) + responseBytes;
@@ -22045,6 +22201,47 @@ var SessionTracker = class {
22045
22201
  getSnapshot() {
22046
22202
  return { ...this.stats };
22047
22203
  }
22204
+ /** Load cumulative stats from disk */
22205
+ loadCumulative() {
22206
+ if (!this.cumulativeFile) return null;
22207
+ try {
22208
+ const data = readFileSync2(this.cumulativeFile, "utf-8");
22209
+ return JSON.parse(data);
22210
+ } catch {
22211
+ return null;
22212
+ }
22213
+ }
22214
+ /** Save current session stats to cumulative file */
22215
+ saveCumulative() {
22216
+ if (!this.cumulativeFile) return;
22217
+ const snap = this.stats;
22218
+ const keptOut = snap.bytesIndexed + snap.bytesSandboxed;
22219
+ const totalReturned = Object.values(snap.bytesReturned).reduce((a, b) => a + b, 0);
22220
+ const cumulative = this.loadCumulative() ?? {
22221
+ totalBytesSaved: 0,
22222
+ totalBytesProcessed: 0,
22223
+ totalCalls: 0,
22224
+ totalSessions: 0,
22225
+ firstSeen: (/* @__PURE__ */ new Date()).toISOString(),
22226
+ lastSeen: (/* @__PURE__ */ new Date()).toISOString(),
22227
+ perCommand: {}
22228
+ };
22229
+ cumulative.totalBytesSaved += keptOut;
22230
+ cumulative.totalBytesProcessed += keptOut + totalReturned;
22231
+ cumulative.totalCalls += Object.values(snap.calls).reduce((a, b) => a + b, 0);
22232
+ cumulative.totalSessions += 1;
22233
+ cumulative.lastSeen = (/* @__PURE__ */ new Date()).toISOString();
22234
+ for (const [name, calls] of Object.entries(snap.calls)) {
22235
+ if (!cumulative.perCommand[name]) {
22236
+ cumulative.perCommand[name] = { calls: 0, bytesSaved: 0 };
22237
+ }
22238
+ cumulative.perCommand[name].calls += calls;
22239
+ }
22240
+ try {
22241
+ writeFileSync2(this.cumulativeFile, JSON.stringify(cumulative, null, 2));
22242
+ } catch {
22243
+ }
22244
+ }
22048
22245
  formatReport() {
22049
22246
  const snap = this.stats;
22050
22247
  const elapsed = Date.now() - snap.sessionStart;
@@ -22105,6 +22302,18 @@ var SessionTracker = class {
22105
22302
  `
22106
22303
  Context-compress kept ${formatBytes(keptOut)} out of context (${reductionPct}% savings).`
22107
22304
  );
22305
+ const cumulative = this.loadCumulative();
22306
+ if (cumulative) {
22307
+ lines.push("\n## Cumulative Savings (All Sessions)\n");
22308
+ lines.push("| Metric | Value |");
22309
+ lines.push("|--------|-------|");
22310
+ lines.push(`| Sessions tracked | ${cumulative.totalSessions} |`);
22311
+ lines.push(`| Total data processed | ${formatBytes(cumulative.totalBytesProcessed)} |`);
22312
+ lines.push(`| Total kept out of context | ${formatBytes(cumulative.totalBytesSaved)} |`);
22313
+ const cumTokensMid = Math.round(cumulative.totalBytesSaved / 4);
22314
+ lines.push(`| Est. total tokens saved | ~${cumTokensMid.toLocaleString()} |`);
22315
+ lines.push(`| Tracking since | ${cumulative.firstSeen.split("T")[0]} |`);
22316
+ }
22108
22317
  return lines.join("\n");
22109
22318
  }
22110
22319
  };
@@ -22722,7 +22931,7 @@ function getVersion() {
22722
22931
  try {
22723
22932
  const __dirname = dirname(fileURLToPath(import.meta.url));
22724
22933
  const pkgPath = join4(__dirname, "..", "package.json");
22725
- const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
22934
+ const pkg = JSON.parse(readFileSync3(pkgPath, "utf-8"));
22726
22935
  return pkg.version ?? "1.0.0";
22727
22936
  } catch {
22728
22937
  return "1.0.0";
@@ -22757,7 +22966,8 @@ async function createServer(config3) {
22757
22966
  store = new ContentStore(":memory:");
22758
22967
  dbFallback = true;
22759
22968
  }
22760
- const tracker = new SessionTracker();
22969
+ const cumulativeFile = config3.persistDb ? join4(config3.dbDir ?? join4(projectDir, ".context-compress"), "stats.json") : void 0;
22970
+ const tracker = new SessionTracker(cumulativeFile);
22761
22971
  let activeExecutions = 0;
22762
22972
  const MAX_CONCURRENT_EXECUTIONS = 8;
22763
22973
  const EXECUTION_LIMIT_ERROR = "Error: too many concurrent executions. Try again shortly.";
@@ -22796,6 +23006,10 @@ Searchable terms: ${terms.join(", ")}
22796
23006
  return compactLabel(filtered, config3.compressionLevel);
22797
23007
  }
22798
23008
  const shutdown = () => {
23009
+ try {
23010
+ tracker.saveCumulative();
23011
+ } catch {
23012
+ }
22799
23013
  try {
22800
23014
  executor.shutdown();
22801
23015
  } catch {
@@ -22974,7 +23188,7 @@ ${result.stderr}`;
22974
23188
  ]
22975
23189
  };
22976
23190
  }
22977
- text = readFileSync2(absPath, "utf-8");
23191
+ text = readFileSync3(absPath, "utf-8");
22978
23192
  label = source ?? filePath;
22979
23193
  } catch (e) {
22980
23194
  const msg = e instanceof Error ? e.message : String(e);
@@ -23255,6 +23469,7 @@ Searchable terms: ${terms.join(", ")}`;
23255
23469
  "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.",
23256
23470
  {},
23257
23471
  async () => {
23472
+ tracker.saveCumulative();
23258
23473
  const report = tracker.formatReport();
23259
23474
  tracker.trackCall("stats", Buffer.byteLength(report));
23260
23475
  return { content: [{ type: "text", text: report }] };