# Detect in CI

> Run impeccable detect non-interactively with --json/--quiet/--scope, interpret exit codes 0 and 2, apply project ignores, and wire optional Puppeteer URL scans.

- Repository: pbakaus/impeccable
- GitHub: https://github.com/pbakaus/impeccable
- Human docs: https://grok-wiki.com/public/docs/pbakaus-impeccable-adadc04d8de4
- Complete Markdown: https://grok-wiki.com/public/docs/pbakaus-impeccable-adadc04d8de4/llms-full.txt

## Source Files

- `cli/engine/cli/main.mjs`
- `cli/bin/cli.js`
- `README.npm.md`
- `package.json`
- `cli/lib/impeccable-config.mjs`
- `scripts/benchmark-detector.mjs`

---

---
title: "Detect in CI"
description: "Run impeccable detect non-interactively with --json/--quiet/--scope, interpret exit codes 0 and 2, apply project ignores, and wire optional Puppeteer URL scans."
---

`npx impeccable detect` is the non-interactive anti-pattern gate for local automation and CI. It scans HTML, CSS, JS/TS, JSX/TSX, Vue, Svelte, Astro, and related sources under a path, or renders live pages when given `http(s)://` or `file://` URLs. The published CLI package is `impeccable` (Node `>=22.18.0`); Puppeteer is an optional dependency used only for URL scans.

## When to use detect in CI

| Goal | Command shape | Exit signal |
| --- | --- | --- |
| Fail the job on design anti-patterns | `npx impeccable detect --json src/` | `2` if non-advisory findings |
| Log-only gate (no fail) | same, ignore exit `2` | still print JSON on stdout |
| Minimal text noise | `npx impeccable detect --quiet src/` | summary on stderr; `2` still applies |
| Scope to typography or layout rules | `--scope type` or `--scope layout` | filters findings after scan |
| Rendered page (browser rules) | `detect http://localhost:3000` | requires Puppeteer + Chrome |

Static file scans need no browser. URL scans need Puppeteer installed in the job image (see [URL scans with Puppeteer](#url-scans-with-puppeteer)).

## Prerequisites

- Node.js `>=22.18.0`
- Project checkout with the paths you want scanned
- Optional: `.impeccable/config.json` and/or `.impeccable/config.local.json` for detector ignores
- Optional: `puppeteer` (package `optionalDependencies`) for URL targets

## Non-interactive flags

CI should prefer flags that avoid prompts and noisy stderr.

| Flag | Effect in CI |
| --- | --- |
| `--json` | Findings as a JSON array on **stdout**. Clean scan prints `[]`. Skips framework “scan via URL” suggestions and the large-tree confirm prompt. |
| `--quiet` | Text mode only: one line summary on **stderr** (`N anti-pattern(s) found.`). No per-finding detail. Silent when clean. Does not change JSON when combined with `--json`. |
| `--scope <name>` | Comma-separated design domains: `type`, `layout`. Unknown or empty values exit `1`. |
| `--viewport WxH` | Browser viewport for URL scans (default `1280x800`). Example: `--viewport 390x844`. |
| `--no-config` | Ignore project detector config, inline `impeccable-disable*` comments, and DESIGN.md loading. |
| `--no-inline-ignores` | Keep config ignores; ignore in-file disable comments only. |
| `--no-design-system` | Do not load DESIGN.md / `.impeccable/design.json` per target. |
| `--no-advisory` | Drop advisory findings before output and exit-code math. |
| `--fast` | Deprecated no-op; full static scan still runs (stderr note). |

Text findings (non-JSON) write to **stderr**. JSON findings write to **stdout**. That split matters when you capture artifacts with `> findings.json`.

### Argument parsing notes

- `impeccable detect …` and the shorthand `impeccable src/` both route to detect.
- Bare `-json` is accepted as `--json`.
- Default target when none is given: `process.cwd()`.
- Directory walks skip `node_modules`, `dist`, `build`, `__pycache__`, and most hidden dirs (exceptions: `.vitepress`, `.vuepress`, `.storybook`). An explicitly passed hidden path still scans.

## Exit codes

| Code | Meaning |
| --- | --- |
| `0` | No non-advisory (“primary”) findings. Clean files, empty JSON `[]`, or advisory-only results. |
| `2` | At least one primary finding remains after ignores and scope filters. |
| `1` | CLI usage or runtime error (bad `--scope`, bad `--viewport`, unknown top-level command, uncaught exception). |
| `130` | Interactive prompt abort (`IMPECCABLE_PROMPT_ABORT`); not expected in non-TTY CI. |

Primary vs advisory:

- Registry rules with `advisory: true` (currently `em-dash-overuse`) appear in output but **do not** set exit `2`.
- In text mode they render under a separate “Advisory” section; in JSON each carries `"advisory": true`.
- Use `--no-advisory` to omit them entirely.

<Warning>
Do not treat every non-zero exit as “anti-patterns found.” Exit `1` is a tool/config failure; exit `2` is a findings gate. Wire CI steps accordingly.
</Warning>

## Project ignores and config

Without `--no-config`, detect loads from the current working directory:

1. `.impeccable/config.json` (shared)
2. `.impeccable/config.local.json` (local overlay)

Relevant keys under `detector`:

```json
{
  "detector": {
    "ignoreRules": ["side-tab"],
    "ignoreFiles": ["**/legacy/**", "storybook-static/**"],
    "ignoreValues": [
      { "rule": "overused-font", "value": "roboto" }
    ],
    "designSystem": { "enabled": true }
  }
}
```

| Key | Role at scan time |
| --- | --- |
| `ignoreRules` | Drop all findings for those rule ids |
| `ignoreFiles` | Skip matching files before analysis (globs) |
| `ignoreValues` | Drop value-scoped findings (e.g. one intentional font) |
| `designSystem.enabled` | When not `false`, load per-target DESIGN.md context |

Inline file comments still apply unless `--no-config` or `--no-inline-ignores`:

```html
<!-- impeccable-disable overused-font -- brand doc -->
```

```css
.brand { font-family: Inter } /* impeccable-disable-line overused-font */
```

`hook.enabled` only controls design-hook auto-runs; manual `detect` still runs when the hook is off, but shares the same detector ignore filters.

Manage ignores via `impeccable ignores …` (see related pages) rather than hand-editing CI-only copies unless you need ephemeral overrides with `--no-config`.

## Detection modes

```text
targets[]
  ├─ path → directory walk → HTML: static HTML/CSS engine
  │                      └─ other: regex/text engine
  ├─ path → single file → same split by extension
  └─ http(s):// or file:// → Puppeteer browser engine
```

- **HTML** (`.html`, `.htm`): static HTML/CSS analysis (linked stylesheets included).
- **Non-HTML scannable files**: regex/text path (CSS, JSX, TSX, Vue, Svelte, etc.).
- **URLs**: full browser render; layout and visual rules that need computed style.

Per-file DESIGN.md resolution uses each target’s project root (not always `cwd`). Remote `http(s)` URLs do not load a local design system. Multi-URL runs reuse one browser via `createBrowserDetector`.

## URL scans with Puppeteer

`puppeteer` is optional. Missing install fails URL targets with:

```text
puppeteer is required for URL scanning. Install: npm install puppeteer
```

That error is written to stderr for that target; other targets continue. A failed URL alone does not force exit `2` unless primary findings exist from successful scans.

### CI install patterns

<Tabs>
  <Tab title="npm (optional dep)">
```bash
npm install impeccable
# optionalDependency may install automatically; pin if your install omits optionals:
npm install puppeteer
```
  </Tab>
  <Tab title="npx without lockfile">
```bash
# Prefer a committed package.json that lists puppeteer when URL scans run in CI
npx impeccable detect --json https://staging.example.com
```
  </Tab>
  <Tab title="explicit chrome needs">
```bash
# Puppeteer downloads Chromium on first use; ensure the job can download
# or cache browsers. Windows prefer system Chrome (channel: chrome) when available.
npx impeccable detect --viewport 1280x800 --json http://127.0.0.1:4173
```
  </Tab>
</Tabs>

Typical pattern: start the app or preview server, wait for health, then scan the URL.

```bash
npm run build && npm run preview -- --host 127.0.0.1 --port 4173 &
# wait-for-port helper of your choice
npx impeccable detect --json --viewport 1280x800 http://127.0.0.1:4173
```

`file:///absolute/path/to/page.html` uses the same browser path for local static artifacts when you need computed styles without a server.

## JSON finding shape

Each finding is an object roughly:

| Field | Type | Notes |
| --- | --- | --- |
| `antipattern` | string | Rule id (e.g. `side-tab`) |
| `name` | string | Display name from registry |
| `description` | string | Rule description |
| `severity` | string | Usually `warning` |
| `category` | string \| null | e.g. `slop` / `quality` |
| `file` | string | Path or URL target |
| `line` | number | `0` when unknown |
| `snippet` | string | Locating text (often quote-wrapped labels) |
| `advisory` | true \| omitted | Present only when advisory |
| `importedBy` | string[] \| omitted | Basename importers when multi-file graph applies |

Clean JSON mode:

```json
[]
```

## Minimal CI recipes

### GitHub Actions (static paths)

```yaml
name: design-detect
on: [push, pull_request]
jobs:
  detect:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "22"
      - name: Detect anti-patterns
        run: npx --yes impeccable@latest detect --json --quiet src/
```

- `--json` keeps machine-readable output on stdout; `--quiet` is redundant for detail (JSON wins) but harmless.
- Job fails on exit `2` by default.

### Capture findings without failing

```bash
set +e
npx impeccable detect --json src/ > impeccable-findings.json
code=$?
set -e
if [ "$code" -eq 2 ]; then
  echo "Findings present; see impeccable-findings.json"
  exit 0   # or upload artifact and still pass
fi
if [ "$code" -ne 0 ]; then
  exit "$code"  # real tool failure
fi
```

### Scope-limited gate

```bash
npx impeccable detect --json --scope layout,type components/
```

### Strict raw scan (ignore project waivers)

```bash
npx impeccable detect --json --no-config src/
```

Use sparingly; teams usually want shared `ignoreRules` / `ignoreFiles` honored in CI the same way as local hooks.

## Interactive paths that CI avoids

These only run when stdin is a TTY **and** not `--json`/`--quiet`:

- Framework dev-server suggestions (`Next` / Vite / etc. on localhost ports)
- Confirm prompt when a directory contains more than 50 scannable files

Non-TTY CI skips the confirm and proceeds. Prefer explicit subdirectory targets to keep scan time predictable.

## Troubleshooting

| Symptom | Likely cause | Fix |
| --- | --- | --- |
| Exit `2` with expected brand fonts | Rule not ignored | Add `ignoreValues` or inline disable; see detector ignores docs |
| Exit `0` but advisory noise in logs | Advisory rules | `--no-advisory` or filter `advisory: true` in JSON |
| Exit `1` on `--scope` | Missing/unknown value | Valid scopes: `type`, `layout` |
| URL scan error, empty/partial results | Puppeteer/Chrome missing | Install `puppeteer`; ensure browser download in the image |
| CI finds harness/vendor noise | Scanning repo root | Target `src/` (or app root); walks already skip many hidden harness dirs |
| Design rules from wrong package monorepo | DESIGN.md root | Design system resolves per file project root; set `--no-design-system` if unwanted |
| Large monorepo timeout | Wide walk | Narrow path args; use `ignoreFiles` |

## Related pages

<CardGroup>
  <Card title="CLI reference" href="/cli-reference">
    Full flag list, shorthand targets, and command inventory for detect and ignores.
  </Card>
  <Card title="Manage detector ignores" href="/manage-detector-ignores">
    ignoreRules, ignoreFiles, ignoreValues, and inline disable comments.
  </Card>
  <Card title="Detector rules" href="/detector-rules">
    Rule ids, categories, advisory rules, and engine scopes.
  </Card>
  <Card title="Configuration reference" href="/configuration-reference">
    `.impeccable/config.json` schema for detector and related settings.
  </Card>
  <Card title="Design hook" href="/design-hook">
    Edit-time detection that shares the same ignore filters as CLI detect.
  </Card>
</CardGroup>
