Skip to main content
Back to blog

Critiq team

  • guides

Your first critiq check: reading pretty output like a senior review

Install the OSS CLI, run your first scan, and learn to read severity, rule IDs, file paths, and fix suggestions in pretty and JSON output.

Your first critiq check: reading pretty output like a senior review content

The first time you run critiq check, you get a wall of green checkmarks, a short secrets advisory, and, if anything needs attention, a file-by-file breakdown with line numbers. That output is designed to read like a careful code review: each finding names the rule, shows the exact line, and points toward a fix you can apply before merge.

This guide walks through a first local scan from install to interpretation. You will run real commands, compare pretty and JSON output, and learn how experienced reviewers triage security and quality findings without treating the CLI as a black box.

What you need

Critiq ships as an npm package pair: @critiq/cli runs the scan and @critiq/rules supplies the open catalog. You do not need a Critiq Cloud account, API key, or network upload, the scan runs entirely on your machine against files in your repo.

Node.js 18 or later and a project directory with source files are enough to follow along. If you prefer to try commands without installing, npx works for one-off runs.

Step 1: Install and run your first check

Add the CLI and catalog as dev dependencies, then point critiq at your repository root. The default target is the current directory.

npm install -D @critiq/cli @critiq/rules
npx critiq check .

The first command installs pinned packages you can commit in package.json. The second runs the recommended preset against every file the adapters understand in your tree. On a clean repo you may see exit code 0 and a long list of passing rules, that is normal.

For a quick try without adding dependencies, skip install and use npx directly:

npx critiq check .

When findings exist, critiq exits with code 1. That behavior is intentional: CI pipelines and pre-commit hooks can fail the job when issues are present. Exit code 2 indicates an internal or runtime error, misconfiguration, missing catalog, or a crash, not a finding.

Step 2: Understand the two layers of output

Pretty output (the default) has three distinct sections. Read them top to bottom before jumping to individual findings.

  • Secrets scan, advisory summary at the top. It does not change the exit code. Use critiq audit secrets when you want secret findings to gate a merge.
  • Rule results, a checklist of every rule that ran, with pass or fail markers. Useful for seeing coverage at a glance.
  • Findings, file-grouped details with the offending line, a snippet, and a location pointer.

At the bottom, a summary line reports how many files and rules were checked, how many failed, and total finding count. That footer is your quick health read after a scan.

Pretty vs JSON output

Pretty output is for humans at the terminal or in a CI log you read by hand. JSON output is for scripts, dashboards, and critiq-action on pull requests.

npx critiq check . --format json
npx critiq check . --format sarif
npx critiq check . --format html

The check command also supports sarif and html for platform handoff. JSON adds a secretsScan block alongside findings; pretty prints the secrets summary inline but keeps exit code catalog-driven. Pick pretty when you are learning; pick json when something else needs to parse the result.

A minimal JSON finding includes rule.id, severity, locations.primary.path, line and column ranges, summary text, and remediation.summary. That structure maps cleanly to the pretty view, you are reading the same facts in two layouts.

Step 3: Read severity like a reviewer

Every finding carries a severity. In pretty output the human-readable rule title appears first; in JSON look at the severity field directly. Critiq uses a small ordered scale:

  • critical and high, security issues that can lead to credential leaks, injection, or unsafe execution. Fix or explicitly accept risk before merge.
  • medium, correctness bugs, maintainability traps, and patterns that often become incidents under change. Worth fixing in the same PR when the diff touches the code.
  • low, hygiene, style-adjacent quality rules, and React or testing nits. Batch or schedule; still tie back to a rule you can read.

Senior reviewers do not treat all reds equally. Start with critical and high security findings, then medium correctness, then low quality. The rule checklist above the findings section helps you see whether a whole category ran, even when nothing failed.

Step 4: Rule IDs, the link to inspectable policy

Each finding maps to a stable rule ID. Shared security rules use IDs like security.no-hardcoded-credentials; language-specific rules use prefixes such as ts.quality.no-boolean-parameter-trap. Pretty output shows the human title (for example, No Hardcoded Credentials); JSON exposes the canonical metadata.id.

Rule IDs are the contract between the finding and the rule file in @critiq/rules. When a teammate asks why something flagged, the ID is how you pull up rationale, references, and test fixtures.

npx critiq rules explain path/to/security.no-hardcoded-credentials.rule.yaml
# Docs catalog: https://docs.critiq.dev/rules/

The rules explain command prints title, summary, detection logic, and emit templates for a rule file. For day-to-day review you often only need the ID and the one-line summary in the finding, but knowing explain exists is what separates skimming from understanding.

Step 5: File paths and line pointers

Findings group under the file path, not under rule name. That matches how you review a pull request: open the file, jump to the line, read the hunk in context.

Pretty output prints a small snippet with a > marker on the flagged line and an at path:line:column footer. JSON puts the same coordinates in locations.primary. Use those numbers in your editor go-to-line flow or in CI annotations.

If the same rule fires twice in one file, you get two entries under that path. If it fires in three files, you see three file headers. That grouping is deliberate, it mirrors inline review comments anchored to paths.

Step 6: Fix suggestions and remediation text

Under each rule title, pretty output shows a summary sentence describing what matched, often quoting the exact expression. JSON exposes the same text in summary and a shorter remediation.summary with the recommended direction.

Critiq does not auto-apply patches in the OSS CLI today. The remediation line tells you what good looks like: load secrets from the environment, parameterize SQL, replace boolean flag parameters with an options object. Treat it like a senior comment suggestion, then implement the fix in your editor.

Walkthrough: three finding types

The examples below come from a small demo repo with intentional issues. Your paths and line numbers will differ; the shape of the output will not.

Security, hardcoded credentials (critical)

Literal secrets in source are a common merge accident. Critiq flags credential-shaped string literals under security.no-hardcoded-credentials.

● src/auth.ts
  ✕ No Hardcoded Credentials
    `secret = "sk_live_7f3a9c2e1b4d"` appears to embed a credential-like literal in source code.
      1 | export function signSession(user: { id: string }) {
    > 2 |   const secret = "sk_live_7f3a9c2e1b4d";
        |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      3 |   return { token: secret + user.id };
    at src/auth.ts:2:9

How to read it: severity is critical in JSON; the rule title maps to security.no-hardcoded-credentials. The fix is to move signing material to an environment variable or secrets manager and keep literals out of git. This is a merge blocker for most teams.

Security, SQL interpolation (high)

String-built SQL with user input is an injection surface. The security.no-sql-interpolation rule catches queries assembled with template literals or concatenation passed to raw query sinks.

● src/db.ts
  ✕ No Sql Interpolation
    `db.query(sql, [])` builds or forwards SQL text directly into a raw query sink.
      2 |   const sql = `SELECT * FROM users WHERE email = '${email}'`;
    > 3 |   return db.query(sql, []);
        |          ^^^^^^^^^^^^^^^^^
      4 | }
    at src/db.ts:3:10

How to read it: the finding points at the sink (db.query), not only the template line, because that is where untrusted text enters the database layer. Remediation is parameterized queries or an ORM API that binds values separately from the statement text.

Quality, boolean parameter trap (medium)

Not every finding is security. Quality rules catch maintainability issues that slow down the next reviewer. ts.quality.no-boolean-parameter-trap flags functions with multiple boolean parameters, call sites become hard to read and easy to swap.

● src/invoice.ts
  ✕ No Boolean Parameter Trap
    `saveInvoice` appears to accept multiple boolean flags.
    > 1 | export function saveInvoice(id: string, sendEmail: boolean, dryRun: boolean) {
        |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      2 |   return { id, sendEmail, dryRun };
    at invoice.ts:1:8

How to read it: medium severity signals refactor when you touch the module, not an emergency patch. JSON remediation suggests an options object or dedicated methods, same advice a senior would leave on a design review.

Triage workflow: five minutes after every scan

  • Scan the footer: finding count and failed file count tell you whether you are looking at one typo or a broader preset gap.
  • Handle critical and high security findings first; confirm secrets advisory separately with critiq audit secrets if policy requires it.
  • For each remaining finding, note rule ID, path, and line, open the rule in docs or run rules explain when the summary is not enough.
  • Fix in place or add a tracked exception in .critiq/config.yaml (disableRules, severityOverrides) only when you understand the rule.
  • Re-run npx critiq check . until exit code 0 or until only accepted findings remain.

Troubleshooting

No findings, is that good or empty?

Exit code 0 and a footer showing Findings: 0 total usually means the preset ran clean on scanned files. Confirm the footer also reports a non-zero file count and matched rule count. If both are zero, you may be pointing at an empty directory, an ignored path, or a tree with no supported languages.

Tests are excluded by default. Set includeTests: true in .critiq/config.yaml if you expect findings in spec files. Check ignorePaths if a directory you care about never appears in the file count.

npx critiq check src/
npx critiq check . --format json | head

Scoping to a subdirectory is a fast way to verify the CLI sees your files. JSON prints scannedFileCount and matchedRuleCount near the top of the document.

Exit codes in CI

  • 0, success; no catalog findings (secrets advisory on check does not flip this).
  • 1, one or more findings, or a non-internal command failure you should read in the log.
  • 2, internal or runtime error; treat as a broken job, not a pass with issues.

In GitHub Actions, pair critiq-action with fail-on-severity when you want merges blocked only above a threshold. Locally, many teams run check without flags first to learn the output, then add gates once they trust the preset.

Command not found or wrong version

If critiq is not on PATH after install, use npx or npm exec. Keep @critiq/cli and @critiq/rules on compatible versions from the same release train, both are listed in the public OSS docs and changelog.

npm install -D @critiq/cli @critiq/rules
npx critiq check . --format json

Optional: commit scan policy

Teams that want consistent local and CI behavior commit a .critiq/config.yaml with catalog package, preset, and ignore paths. That file is optional for your first run but worth adding once you know which rules you want always on.

# Example after you add .critiq/config.yaml
npx critiq check .
npx critiq check --base origin/main --head HEAD

Diff-scoped scans with --base and --head limit findings to changed files, closer to pull request review. Start with full-repo scans while learning pretty output; switch to diff scope when you wire CI.

What to do next

You now have a repeatable first scan: install, run check, read severity and rule IDs, jump to file paths, and apply remediation hints like a senior review comment. The output is deterministic, the same code yields the same findings, and every finding ties to a rule you can read in the open catalog.

For install commands, rule categories, and a live review snippet, visit /products/oss on critiq.dev. For CLI flags, config reference, and catalog browsing, continue at https://docs.critiq.dev/getting-started and https://docs.critiq.dev/cli.

  • Install @critiq/cli and @critiq/rules, /products/oss
  • Read the CLI reference, https://docs.critiq.dev/cli
  • Browse rule IDs, https://docs.critiq.dev/rules/
  • Gate pull requests, https://github.com/critiq-dev/critiq-action