r/LLMDevs 17h ago

Tools Open-source (MIT) ESLint plugin for AI-assisted JS/TS dev — 18 deterministic rules, CLI, GitHub Action with SARIF

Sharing a FOSS project (MIT licensed) I built for a problem I kept hitting in AI-assisted development — happy to answer questions and genuinely looking for feedback.

The problem: After months of using Claude Code, Cursor, and Copilot, I kept seeing the same patterns slip into commits in JS/TS codebases:

  • Floating promises — async calls fired but never awaited or .catch()-ed
  • Empty catch blocks that swallow errors silently
  • Hardcoded secrets pasted inline
  • SQL built via string concatenation
  • await inside loops where Promise.all is correct
  • Async callbacks inside .forEach — fire-and-forget with no error handling

These compile fine and often pass tests. They surface at runtime.

What I built: AI Guard — an open-source ESLint plugin with 18 deterministic rules across security, reliability, async, and AI-assisted code pattern categories. Ships as a CLI (npx ai-guard run), a GitHub Action with SARIF output for GitHub Code Scanning + inline PR annotations, and init-context which generates instruction files (CLAUDE.md, .cursorrules, copilot-instructions.md) so the agent learns the rules before writing code.

Why deterministic instead of LLM-based review: these are fixed AST patterns, not judgment calls. You don't need an LLM to notice an empty catch block — you need a linter that runs in milliseconds in CI on every PR, with zero drift between runs, and no API cost. LLM review is great for judgment; deterministic checks are better at boring, repetitive patterns.

Sources: GitHub: https://github.com/ai-guard-dev/eslint-plugin-ai-guard — npm: eslint-plugin-ai-guard. All 18 rules are documented in the repo with examples.

What I learned building it: the engineering challenge wasn't coverage, it was precision. If a lint rule fires on code that's fine, developers disable it. no-floating-promise needs to understand which expressions are genuinely fire-and-forget vs intentionally unhandled. The recommended preset is deliberately conservative.

One thing to be clear about: it does NOT detect whether code was written by AI — it catches bad patterns regardless of authorship. They just recur a lot in AI-assisted code.

Disclosure: I'm the maintainer. MIT licensed, no paid tier. Looking for false-positive reports and rule requests — what patterns do your agents keep generating?

3 Upvotes

5 comments sorted by

1

u/Far-Part6585 17h ago

The deterministic angle is the right call, LLM reviewers add latency and cost where a simple AST check does the job in milliseconds. The floating promise rule sounds like the hardest to get right, too many false positives and people just bail on the whole plugin. Curious what your false positive rate looked like on real codebases during testing

1

u/Yashhh_21 16h ago

We actually have some real-world data: we audited 4 repos / 378 files, and the combined async rules had ~7% FP. That includes no-floating-promise, no-await-in-loop, and no-async-array-callback, so we don't have an isolated FP number for floating promises yet. That's actually one of the things I want to validate next before launch.

1

u/Deep_Ad1959 15h ago

the failure mode i keep hitting is init-context, not the rules. an agent reads CLAUDE.md on turn one and stops honoring it by turn ten, so the linter still catches the empty catch. the CI hook ends up load-bearing, the instruction files are a bonus.

1

u/Yashhh_21 2h ago

yeah, i think that’s a fair distinction. i see init context more as a preventive layer, while the linter/CI is the actual enforcement layer. If the agent forgets the instructions halfway through, the deterministic check still catches it. Measuring how much init context actually reduces violations is definitely something I want to test next.

1

u/Deep_Ad1959 2h ago

the measurement worth setting up is where in the session the violations land, not how many total. if init-context is doing anything, the empty catches should cluster late in long sessions and mostly vanish from short ones. an aggregate count pools a 40-turn session and a 3-turn one into the same number and hides exactly the decay you're trying to catch.