CLI
Pre-deploy risk scanner. The same classes of issues nreactive's runtime SDK reports post-deploy — but caught by static analysis on your repo before they ship. Zero install, zero signup for the first scan.
npx nreactive scanThat's the whole getting-started story. The CLI packs the current repo with
Repomix, POSTs it to
https://nreactive.com/api/scan, and prints the top risks inline.
What it catches
Static-analysis variants of the runtime errors nreactive reports from production:
- Unhandled promise rejections / missing
await - Hardcoded secrets and credentials
- Missing input validation on routes
- Race conditions and shared-state bugs
- Mis-typed env access (
process.env.XwhereXisn't declared) - Common N+1 patterns
Each finding comes back with severity, file:line, a short description,
and a suggested fix.
Install
Zero-install with npx is the easiest path. If you want nreactive on
your $PATH:
npm i -g @nreactive/cli # or
pnpm add -g @nreactive/cli # or
bun add -g @nreactive/cliThe package is published as @nreactive/cli but the binary it installs is
named nreactive — same command either way:
nreactive scanCI mode
Add it to your pipeline
npx nreactive scan --ci --min-severity high--ci makes the command exit non-zero if any finding at or above
--min-severity exists. Drop into GitHub Actions:
# .github/workflows/scan.yml
- run: npx nreactive scan --ci --min-severity highOr GitLab CI:
scan:
script:
- npx nreactive scan --ci --min-severity highSign in for dashboard history (optional)
The first scan works anonymously. To link scans to your dashboard, export a token from the Apps page:
export NREACTIVE_TOKEN=...
npx nreactive scanAnonymous scans are deleted after analysis. Signed-in scans show up in your
dashboard history with a reportUrl deep link.
Flags
| Flag | Default | Description |
|---|---|---|
| --endpoint | https://nreactive.com | Backend base URL. Override with NREACTIVE_ENDPOINT. |
| --token | (none) | Bearer token. First scan works anonymously; signed-in scans link to your dashboard. Override with NREACTIVE_TOKEN. |
| --ci | false | Exit non-zero on findings ≥ --min-severity. |
| --min-severity | high | One of critical, high, medium, low. |
| --max-bytes | 5000000 | Hard cap on the packed-repo size. |
| --json | false | Emit the raw ScanResponse JSON on stdout (for piping into other tools). |
| --cwd | process.cwd() | Repository root to scan. |
Exit codes
| Code | Meaning |
|---|---|
| 0 | OK (or non-CI mode regardless of findings) |
| 1 | CI mode: findings ≥ --min-severity |
| 2 | Usage error (bad flag) |
| 3 | Network error reaching the backend |
| 70 | Internal error |
CI distinguishes "scanner found bugs" (1) from "scanner is broken"
(3, 70) so you can fail loud on real findings without breaking on
flakes.
How it works
- The repo is packed locally with Repomix (XML format, default ignore rules).
- The pack is POSTed to
{endpoint}/api/scan?mode=cli. - The backend runs the same analyzers that power the dashboard and
returns a compact response (
scanId, top risks, summary, optionalreportUrl). - Output is rendered inline with
chalk;--jsonemits the raw response for piping into other tools.
No code is stored unless you're signed in and explicitly opt in (your dashboard tracks scan history). Anonymous scans are deleted after analysis.
Every request includes an x-nreactive-self: 1 header so the runtime
SDK's HTTP-client integration skips it when both ship in the same
project — no self-loop scans.
Wire contract
Public, stable across patch versions:
// POST {endpoint}/api/scan?mode=cli
interface ScanRequest {
pack: string; // Repomix XML output
meta: {
cliVersion: string;
repoName?: string; // "owner/repo"
branch?: string;
commit?: string;
};
}
interface Risk {
id: string;
severity: "critical" | "high" | "medium" | "low";
title: string;
file?: string;
line?: number;
description: string;
suggestedFix?: string;
}
interface ScanResponse {
scanId: string;
risks: Risk[];
summary: { totalRisks: number; bySeverity: Partial<Record<Severity, number>> };
reportUrl?: string;
}