Skip to content

fix(detector): add --source-mode to the bundled detect.js - #340

Merged
conorbronsdon merged 2 commits into
conorbronsdon:mainfrom
ayduanzf-hub:fix/244-detect-source-mode
Sep 23, 2026
Merged

conorbronsdon merged 2 commits into
conorbronsdon:mainfrom
ayduanzf-hub:fix/244-detect-source-mode

Conversation

@ayduanzf-hub

@ayduanzf-hub ayduanzf-hub commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Summary

skills/ai-writing-detector/scripts/detect.js supported only --file and --context, so the published plugin could not reach rendered-Markdown scoring. On a Markdown file with YAML frontmatter the root CLI returns [0, "Clean", []] while the bundled script flags all three frontmatter lines, and passing the flag threw an uncaught unknown argument stack trace instead of the root CLI's exit-2 message.

Ported the one flag, as scoped in #244. detect.js keeps its --file/--context interface and the root CLI keeps its positional path; the two are not unified.

  • Add --source-mode <plain|rendered-markdown>, validated and passed through as sourceMode to analyzeText. No detector change was needed: the masking already lives in detector/patterns.js.
  • Accept the marketing and personal contexts that bin/avoid-ai-writing.js and VALID_CONTEXT_MODES in patterns.js already support. The bundled parser rejected both, so an invocation the root CLI accepts exited 2 here.
  • Mirror the root CLI's empty-result normalization, so blank input reports the selected context and source mode instead of stats: {}.
  • Replace the throw new Error(...) calls with a usage message and process.exit(2), matching the root CLI, and add -h/--help.
  • Add scripts/detect-parity.test.js, registered in scripts/run-tests.js. It spawns both entry points on the same file, compares their JSON for every supported context and both source modes, covers empty and whitespace-only input, asserts rendered-markdown returns Clean where plain mode scores the frontmatter, and pins the exit-2 error cases (usage on stderr, nothing on stdout, no stack trace).
  • Document the flag in skills/ai-writing-detector/SKILL.md next to --context.
  • Add an ## [Unreleased] changelog entry, since this changes the published plugin.

On the same fixture through the bundled script:

mode issues score label
plain (default) 7 35 Some AI patterns
rendered-markdown 0 0 Clean

All four contexts and both source modes produce byte-identical JSON through both entry points, including on blank input.

Neither sync script generates detect.js — sync-plugin-skill.sh copies only patterns.js, validate.js, and CATEGORIES.md into the bundled directory — so the drift this issue reports cannot recur through them. Running both leaves the tree clean apart from the five files here.

Checklist

  • npm test passes (engine fixtures + CATEGORIES.md contract check)
  • If I added a detector type: it's documented in detector/CATEGORIES.md and has a fixture in detector/patterns.test.js (a true positive and a must-not-fire case) — not applicable, no detector type added
  • If I added a judgment-only rule: it's listed under "Skill-only" in detector/CATEGORIES.md — not applicable
  • I considered false positives and added carve-outs for legitimate human writing — this change removes a false-positive class rather than adding one: unedited frontmatter scored as the author's prose
  • Any factual claim about how AI or humans write (e.g. "ChatGPT emits X", "humans rarely do Y") cites a source — not applicable, no such claim
  • The prose I added passes the skill's own audit (no AI-writing tells, terse bullets, no hollow intensifiers)
  • I added an Unreleased CHANGELOG.md entry for a user-facing change, or this PR is exempt under the changelog policy
  • If this PR prepares a release: the dated changelog heading and skill/package/plugin versions agree — not applicable, not a release PR
  • If I added or removed a detection ### in references/patterns.md: the **NN pattern categories** bullet in README.md and the quoted count in CLAUDE.md match scripts/check-pattern-count.sh — not applicable, no category change

Fixes #244

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Add source-mode parity to the bundled detector CLI

🐞 Bug fix 🧪 Tests 📝 Documentation 🕐 10-20 Minutes

Grey Divider

AI Description

• Exposes rendered-Markdown source mode through the bundled detector script.
• Aligns argument failures and help behavior with the root CLI.
• Adds parity coverage and plugin documentation for the new flag.
Diagram

graph TD
  Caller["Plugin caller"] --> CLI["Bundled detect.js"] --> Parse{"Valid options?"}
  Parse -->|Valid| Input["File or stdin"] --> Detector["analyzeText"] --> Output["JSON result"]
  Parse -->|Invalid| Usage["Usage and exit 2"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Share CLI parsing utilities
  • ➕ Reduces future option drift between the root and bundled entry points.
  • ➕ Centralizes validation, help formatting, and exit-code behavior.
  • ➖ Requires reconciling intentionally different file arguments and supported contexts.
  • ➖ Could introduce packaging coupling for a narrowly scoped plugin fix.

Recommendation: Keep the PR's scoped flag port and parity tests. Extracting shared parsing could prevent future drift, but the entry points intentionally expose different contracts; consolidating them would add risk and scope without improving this fix.

Files changed (5) +198 / -22

Bug fix (1) +95 / -19
detect.jsSupport source mode and controlled CLI errors +95/-19

Support source mode and controlled CLI errors

• Adds validated '--source-mode' and help options, forwarding the selected mode to 'analyzeText'. Refactors argument and input failures to print usage on stderr and exit with status 2 instead of throwing uncaught errors.

skills/ai-writing-detector/scripts/detect.js

Tests (2) +89 / -0
detect-parity.test.jsAdd bundled and root CLI parity tests +88/-0

Add bundled and root CLI parity tests

• Spawns both detector entry points against a Markdown fixture and compares their rendered-mode JSON. It also verifies frontmatter masking, default plain behavior, help output, and stack-trace-free usage errors.

scripts/detect-parity.test.js

run-tests.jsRegister detector parity suite +1/-0

Register detector parity suite

• Adds the new bundled detector parity test to the default test-suite execution order.

scripts/run-tests.js

Documentation (2) +14 / -3
CHANGELOG.mdDocument bundled source-mode support +4/-0

Document bundled source-mode support

• Adds an Unreleased entry describing rendered-Markdown support and controlled exit-2 argument failures in the published plugin.

CHANGELOG.md

SKILL.mdDocument rendered-Markdown detector usage +10/-3

Document rendered-Markdown detector usage

• Explains when to select rendered-Markdown mode and adds a command example for files containing YAML frontmatter or HTML comments.

skills/ai-writing-detector/SKILL.md

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (1) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Marketing scans fail in the plugin ✓ Resolved 📎 Requirement gap ≡ Correctness
Description
parseArgs restricts --context to general and technical, although the root CLI and shared
detector also accept marketing and personal. Invoking the bundled detector with either valid
root context now exits 2 before analysis, so equivalent inputs never produce the root CLI result.
Code

skills/ai-writing-detector/scripts/detect.js[29]

+const CONTEXTS = ["general", "technical"];
Evidence
Compliance rule 1 requires equivalent inputs and flags to produce the same behavior. The new bundled
parser permits only two contexts, while the root CLI and shared detector explicitly accept four,
causing the bundled command to reject valid root-CLI invocations.

Ensure bundled detector matches root CLI behavior
skills/ai-writing-detector/scripts/detect.js[29-30]
bin/avoid-ai-writing.js[27-28]
skills/ai-writing-detector/scripts/patterns.js[1694-1700]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The bundled detector rejects the valid `marketing` and `personal` contexts supported by both the root CLI and the shared detector, breaking behavioral parity.
## Fix Focus Areas
- skills/ai-writing-detector/scripts/detect.js[29-29]
- scripts/detect-parity.test.js[42-47]
## Recommended Fix
Add `marketing` and `personal` to the bundled parser's accepted contexts, update its usage text, and extend the parity test to compare both entry points for every supported context.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Empty scans omit selected modes 📎 Requirement gap ≡ Correctness
Description
main serializes the raw analyzeText result without the root CLI's empty-result normalization
that adds the selected contextMode and sourceMode to stats. Empty files or whitespace-only
stdin therefore return stats: {}—including omission of an explicitly selected rendered-markdown
source mode—so bundled and root outputs diverge for equivalent inputs and flags.
Code

skills/ai-writing-detector/scripts/detect.js[R99-102]

+  const result = AIDetector.analyzeText(input.text, {
+    contextMode: parsed.context,
+    sourceMode: parsed.sourceMode,
+  });
Evidence
The detector returns early with stats: {} for empty text, before processing sourceMode or
contextMode, while the root CLI compensates by inserting both parsed modes before serialization.
The bundled entry point passes the new option but serializes the unnormalized result, producing
different JSON from the root CLI for valid empty or whitespace-only input and violating the required
parity for equivalent inputs and flags.

Ensure bundled detector matches root CLI behavior
skills/ai-writing-detector/scripts/detect.js[99-103]
bin/avoid-ai-writing.js[125-133]
skills/ai-writing-detector/scripts/patterns.js[1680-1682]
detector/patterns.js[1678-1681]
bin/avoid-ai-writing.js[119-127]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The bundled detector emits empty `stats` for blank input because it omits the root CLI's normalization, so its output does not expose the selected source or context mode and differs from the root CLI.
## Fix Focus Areas
- skills/ai-writing-detector/scripts/detect.js[99-103]
- scripts/detect-parity.test.js[42-61]
## Recommended Fix
After `analyzeText`, mirror the root CLI's empty-result normalization by setting `result.stats.contextMode` and `result.stats.sourceMode` when `stats` is empty. Add parity coverage for both empty and whitespace-only input with explicit flags, including `--source-mode rendered-markdown`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can keep summaries lean with Finding overflow, which tucks the rest behind 'View more'

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread skills/ai-writing-detector/scripts/detect.js Outdated
Comment thread skills/ai-writing-detector/scripts/detect.js
@ayduanzf-hub
ayduanzf-hub force-pushed the fix/244-detect-source-mode branch from e91e5a1 to 0948bbc Compare September 23, 2026 07:43
skills/ai-writing-detector/scripts/detect.js supports only --file and
--context, so the published plugin cannot reach rendered-Markdown scoring.
On a Markdown file with YAML frontmatter the root CLI reports Clean while the
bundled script flags all three frontmatter lines, and passing the flag throws
an uncaught "unknown argument" stack trace instead of the root CLI's exit-2
message.

Port the one flag, as scoped in conorbronsdon#244, and close the parity gaps in the two
paths the bundled script shares with the root CLI:

- add --source-mode <plain|rendered-markdown>, validated, passed through as
  sourceMode to analyzeText;
- accept the marketing and personal contexts the root CLI and the shared
  detector already support, so equivalent invocations agree instead of the
  bundled parser rejecting two valid values;
- mirror the root CLI's empty-result normalization, so blank input reports the
  selected context and source mode rather than an empty stats object;
- replace the throws on a bad argument with a usage message and exit 2,
  matching the root CLI (including -h/--help);
- add scripts/detect-parity.test.js, which spawns both entry points on the
  same file, compares the JSON across every supported context and both source
  modes, covers empty and whitespace-only input, asserts rendered-markdown
  returns Clean where plain mode scores the frontmatter, and pins the exit-2
  error cases without a stack trace;
- document the flag in the sub-skill SKILL.md next to --context;
- register the new suite in scripts/run-tests.js.

detect.js keeps its --file/--context interface; the root CLI keeps its
positional path. Only the flag is ported.

Refs conorbronsdon#244
@ayduanzf-hub
ayduanzf-hub force-pushed the fix/244-detect-source-mode branch from 0948bbc to 6c06cc6 Compare September 23, 2026 07:45
@ayduanzf-hub

Copy link
Copy Markdown
Contributor Author

Both review findings are addressed in 6c06cc6, and CI is green on that commit (test, check, ssot).

  • Contexts (0948bbc) — the bundled parser now accepts marketing and personal alongside general and technical, matching CONTEXTS in bin/avoid-ai-writing.js and VALID_CONTEXT_MODES in patterns.js. The parity test compares both entry points across all four contexts instead of one.
  • Blank input (6c06cc6) — detect.js mirrors the root CLI's empty-result normalization, so empty and whitespace-only input report the selected contextMode and sourceMode rather than stats: {}.

The review summary still lists one open requirement gap; that reflects 0948bbc, before the second fix landed. Verified on the pushed branch: empty input with --context marketing --source-mode rendered-markdown returns {"contextMode":"marketing","sourceMode":"rendered-markdown"} from both entry points, byte-identical.

The new suite is registered in scripts/run-tests.js, so the npm test job covers it.

@conorbronsdon conorbronsdon left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, @ayduanzf-hub — this is a careful follow-up, especially the all-context and blank-input parity coverage. I restored the executable bit on detect.js in 68b59f5; no source bytes changed. The full suite, plugin validator, sync checks, mutation control, and all hosted checks are green. Approved.

One contributor-policy note for next time: #244 was later labeled good first issue. We reserve those issues for one first contribution per person, including while that first PR awaits review. I'm happy to land this one because the issue was not originally presented that way, but please choose a help wanted issue without good first issue for your next contribution. A few unclaimed options:

  • #85 — the documentation or conversational corpus leg
  • #94 — the Vale package exploration; coordinate the repository and coverage decision first
  • #238 — quoted-material masking; comment first so we can resolve the listed design choices

Thanks again for both contributions.

@conorbronsdon
conorbronsdon merged commit d5e524e into conorbronsdon:main Sep 23, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add --source-mode to the bundled ChatGPT/Codex detector script

2 participants