From 89f43cc9c94de4e2f1cd9ff1a9633588b814f440 Mon Sep 17 00:00:00 2001 From: kevin-lozada-santos Date: Sun, 13 Sep 2026 22:48:17 -0400 Subject: [PATCH] fix: allow pre-commit args to override gate defaults --- .pre-commit-hooks.yaml | 3 ++- CHANGELOG.md | 2 ++ README.md | 12 ++++++++++-- bin/avoid-ai-writing-gate.test.js | 28 ++++++++++++++++++++++++++++ 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 9153ac4..2477e23 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -1,7 +1,8 @@ - id: avoid-ai-writing name: avoid-ai-writing deterministic prose gate description: Fail when a staged Markdown file exceeds the configured detector finding count. - entry: avoid-ai-writing-gate --context technical --source-mode rendered-markdown --threshold 6 -- + entry: avoid-ai-writing-gate --context technical --source-mode rendered-markdown --threshold 6 + args: ["--"] language: node files: \.mdx?$ types: [text] diff --git a/CHANGELOG.md b/CHANGELOG.md index 24063f0..e55ca54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ All notable changes to this project are documented here. ## [Unreleased] +- Allow pre-commit `args` to override the gate defaults by moving the filename separator out of `entry` and into default `args` (#243). + ### Changed - Rename the user-facing "Emotional flatline" category to "Stock reaction framing" while preserving its `emotional-flatline` API type. Keep specific reactions, flag empty framing, and make the style finding neutral in authorship scoring until relevant positive evidence establishes a direction (#82). diff --git a/README.md b/README.md index 7b798dd..26887b2 100644 --- a/README.md +++ b/README.md @@ -436,8 +436,16 @@ repos: Pin `rev` to a release tag or commit SHA in shared repositories. The hook scans staged `.md` / `.mdx` files with the same **6-findings** corpus-backed default. -Override the entry in your pre-commit config when you need a stricter or more -permissive finding threshold. +Use `args` to override the threshold, context, or source mode: + +```yaml + - id: avoid-ai-writing + args: ["--threshold", "0", "--context", "technical", "--source-mode", "plain", "--"] +``` + +Pre-commit replaces the hook's default `args: ["--"]` when you provide `args`. +End an overriding list with `"--"` to protect filenames that begin with a dash. +The entry keeps its defaults; later options in `args` take precedence. The gate only **detects**. Preservation validation still requires an original and a rewritten file and remains a separate command: diff --git a/bin/avoid-ai-writing-gate.test.js b/bin/avoid-ai-writing-gate.test.js index ba2ad6d..4fb041b 100644 --- a/bin/avoid-ai-writing-gate.test.js +++ b/bin/avoid-ai-writing-gate.test.js @@ -69,5 +69,33 @@ assert.strictEqual(dashPrefixed.status, 1, dashPrefixed.stderr); assert.match(dashPrefixed.stdout, /-draft\.md/); assert.doesNotMatch(dashPrefixed.stderr, /unknown option/); +// Exercise entry + configured args + filenames in pre-commit's actual order. +// A separator baked into entry turns override options into filenames. +const manifest = fs.readFileSync(path.join(__dirname, "../.pre-commit-hooks.yaml"), "utf8"); +const entry = manifest.match(/^ entry: (.+)$/m)[1].trim().split(/\s+/).slice(1); +const defaultsMatch = manifest.match(/^ args: (.+)$/m); +const defaultArgs = defaultsMatch ? JSON.parse(defaultsMatch[1]) : []; +const hook = (args, filename = "-draft.md") => run([...entry, ...args, filename], gitRepo); +const hookDefault = hook(defaultArgs); +assert.strictEqual(hookDefault.status, 0, hookDefault.stderr); +assert.match(hookDefault.stdout, /threshold 6/); +const hookStrict = hook(["--threshold", "0", "--"]); +assert.strictEqual(hookStrict.status, 1, hookStrict.stderr); +assert.match(hookStrict.stdout, /^FAIL .*threshold 0/m); +const hookPermissive = hook(["--threshold", "999", "--"]); +assert.strictEqual(hookPermissive.status, 0, hookPermissive.stderr); +assert.match(hookPermissive.stdout, /threshold 999/); +fs.writeFileSync(path.join(gitRepo, "comment.md"), "\nThe deploy finished.\n", "utf8"); +assert.strictEqual(hook(["--threshold", "0", "--"], "comment.md").status, 0); +assert.strictEqual(hook(["--threshold", "0", "--source-mode", "plain", "--"], "comment.md").status, 1); +const hookContext = hook(["--context", "not-a-context", "--"]); +assert.strictEqual(hookContext.status, 2); +assert.match(hookContext.stderr, /context/); +assert.doesNotMatch(hookContext.stderr, /ENOENT/); +const hookSource = hook(["--source-mode", "not-a-mode", "--"]); +assert.strictEqual(hookSource.status, 2); +assert.match(hookSource.stderr, /source-mode/); +assert.doesNotMatch(hookSource.stderr, /ENOENT/); + fs.rmSync(tmp, { recursive: true, force: true }); console.log("avoid-ai-writing gate cli: ok");