One command. Eight checks. Zero config.
Source bugs, dead links, API endpoints, accessibility, visual regression, performance, AI exploration, and cross-referencing. All in one pass.
You ship features. But did you actually test them?
sniff is an AI-powered QA tool that scans your project for real problems: leftover debugger statements, dead links, API endpoint issues, accessibility violations, visual regressions, performance budget breaches, and adversarial edge cases. It runs eight checks in a single command and cross-references source code with browser behavior for high-confidence findings.
npx sniff-qa
That's it. No config files. No signup. No API keys. It just works.
npx sniff-qa (zero config, no signup, no API keys required for base checks)| Step | Action |
|---|---|
| 1 | Run a source scan: npx sniff-qa |
| 2 | Run the full audit: npx sniff-qa --url http://localhost:3000 |
| 3 | Read the report and fix what matters |
The source scan finishes in seconds. The full audit adds accessibility, visual regression, performance, and AI exploration on top of it.
[!TIP] Drop your URL in
sniff.config.tsonce and you can just runsnifffor the full audit every time.
graph LR
A["๐ Quick Scan<br/><sub>npx sniff-qa</sub>"] --> B["Source bugs only<br/><sub>No browser needed</sub>"]
C["๐ก๏ธ Full Audit<br/><sub>npx sniff-qa --url URL</sub>"] --> D["All 8 checks<br/><sub>Source + Browser + Xref</sub>"]
E["๐๏ธ CI Mode<br/><sub>npx sniff-qa --url URL --ci</sub>"] --> F["Deterministic<br/><sub>Skips AI explorer</sub>"]
style A fill:#fef2f2,stroke:#ef4444,color:#7f1d1d
style B fill:#fee2e2,stroke:#ef4444,color:#7f1d1d
style C fill:#fef2f2,stroke:#ef4444,color:#7f1d1d
style D fill:#fee2e2,stroke:#ef4444,color:#7f1d1d
style E fill:#fef2f2,stroke:#ef4444,color:#7f1d1d
style F fill:#fee2e2,stroke:#ef4444,color:#7f1d1d
npx sniff-qa # quick scan (no browser)
npx sniff-qa --url http://localhost:3000 # full audit (everything)
npx sniff-qa --url http://localhost:3000 --ci # ci mode (skips AI explorer)
CI mode auto-skips the AI explorer because it is non-deterministic. Everything else runs exactly the same.
| Check | Example finding | Details | |
|---|---|---|---|
| ๐ | Source code | debugger statement in handler.ts:42 |
Leftover debugger, placeholder text, hardcoded URLs, broken imports, TODO/FIXME |
| ๐ | Dead links | Broken link ./guide.md in README.md:28 |
Validates internal file refs, external URLs (HTTP HEAD with retry), and anchor links. Catches 404s before your users do |
| ๐ฃ๏ธ | API endpoints | POST /users has no input validation |
Discovers routes from Express, Fastify, Hono, Next.js, SvelteKit, tRPC, and GraphQL. Flags missing error handling, auth, and hardcoded secrets |
| โฟ | Accessibility | Missing form label on /login |
WCAG 2.x violations via axe-core with exact fix guidance |
| ๐ผ | Visual regression | /pricing changed 2.3% of pixels |
Local pixel diffing via pixelmatch. Commit baselines to track changes |
| โก | Performance | LCP 4200ms on /dashboard (budget: 2500ms) |
Lighthouse budgets for LCP, FCP, TTI |
| ๐ค | AI explorer | XSS in /signup email field crashes app |
Roams your app, fills forms with adversarial inputs, reports crashes and console errors |
| ๐ | Cross-reference | console.log in source confirmed at runtime |
Correlates source findings with browser evidence. Corroborated issues get bumped severity and HIGH confidence tags |
Your code goes in. A report with actionable findings comes out. When you provide a URL, sniff also runs browser checks and cross-references source findings with runtime behavior for high-confidence results. You don't configure anything.
| Sniff | Lighthouse CI | Pa11y | BackstopJS | |
|---|---|---|---|---|
| Source scanning | โ | โ | โ | โ |
| Dead link checking | โ | โ | โ | โ |
| API endpoint discovery | โ | โ | โ | โ |
| Accessibility | โ | partial | โ | โ |
| Visual regression | โ | โ | โ | โ |
| Performance | โ | โ | โ | โ |
| AI exploration | โ | โ | โ | โ |
| Cross-reference engine | โ | โ | โ | โ |
| Flakiness quarantine | โ | โ | โ | โ |
| Single command | โ | โ | โ | โ |
| MCP server | โ | โ | โ | โ |
| Zero config | โ | โ | โ | โ |
[!IMPORTANT] Sniff is the first QA tool with native MCP integration. Your AI editor can trigger scans, read results, and fix issues without you switching context.
Sniff ships 6 rule categories that run on every scan. No browser needed.
| Rule | ID | Severity | What it catches |
|---|---|---|---|
| Debug statements | debug-console-log, debug-debugger |
medium / high | console.log, console.debug, debugger left in production code |
| Placeholder text | placeholder-lorem, placeholder-todo, placeholder-fixme, placeholder-tbd |
high / medium | Lorem ipsum, TODO, FIXME, TBD markers |
| Hardcoded URLs | hardcoded-localhost, hardcoded-127 |
medium | http://localhost:* and http://127.0.0.1:* in non-test files |
| Broken imports | broken-import |
medium | Relative imports that don't resolve to a file (with TS extension mapping) |
| Dead links | dead-link-internal, dead-link-external, dead-link-anchor |
high / medium / low | Broken file references, 404 external URLs, missing anchor targets |
| API endpoints | api-no-error-handling, api-no-validation, api-no-auth, api-hardcoded-secret |
critical / medium / low | Route handlers missing try/catch, input validation, auth, or containing hardcoded secrets |
Scans .md, .html, .jsx, .tsx, .vue, .svelte, and .astro files for links and validates them:
#section references against markdown headings and HTML id/name attributesmailto:, tel:, data:, javascript:, and template variablesHIGH README.md:28 Broken internal link: ./missing-guide.md
MED docs/api.md:15 HTTP 404: https://example.com/old-endpoint
MED CONTRIBUTING.md:8 Anchor #setup-guide not found in ./README.md
Automatically discovers API routes from 8 frameworks:
| Framework | Detection method |
|---|---|
| Express | app.get(), router.post(), etc. |
| Fastify | fastify.get(), fastify.post(), etc. |
| Hono | app.get() with new Hono() detection |
| Next.js App Router | app/**/route.ts with exported GET/POST handlers |
| Next.js Pages Router | pages/api/**/*.ts |
| SvelteKit | routes/**/+server.ts |
| tRPC | publicProcedure.query() / .mutation() |
| GraphQL | type Query { } / type Mutation { } definitions |
For each endpoint, sniff checks for:
CRIT src/routes.ts:12 Hardcoded Stripe key in POST /checkout handler
MED src/api.ts:5 POST /users has no input validation
MED src/api.ts:18 GET /admin/data has no error handling
LOW src/api.ts:25 GET /admin/data has no visible auth middleware
INFO Discovered 6 API endpoints (express: 4, nextjs-app: 2)
When you run sniff with --url, the cross-reference engine correlates source code findings with browser runtime behavior. Findings confirmed in both layers get bumped severity and a HIGH confidence tag.
5 correlation strategies:
| Source finding | Browser evidence | Correlation |
|---|---|---|
| Broken import / dead link | 404 network request | broken-import-to-404 |
console.log statement |
Console output captured | console-log-to-runtime |
| Hardcoded localhost URL | Network request to that URL | hardcoded-url-to-network |
| Missing label / a11y issue | axe-core violation | source-a11y-to-axe |
| Placeholder text in source | Text visible on page | placeholder-to-runtime |
CORROBORATED (source + browser evidence)
src/handler.ts:42 console.log("debug data")
Source: debug-console-log rule triggered
Browser: console.log output captured at /dashboard
Confidence: HIGH (confirmed in both layers)
No config needed. Sniff auto-detects your framework from package.json and source files.
| โ๏ธ React | โฒ Next.js | ๐ Vue | ๐ถ Svelte | ๐ ฐ๏ธ Angular | ๐ Vanilla |
| JSX / TSX | App Router | SFC | Components | Templates | HTML / CSS |
Sniff ships an MCP server. Pick your tool and copy the snippet.
claude mcp add sniff-qa npx sniff-qa --mcp
Or .mcp.json:
{
"mcpServers": {
"sniff-qa": { "command": "npx", "args": ["sniff-qa", "--mcp"] }
}
}
~/.cursor/mcp.json or .cursor/mcp.json:
{
"mcpServers": {
"sniff-qa": {
"type": "stdio",
"command": "npx",
"args": ["sniff-qa", "--mcp"]
}
}
}
~/.codeium/windsurf/mcp_config.json:
{
"mcpServers": {
"sniff-qa": { "command": "npx", "args": ["sniff-qa", "--mcp"] }
}
}
codex mcp add sniff-qa -- npx -y sniff-qa --mcp
Or add to ~/.codex/config.toml:
[mcp_servers.sniff-qa]
command = "npx"
args = ["-y", "sniff-qa", "--mcp"]
~/.gemini/mcp_config.json:
{
"mcpServers": {
"sniff-qa": { "command": "npx", "args": ["sniff-qa", "--mcp"] }
}
}
.continue/mcpServers/sniff-qa.yaml:
mcpServers:
sniff-qa:
command: npx
args: [sniff-qa, --mcp]
type: stdio
Add to .vscode/mcp.json:
{
"servers": {
"sniff-qa": {
"type": "stdio",
"command": "npx",
"args": ["-y", "sniff-qa", "--mcp"]
}
}
}
clawhub install sniff-qa
Or add to ~/.openclaw/openclaw.json:
{
"mcpServers": {
"sniff-qa": {
"command": "npx",
"args": ["-y", "sniff-qa", "--mcp"]
}
}
}
Once configured, ask: "Scan this project for issues" or "Check accessibility on localhost:3000".
MCP tools exposed: sniff_scan (source), sniff_run (browser), sniff_report (last results).
npm install -D sniff-qa
{
"scripts": {
"qa": "sniff --url http://localhost:3000"
}
}
Then run npm run qa. Requires Node.js 22+. Playwright browsers install automatically the first time.
npx sniff-qa ci
Generates .github/workflows/sniff.yml with Playwright caching, JUnit output, flakiness quarantine, and report artifacts.
Flakiness quarantine. Tests that fail 3 of 5 runs get quarantined. They still run, still appear in reports, but won't block your pipeline.
sniff quick scan (source only)
sniff --url <url> full audit (everything)
sniff --url <url> --ci ci mode (no AI explorer)
sniff init scaffold sniff.config.ts
sniff ci generate .github/workflows/sniff.yml
sniff report show last results
sniff update-baselines accept current screenshots as baselines
| Flag | Effect |
|---|---|
--no-explore |
Skip AI explorer in full mode |
--no-browser |
Force source-only even when URL is set |
--max-steps <n> |
Cap exploration steps (default: 50) |
--no-headless |
Show the browser window |
--format html,json,junit |
Choose report formats |
--fail-on critical,high |
Severities that exit non-zero |
--track-flakes |
Enable flakiness detection |
--json |
Machine-readable output |
Optional. Drop sniff.config.ts in your project root:
import { defineConfig } from 'sniff-qa';
export default defineConfig({
browser: {
baseUrl: 'http://localhost:3000',
},
viewports: [
{ name: 'mobile', width: 375, height: 667 },
{ name: 'desktop', width: 1280, height: 720 },
],
performance: {
budgets: { lcp: 2500, fcp: 1800, tti: 3800 },
},
visual: { threshold: 0.1 },
exploration: { maxSteps: 50 },
flakiness: { windowSize: 5, threshold: 3 },
// Dead link checker (new in v0.2)
deadLinks: {
checkExternal: true, // validate external URLs via HTTP
timeout: 5000, // ms per request
retries: 2, // retry failed requests
ignorePatterns: [], // regex patterns to skip
maxConcurrent: 10, // parallel HTTP requests
},
// API endpoint discovery (new in v0.2)
apiEndpoints: {
checkErrorHandling: true, // flag missing try/catch
checkValidation: true, // flag missing input validation
checkAuth: true, // flag missing auth middleware
checkSecrets: true, // flag hardcoded secrets
frameworks: [], // empty = auto-detect all
},
// Disable specific rules
rules: {
'debug-console-log': 'off', // example: allow console.log
'dead-link-internal': 'off', // example: skip dead link checks
},
});
| ๐ซ | No telemetry. Sniff does not phone home. Ever. |
| ๐ | No signup. No accounts. No API keys for core functionality. |
| ๐ฆ | No data collection. Your code stays on your machine. |
| ๐๏ธ | Open source. Read every line. Apache 2.0. |
[!NOTE] The AI explorer requires an Anthropic API key only if you want the chaos monkey feature. The other seven checks (source scanning, dead links, API endpoints, accessibility, visual regression, performance, cross-referencing) work completely offline with zero external calls. External link checking in the dead link scanner makes HTTP requests to validate URLs, but never sends your code.
Playwright ยท axe-core ยท Lighthouse ยท pixelmatch ยท Zod ยท MCP SDK ยท Anthropic SDK
Easiest way in: add a source rule. Each rule is a regex pattern with a severity level in src/scanners/source/rules/. See CONTRIBUTING.md.
Good first contributions:
Built by Adam Boudjemaa ยท Apache 2.0 ยท No telemetry ยท No data collection