From 8ac39444a8b7a984ba1bfbb6b4bd2e7c0edd71f7 Mon Sep 17 00:00:00 2001 From: wellparth Date: Mon, 7 Sep 2026 23:30:32 +0530 Subject: [PATCH] fix: --fail-on-waste 0 no longer fails clean repos wastePct (0) >= 0 is always true, so a clean repo could never pass a 0% threshold and CI wiring --fail-on-waste 0 failed every build. Require actual waste (> 0%) to trip the threshold; all non-zero thresholds keep their existing >= semantics and boundary behaviour. Closes #19 --- CHANGELOG.md | 7 +++++++ package.json | 2 +- src/cli.js | 4 +++- test/ctxtrim.test.js | 24 ++++++++++++++++++++++++ 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de55dc8..169afa3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,13 @@ All notable changes to this project are documented here, following [Keep a Changelog](https://keepachangelog.com/) and semantic versioning. +## [0.1.5] - 2026-09-07 + +### Fixed + +- `--fail-on-waste 0` no longer fails every build: a 0% waste (clean) repo now + passes, while any actual waste still trips the threshold. + ## [0.1.4] - 2026-09-07 ### Fixed diff --git a/package.json b/package.json index fe13b85..e5d9185 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ctxtrim", - "version": "0.1.4", + "version": "0.1.5", "description": "Trim what bloats your AI coding context. Scan a repo, find the high-cost/low-value files ballooning your Claude Code / Cursor / Codex context, and write ignore files to cut token cost. Zero dependencies.", "type": "module", "bin": { diff --git a/src/cli.js b/src/cli.js index c357b08..75972fe 100644 --- a/src/cli.js +++ b/src/cli.js @@ -87,6 +87,8 @@ export function run(argv, { version }) { if (o.format === "json") process.stdout.write(jsonReport(scan, { price: o.price, wrote }) + "\n"); else process.stdout.write(textReport(scan, { price: o.price, top: o.top, wrote })); - if (o.failOnWaste != null && scan.totals.wastePct >= o.failOnWaste) return 1; + // wastePct >= 0 is always true, so a 0-waste (clean) repo would never pass + // --fail-on-waste 0; require actual waste to trip the threshold. + if (o.failOnWaste != null && scan.totals.wastePct >= o.failOnWaste && scan.totals.wastePct > 0) return 1; return 0; } diff --git a/test/ctxtrim.test.js b/test/ctxtrim.test.js index ca72059..1d62b67 100644 --- a/test/ctxtrim.test.js +++ b/test/ctxtrim.test.js @@ -152,6 +152,30 @@ test("clean repo (only source) reports nothing to trim", () => { assert.equal(s.totals.trimTokens, 0); }); +test("CLI exits 0 on a clean repo with --fail-on-waste 0", () => { + const result = spawnSync(process.execPath, [cli, join(repo, "src"), "--fail-on-waste", "0"], { + encoding: "utf8", + }); + assert.equal(result.status, 0, `wastePct 0 should not trip threshold 0\n${result.stderr}`); +}); + +test("CLI still fails with --fail-on-waste 0 when there is waste", () => { + const result = spawnSync(process.execPath, [cli, repo, "--fail-on-waste", "0"], { encoding: "utf8" }); + assert.equal(result.status, 1, "a dirty repo should still fail threshold 0"); +}); + +test("CLI fails only when waste reaches the threshold", () => { + // the fixture is 100% waste: threshold equal to waste must still fail... + const equal = spawnSync(process.execPath, [cli, repo, "--fail-on-waste", "100"], { encoding: "utf8" }); + assert.equal(equal.status, 1, "waste at the threshold must fail"); + // ...and a 1% threshold still catches it + const low = spawnSync(process.execPath, [cli, repo, "--fail-on-waste", "1"], { encoding: "utf8" }); + assert.equal(low.status, 1, "1% threshold should fail a repo with waste"); + // while a clean repo sits well below the threshold + const pass = spawnSync(process.execPath, [cli, join(repo, "src"), "--fail-on-waste", "50"], { encoding: "utf8" }); + assert.equal(pass.status, 0, `clean repo should pass a 50%% threshold\n${pass.stderr}`); +}); + test("CLI rejects non-finite and negative numeric options", () => { const invalid = [ ["--max-tokens", "abc"],