diff --git a/CHANGELOG.md b/CHANGELOG.md index c2c5d80..6f15712 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project are documented here, following [Keep a Changelog](https://keepachangelog.com/) and semantic versioning. +## [0.1.13] - 2026-09-16 + +### Fixed + +- Repair orphan managed-block end markers instead of duplicating them. + ## [0.1.12] - 2026-09-16 ### Fixed diff --git a/package-lock.json b/package-lock.json index 71e5608..fc7b59a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ctxtrim", - "version": "0.1.12", + "version": "0.1.13", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ctxtrim", - "version": "0.1.12", + "version": "0.1.13", "license": "MIT", "bin": { "ctxtrim": "bin/ctxtrim.js" diff --git a/package.json b/package.json index 95b3207..d1e1015 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ctxtrim", - "version": "0.1.12", + "version": "0.1.13", "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/ignore.js b/src/ignore.js index 5d3b89a..9d19e8c 100644 --- a/src/ignore.js +++ b/src/ignore.js @@ -22,7 +22,7 @@ export function block(patterns) { export function merge(existing, patterns) { const b = block(patterns); const startIndex = existing.indexOf(START); - const endIndex = existing.indexOf(END, startIndex + START.length); + const endIndex = existing.indexOf(END, startIndex === -1 ? 0 : startIndex + START.length); if (startIndex !== -1 && endIndex !== -1) { return existing.replace(new RegExp(escape(START) + "[\\s\\S]*?" + escape(END)), b).trimEnd() + "\n"; } @@ -30,6 +30,10 @@ export function merge(existing, patterns) { const base = existing.slice(0, startIndex).trim(); return (base ? base + "\n\n" : "") + b + "\n"; } + if (endIndex !== -1) { + const base = (existing.slice(0, endIndex) + existing.slice(endIndex + END.length)).trim(); + return (base ? base + "\n\n" : "") + b + "\n"; + } const base = existing.trim(); return (base ? base + "\n\n" : "") + b + "\n"; } diff --git a/test/ctxtrim.test.js b/test/ctxtrim.test.js index 0ccd82c..88e3864 100644 --- a/test/ctxtrim.test.js +++ b/test/ctxtrim.test.js @@ -158,6 +158,14 @@ test("ignore merge repairs a managed block with no end marker", () => { assert.equal((repaired.match(/ctxtrim \(managed\)/g) || []).length, 2); }); +test("ignore merge repairs an orphan end marker", () => { + const existing = "# my own rule\n# <<< ctxtrim (managed) <<<\n"; + const repaired = merge(existing, ["dist/"]); + assert.equal((repaired.match(/ctxtrim \(managed\)/g) || []).length, 2); + assert.ok(repaired.includes("# my own rule")); + assert.ok(repaired.includes(block(["dist/"]))); +}); + test("clean repo (only source) reports nothing to trim", () => { // scanning the src subdir alone = only source const s = scanRepo(join(repo, "src"));