From 1c865a97c9267bbc7267576f7e8a9fe3b3ececf1 Mon Sep 17 00:00:00 2001 From: Saturday-boyi <2174084306@qq.com> Date: Tue, 8 Sep 2026 09:15:25 +0800 Subject: [PATCH] fix: write duplicate ignore targets only once --- CHANGELOG.md | 6 ++++++ README.md | 2 ++ package-lock.json | 4 ++-- package.json | 2 +- src/ignore.js | 2 +- test/ctxtrim.test.js | 18 ++++++++++++++++++ 6 files changed, 30 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de55dc8..56aea10 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.5] - 2026-09-08 + +### Fixed + +- Write and report each ignore file only once when `--targets` contains duplicates. + ## [0.1.4] - 2026-09-07 ### Fixed diff --git a/README.md b/README.md index e3f364b..93cc266 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,8 @@ npx ctxtrim --fail-on-waste 40 # CI: exit 1 if 40%+ of context is junk **Ignore files it writes** (all real formats the tools honor): `.cursorignore` (Cursor), `.aiexclude` (Gemini Code Assist / Firebase Studio), `.aiignore` (generic). Writes are **idempotent** — a managed block between markers, so your own rules are preserved and re-runs just update the block. +Repeated names in `--targets` are ignored; each ignore file is written and reported once, in the order first requested. + ## What it is (and isn't) - A **fast, deterministic, zero-dependency** heuristic — no model calls, nothing uploaded. diff --git a/package-lock.json b/package-lock.json index c70f8c6..956b20d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ctxtrim", - "version": "0.1.4", + "version": "0.1.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ctxtrim", - "version": "0.1.4", + "version": "0.1.5", "license": "MIT", "bin": { "ctxtrim": "bin/ctxtrim.js" 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/ignore.js b/src/ignore.js index e6b7e01..5d3b89a 100644 --- a/src/ignore.js +++ b/src/ignore.js @@ -37,7 +37,7 @@ export function merge(existing, patterns) { /** @returns {{file, action:'created'|'updated', patterns:number}[]} */ export function writeIgnores(root, patterns, targets) { const results = []; - for (const t of targets) { + for (const t of new Set(targets)) { const fname = TARGETS[t]; if (!fname) continue; const path = join(root, fname); diff --git a/test/ctxtrim.test.js b/test/ctxtrim.test.js index ca72059..ece1339 100644 --- a/test/ctxtrim.test.js +++ b/test/ctxtrim.test.js @@ -185,3 +185,21 @@ test("unknown --targets values fail before writing ignore files", (t) => { assert.equal(existsSync(join(root, ".aiexclude")), false); assert.equal(existsSync(join(root, ".aiignore")), false); }); + +test("duplicate --targets writes and reports each ignore file once", (t) => { + const root = mkdtempSync(join(tmpdir(), "ctxtrim-duplicate-target-")); + t.after(() => rmSync(root, { recursive: true, force: true })); + writeFileSync(join(root, "package-lock.json"), "{}\n"); + + const result = spawnSync(process.execPath, [ + cli, root, "--write", "--format", "json", "--targets", "cursor, gemini,cursor,gemini", + ], { encoding: "utf8" }); + + assert.equal(result.status, 0, result.stderr); + assert.deepEqual(JSON.parse(result.stdout).wrote, [ + { file: ".cursorignore", action: "created", patterns: 1 }, + { file: ".aiexclude", action: "created", patterns: 1 }, + ]); + assert.equal(existsSync(join(root, ".cursorignore")), true); + assert.equal(existsSync(join(root, ".aiexclude")), true); +});