iconscan logoiconscan

iconscan CLI

$bunx iconscan .

Run this from the root of a React, Next.js, or TypeScript app. The one-shot command needs no project install. iconscan reads source files only — it never calls an AI model, uploads code, or touches your project unless you pass --apply.

Usage

iconscan [path] [options]

The optional path defaults to the current directory. Pass a relative or absolute path to scan another project — the scan walks *.ts/*.tsx files, skipping node_modules, build output, and your --exclude globs.

What it checks

Findings are grouped by rule and severity. Warnings carry a suggested fix; infos are facts worth a human glance.

RuleSeverityWhat it means
dead-importwarningIcon imported but never rendered — safe to delete.
brand-iconwarning / infoBrand logo imported from a library that removed brands (lucide, heroicons) → warning + react-icons/si fix. From a library that still ships brands or a local component → info, verify it renders the official asset.
generic-iconinfoPlaceholder names like Icon, Logo, Img — rename to something meaningful.
duplicateinfoSame icon imported from multiple libraries.
fragmentationinfoMore than two icon libraries in one project.
parse-errorinfoFile failed AST parsing — scanned with the regex fallback, results may undercount.

Options

[path]Project directory to scan. Defaults to .
-f, --format <fmt>pretty (default), json, or md.
-e, --exclude <globs>Comma-separated glob patterns to skip.
--applyApply safe fixes — AST-verified renames with .iconscan.bak backups. Prompts before writing unless --yes.
--yesSkip the confirmation prompt for --apply.
--promptPrint a paste-ready AI-agent remediation handoff instead of the report.
--rollbackRestore files from .iconscan.bak backups.
--fail-under <n>Exit 1 when the score is below n (0–100).
--help / --versionPrint usage / installed version.

Exit codes: 0 success, 1 score below --fail-under, 2 usage error (bad format, nonexistent path).

Output formats

The report always goes to stdout; progress and status go to stderr, so piping stays clean.

iconscan .                  # colored terminal report (default)
iconscan . --format md    # markdown report for PRs/issues
iconscan . --format json  # score, stats, and every issue, machine-readable

Scoring

Starts at 100 and subtracts capped penalties per category — dead imports, duplicates, generic names, parse errors, libraries beyond two, and error-severity issues. Deterministic: same project, same score.

Autofix and rollback

--apply performs AST-level rewrites — it removes dead specifiers, migrates brand imports to verified targets, and consolidates libraries. Before touching a file it writes a byte-identical .iconscan.bak backup, and only emits a rename when the target library is installed and actually exports the replacement (verifyExport) — otherwise it skips and says why.

iconscan . --apply          # review each fix interactively
iconscan . --apply --yes    # apply all safe fixes
iconscan . --rollback       # restore every .iconscan.bak

Agent prompt

--prompt prints a neutral Markdown handoff: the score, grouped findings with evidence, suggested fixes, acceptance criteria, and the exact rescan command. Paste it into Claude Code, Codex, or any coding agent — the agent starts from deterministic evidence instead of rediscovering problems.

iconscan . --prompt > iconscan-handoff.md

JSON and CI

--format json emits the complete report — score, stats, and every issue with rule, severity, file, line, and fix. Combine with --fail-under to gate merges:

# .github/workflows/iconscan.yml
name: iconscan
on: [push, pull_request]
jobs:
  icons:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npx iconscan . --fail-under 80

Pin an exact version in CI — a moving dist-tag is not a reproducible build input.

Troubleshooting

An older version runs right after a release

One-shot runners cache downloads. Pin the exact version (iconscan@<version>) or clear the cache; check what ran with --version.

Brand imports show info, not a fix

Intentional — iconscan only autofixes brands from libraries that removed them upstream (lucide, heroicons). Libraries that still ship brands get a verify-official info; migrating them would swap one unofficial asset for another.

A file is skipped with a parse error

Unparseable files fall back to regex extraction, which can undercount refs — it is counted in stats.parseErrors and surfaced as an info finding, never silently dropped.

Report a bug or missing library

Open an issue at github.com/princepal9120/iconscan.