pyreon

@pyreon/cli is the command-line companion for Pyreon projects. It ships five commands: pyreon doctor — a project-wide health audit that runs a battery of independent gates in parallel, aggregates every finding into a unified report, and computes a 0-100 health score with a letter grade and a per-category bar chart; pyreon context — a project-structure scanner that writes a machine-readable summary for AI coding assistants; pyreon info — an environment report that lists every installed @pyreon/* version and flags version skew before it trips the duplicate-instance guard; pyreon upgrade — the fix for that skew, aligning every @pyreon/* dependency to one version; and pyreon lint — a thin front door to @pyreon/lint that forwards every pyreon-lint flag.

@pyreon/clistable

Installation

npm install -D @pyreon/cli
bun add -d @pyreon/cli
pnpm add -D @pyreon/cli
yarn add -D @pyreon/cli

The package installs a single pyreon binary. Run it through your package manager's runner without a global install:

npx pyreon doctor
bunx pyreon doctor
pnpm exec pyreon doctor
yarn pyreon doctor

@pyreon/cli declares typescript as a peer dependency (>=5.0.0) because the audit gates lean on TypeScript's compiler API for AST analysis. It works in any project — the gates that target Pyreon-specific surfaces (SSG conventions, islands, doc claims) skip gracefully when the project isn't a Pyreon repo.

Why a doctor command?

Pyreon's quality gates have always existed as a sprawl of standalone scripts — check-distribution, check-doc-claims, audit-types, audit-tests, the islands/SSG/content auditors. Each prints its own format, exits its own way, and you have to remember to run all of them. pyreon doctor is the single entry point: one command runs every gate, isolates failures (a gate that throws never takes down the rest), and rolls the results into one scored report you can read at a glance — or pipe into CI, GitHub Actions, or an AI agent.

The output is modeled after react.doctor: a big score banner, per-category bars, and the top findings with actionable fixes inline.

pyreon doctor
  pyreon doctor · project health audit

  Score:  92/100   Grade: A

  Per category:

  correctness    █████████░░░ 87  · 1E 1W
  performance    ████████████ 100 · clean
  architecture   ████████████ 100 · clean
  testing        ████████████ 100 · clean
  documentation  ████████████ 100 · clean
  best-practices clean (advisory — not graded)

  Top findings (2 of 2):

  ✗ useState imported from React. Use signal() from @pyreon/reactivity.  react-patterns/no-usestate
     src/App.tsx:1:9
     fix: import { signal } from "@pyreon/reactivity"

  ! className → class (HTML standard attribute).  react-patterns/className
     src/App.tsx:3:18

  1 error · 1 warning · 12 gates · 1.4s

By default pyreon doctor runs the 12 fast gates (~2-5s on a warm cache). The 2 slow gates are opt-in via --full.

pyreon check

A fast, file-scoped scan for Pyreon/React anti-patterns — the terminal-native twin of the MCP validate tool. It runs the compiler's two static detectors (detectPyreonPatterns for "using Pyreon wrong" footguns, detectReactPatterns for "from React" mistakes) over source files and prints each finding with its inline fix. It exits non-zero on any finding, so it doubles as a pre-commit / CI gate.

pyreon check                    # scan git-changed .ts/.tsx files (the pre-commit inner loop)
pyreon check src/              # scan a directory (recursive; skips node_modules/lib/dist)
pyreon check src/App.tsx        # scan specific files
pyreon check --fix              # apply the mechanically-safe auto-fixes in place
pyreon check --json             # machine-readable findings for tooling

Example output:

  src/Counter.tsx
    4:2  [signal-write-as-call] (auto-fixable — run with --fix)
      `count(value)` does NOT write the signal — use `count.set(value)` …
      → count.set(5)
    5:9  [for-missing-by]
      <For each={...}> requires a `by` prop so the keyed reconciler can preserve identity …
      → <For each={items} by={(item) => item.id}>

  2 findings in 1 file  ·  some are auto-fixable with --fix

How it differs from the neighbours: pyreon doctor is the whole-project health audit (all gates + a 0-100 score, slower); pyreon lint runs the @pyreon/lint rule set (a different engine). pyreon check is the fast, file-scoped compiler-detector pass — the quickest way to answer "did I introduce a Pyreon anti-pattern in this file / my diff?" --fix applies migratePyreonCode + migrateReactCode (only the mechanically-safe fixes; anything else stays reported so it still gates).

pyreon add

Install one or more @pyreon/* packages and print exactly how to wire each one in — the root provider to add plus a usage snippet, tailored per package.

pyreon add query              # installs @pyreon/query (bare name → @pyreon/query)
pyreon add query toast i18n   # several at once
pyreon add @pyreon/form --dry-run   # show the plan + recipe without installing
pyreon add query --json       # machine-readable plan

It auto-detects your package manager from the lockfile (bun.lock → bun, pnpm-lock.yaml → pnpm, yarn.lock → yarn, else npm), walking up from the current directory — so you don't have to remember which add command this project uses. Then it prints the setup:

  @pyreon/query — TanStack Query adapter …
  1. Add the provider near your app root:
      import { QueryClient, QueryClientProvider } from '@pyreon/query'
      const queryClient = new QueryClient()
      <QueryClientProvider client={queryClient}> <App /> </QueryClientProvider>
  2. Use it:
      const todos = useQuery(() => ({ queryKey: ['todos', id()], queryFn: fetchTodos }))
  Docs: https://pyreon.dev/docs/query

Curated recipes exist for the flagship packages (query, toast, i18n, permissions, form, store, router, head); any other @pyreon/* package still installs, with a generic docs pointer. The recipes live in the CLI itself (hand-authored + verified against each package's real API) rather than being generated from manifests — published packages don't ship their manifests, and reaching across packages for them would bloat the CLI's install.

pyreon new

Scaffold a new Pyreon project — a single front door alongside the rest of the pyreon commands, instead of a separately-remembered npm create @pyreon/zero.

pyreon new my-app              # web / full-stack app (delegates to @pyreon/create-zero)
pyreon new my-app --native     # multiplatform (SwiftUI + Compose) via @pyreon/create-multiplatform
pyreon new my-app --template blog   # any other flag passes straight through
pyreon new my-app --dry-run    # print the npx command without running it

It's a thin, dependency-free delegator: it npx-runs the appropriate create-* scaffolder (which owns the interactive prompts and templates), pinned to @latest so a new project always starts on the freshest templates regardless of how old your globally-installed @pyreon/cli is. Your project name and any extra flags pass through untouched.

pyreon mcp

Launch the Pyreon MCP server — the Model Context Protocol server (@pyreon/mcp) that serves Pyreon's API reference, pattern catalog, validate, diagnose, and changelog to AI coding assistants. A single front door alongside the other pyreon commands, instead of a separately-remembered package invocation.

pyreon mcp                 # launch the stdio MCP server (blocks; the client talks over stdin/stdout)
pyreon mcp --dry-run       # print the npx command without launching
pyreon mcp <args>          # any extra flags pass straight through to the server

It's a thin, dependency-free delegator: it npx-runs @pyreon/mcp. Unlike pyreon new, it is deliberately not pinned to @latestnpx @pyreon/mcp prefers the project-local @pyreon/mcp when it's installed, so the served API reference matches your installed Pyreon version, and only fetches it on demand when the project doesn't have it. It inherits stdio, so the AI client that spawned pyreon mcp communicates with the server directly.

Point your assistant's MCP config at pyreon mcp (e.g. as the command) to get Pyreon-aware validate / get_api / get_pattern / diagnose tools.

pyreon atlas

pyreon atlas scan .              # derive + verify the component catalog (exits non-zero on failing scenarios)
pyreon atlas dev .               # serve the Atlas workbench for this project
pyreon atlas verify-browser .    # browser-half verification: reactive coverage + visual snapshots
pyreon atlas --dry-run <args>    # print the npx command without launching

The Atlas component workbench from the CLI front door. A thin, dependency-free delegator to @pyreon/atlas — every arg after atlas passes straight through. Like pyreon mcp it is deliberately not pinned to @latest: the project-local @pyreon/atlas wins when installed, so the derived catalog matches your installed Pyreon version.

pyreon doctor

How it works

The orchestrator selects a gate set, runs every gate concurrently via Promise.all, and hands the results to the aggregator. The gates are fully independent — no shared state, no file-write contention (only --fix writes, and the writing gates target disjoint file patterns) — so running them in parallel cuts the fast set from ~5s sequential to ~1-2s wall-clock.

Each gate emits findings in a unified shape: a category (correctness / performance / architecture / testing / documentation / best-practices), a severity (error / warning / info), a stable <gate>/<rule> code, a human message, an optional source location, and an optional fix hint. The aggregator sorts findings (errors first, then by category, then by code), tallies totals, and computes the score.

Scan scope — workspace roots

The file-scanning gates (react-patterns, pyreon-patterns, lint, audit-tests) resolve their scan scope from the workspace's own configuration — the root package.json workspaces globs or pnpm-workspace.yaml — discovered by walking up from the cwd. Multi-root layouts (apps/* + packages/* + modules/*) are fully covered, and results are identical from any directory in the repo; a repo with no workspaces is treated as a single package. Per package, src/** is scanned when present, always minus tests / fixtures / .d.ts (detector fixtures deliberately contain anti-patterns — scoring them would produce a false grade).

The report header shows what was scanned (Scope: 4 package root(s) from workspaces (…) plus per-gate file counts), and a gate that matches no files is skipped with a warning instead of reading as a clean pass — its category is "not measured" and never contributes a free 100. If nothing at all was measured, the score renders as and --ci exits non-zero. Escape hatches: --roots <glob,...> overrides discovery for non-standard layouts, and pyreon.doctor.excludeRoots globs in the root package.json exclude demo/docs workspaces from grading.

Gates

There are 14 gates total: 12 fast (run by default) and 2 slow (opt in with --full).

GateSpeedCategoryWhat it checks
react-patternsfastcorrectness"Coming from React" mistakes — useState, useEffect, className, htmlFor, onChange on inputs, .value writes, React-package imports. *-compat packages are excluded (exposing useState/className is their literal purpose).
pyreon-patternsfastcorrectness"Using Pyreon wrong" mistakes — <For> missing by, destructured props, signal-write-as-call, and more (the [detector: …] anti-pattern codes). Codes a @pyreon/lint rule fully owns (process-dev-gate, raw-add-event-listener, query-options-as-function) are deferred to the lint gate so they aren't double-reported at the wrong severity.
lintfastby ruleEvery configured @pyreon/lint rule across first-party source (reads your .pyreonlintrc.json). Each finding's category is derived from the rule — performance rules land in performance, architecture/ssr/ssg/router/styling/accessibility in architecture, the rest in correctness. Opt-in best-practice rules (meta.optIn) route to the advisory best-practices bucket.
distributionfastarchitecturePublished-package hygiene — every @pyreon/* package declares sideEffects, and its files array does NOT exclude !lib/**/*.map (source maps must ship so framework stack traces are readable). Includes a live npm pack --dry-run probe of @pyreon/reactivity confirming .map files land in the tarball.
doc-claimsfastdocumentationHand-quoted numeric claims in docs match the source of truth — hook-export count, doc-page count, lint-rule count, lint-category count, detector-code count. Catches the drift where one claim site is bumped and the others aren't.
islands-auditfastarchitectureCross-file island detectors (same scanner as the MCP audit_islands tool): duplicate-name, never-with-registry-entry, registry-mismatch, nested-island, dead-island.
ssg-auditfastarchitectureSSG / ISR convention checker — _404.tsx placement (404-outside-layout-dir), dynamic routes missing getStaticPaths (warn — legit under mode: 'ssr'/'isr'), non-literal revalidate exports.
content-auditfastarchitecture@pyreon/zero-content content — missing-frontmatter-title, broken-internal-link, orphaned-md-file. Validated per-config so two apps that both mount /docs don't cross-contaminate.
native-auditfastarchitectureMultiplatform (PMTC) build hazards in .tsx files importing @pyreon/primitivesweb-only-package-import (a package that can't render on native) and native-unsupported-decl (a top-level interface/enum/class PMTC silently drops). Both warnings — they don't break the web build. Skips when no @pyreon/primitives importer is found.
audit-testsfasttestingThe mock-vnode anti-pattern — tests that build { type, props, children } literals (or a vnode() helper) instead of going through real h(). Classifies each file HIGH / MEDIUM / LOW.
check-dedupfastarchitectureDuplicate @pyreon/* versions in the lockfile (bun.lock / package-lock.json / pnpm-lock.yaml). Multiple module instances of one Pyreon package break framework contracts; this catches it statically before the runtime sentinel throws.
audit-leak-classesfastbest-practices (advisory)The memory-leak class heuristics (Class A / C / D / I). Advisory — false-positive-prone, so findings are VISIBLE but excluded from the grade and from --ci.
audit-typesslowarchitectureTyped-but-unimplemented public fields — interface fields with zero non-type references in their owning package (the mode: "ssg" typed-but-never-read bug class). Requires --full.
bundle-budgetsslowperformanceEach published package's gzipped main-entry size against its locked budget (over-budget), packages missing a budget entry (missing-budget), and packages the bundler couldn't measure (bundle-failed). Requires --full.

When a gate is excluded (slow gates without --full, or --skip/--only filtering), it appears in the report's skipped footer with the reason — enable with --full for slow gates, skipped for user-filtered ones. Some gates self-skip when their target surface is absent: native-audit (no @pyreon/primitives importer), audit-types (no scripts/audit-types.ts, i.e. outside the Pyreon monorepo).

The score

The score is a simple mean of the included category subscores. A category with no contributing gate is excluded from the mean rather than counted as a misleading 100 — an unmeasured category shouldn't pull the average up.

Per-finding penalty by severity:

SeverityPenalty
error10 points
warning3 points
info1 point

Each category subscore is 100 - clamp(sum of penalties, 0, 100), so 10 errors saturate a category to 0, 33 warnings cost 99 points, and so on. The mental math is predictable by design: fix one error, gain 10 points (capped at 100).

The best-practices category is advisory — it is scored and displayed (so opt-in best-practice findings stay visible), but it is NEVER folded into the overall mean or grade, and its errors never fail --ci. Opinionated rules shouldn't tank an objective health score.

Letter grades:

GradeScore
A90-100
B80-89
C70-79
D60-69
Fbelow 60

Flags

FlagEffect
--fullInclude the slow gates (audit-types, bundle-budgets).
--fixAuto-fix what's fixable — lint rules with auto-fixers + react-patterns migrations. Writes to disk; applied fixes surface as info findings, residual issues stay as warnings/errors.
--only <gates>Run ONLY the listed comma-separated gates. Takes precedence over --full and the default set.
--skip <gates>Exclude the listed comma-separated gates (applied after --only).
--format <text|json|gha>Output format (default: text).
--jsonShortcut for --format=json — emits the full DoctorReport.
--ghaShortcut for --format=gha — GitHub Actions annotation lines.
--ciExit non-zero on error findings only (advisory best-practices errors excluded; warnings/info never fail).
--audit-min-risk <high|medium|low>Minimum risk floor for the audit-tests gate (default: medium).
--help, -hPrint usage.
--version, -vPrint the CLI version.

Valid gate names for --only / --skip: react-patterns, pyreon-patterns, lint, distribution, doc-claims, islands-audit, ssg-audit, content-audit, native-audit, audit-tests, check-dedup, audit-leak-classes, audit-types, bundle-budgets. An unknown gate name is rejected with an error listing the valid set.

pyreon doctor                                  # 12 fast gates + score
pyreon doctor --full                           # add audit-types + bundle-budgets (14 gates)
pyreon doctor --only lint,react-patterns       # run a focused subset
pyreon doctor --skip doc-claims                # everything except doc-claims
pyreon doctor --only audit-types --full        # one slow gate in isolation
pyreon doctor --fix                            # auto-fix lint + react-patterns
pyreon doctor --ci                             # CI gate — exit non-zero on errors
pyreon doctor --json                           # full report for AI agents / dashboards
pyreon doctor --gha                            # GitHub Actions inline annotations
pyreon doctor --audit-min-risk high            # only HIGH-risk test-env findings

Output formats

Text (default)

The human-readable renderer (--format=text, the default) prints, in order:

  1. Bannerpyreon doctor · project health audit + Score: N/100 Grade: X.

  2. Per-category bars — a 12-cell bar per category, colored by grade, with an E/W/i breakdown (1E 2W 3i) or clean. The advisory best-practices row is labeled advisory — excluded from grade & --ci so it's never mistaken for a grade-affecting bucket. A category with no contributing gate shows skipped.

  3. Top findings — up to 10 findings, each with a severity glyph ( error, ! warning, info), the bolded message, the <gate>/<rule> code, a clickable file location, related locations (), and a fix: hint. A …and N more line points to --json for the full list.

  4. Footer — skipped gates with reasons, total counts, gate count, and elapsed wall-clock.

Colors degrade gracefully via @pyreon/ansi (respects NO_COLOR / FORCE_COLOR / TTY detection), and file paths are OSC-8 hyperlinks in terminals that support them (iTerm2, WezTerm, kitty, VS Code).

JSON (--json / --format=json)

Emits the full DoctorReport as pretty-printed JSON — the stable shape consumed by AI agents, CI dashboards, and third-party tooling.

pyreon doctor --json
{
  "score": 92,
  "grade": "A",
  "categories": [
    { "category": "correctness", "score": 87, "errors": 1, "warnings": 1, "infos": 0, "grade": "B", "included": true },
    { "category": "performance", "score": 100, "errors": 0, "warnings": 0, "infos": 0, "grade": "A", "included": false },
    { "category": "architecture", "score": 100, "errors": 0, "warnings": 0, "infos": 0, "grade": "A", "included": true },
    { "category": "testing", "score": 100, "errors": 0, "warnings": 0, "infos": 0, "grade": "A", "included": true },
    { "category": "documentation", "score": 100, "errors": 0, "warnings": 0, "infos": 0, "grade": "A", "included": true },
    { "category": "best-practices", "score": 100, "errors": 0, "warnings": 0, "infos": 0, "grade": "A", "included": false }
  ],
  "gates": [
    {
      "gate": "react-patterns",
      "category": "correctness",
      "findings": [
        {
          "category": "correctness",
          "severity": "error",
          "code": "react-patterns/no-usestate",
          "gate": "react-patterns",
          "message": "useState imported from React. Use signal() from @pyreon/reactivity.",
          "location": { "path": "/abs/src/App.tsx", "relPath": "src/App.tsx", "line": 1, "column": 9 },
          "fix": "import { signal } from \"@pyreon/reactivity\"",
          "fixable": false
        }
      ],
      "meta": { "scanned": 42, "elapsedMs": 31 }
    }
  ],
  "findings": [ "…flat list, ordered errors → warnings → info" ],
  "totals": { "errors": 1, "warnings": 1, "infos": 0 },
  "elapsedMs": 1402,
  "timestamp": "2026-06-24T10:00:00.000Z"
}

Top-level keys: score, grade, categories (always 6 entries, included flags coverage), gates (every gate that ran or was skipped, with meta), findings (the flat sorted list), totals, elapsedMs (wall-clock), and timestamp (ISO, for diffing runs).

GitHub Actions (--gha / --format=gha)

Emits one ::error / ::warning / ::notice workflow-command line per finding, which GitHub renders as inline annotations in the PR's "Files changed" tab. Doctor error → GHA error, warningwarning, infonotice. A summary ::notice:: header carries the score + totals.

pyreon doctor --gha
::notice::pyreon doctor score: 92/100 (A) — 1 errors, 1 warnings, 0 info
::error title=react-patterns%2Fno-usestate,file=src%2FApp.tsx,line=1,col=9::useState imported from React. Use signal() from @pyreon/reactivity. — import { signal } from "@pyreon/reactivity"
::warning title=react-patterns%2FclassName,file=src%2FApp.tsx,line=3,col=18::className → class (HTML standard attribute).

Property values (file=, title=) are URL-encoded so commas and colons in paths don't truncate the annotation.

CI usage

The canonical CI shape is pyreon doctor --ci (exit non-zero on errors) combined with --gha for inline PR annotations:

# .github/workflows/health.yml
name: Health
on: [pull_request]
jobs:
  doctor:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: oven-sh/setup-bun@v2
      - run: bun install
      # Inline annotations on the PR (always exits 0 in this mode unless --ci added)
      - run: bunx pyreon doctor --gha
      # The actual gate — fails the job on any error finding
      - run: bunx pyreon doctor --ci

--ci and --gha/--json are independent: --ci controls the exit code, the format flags control the output. To both annotate and gate in one run, combine them — pyreon doctor --gha --ci prints GHA annotations and exits non-zero on errors.

For a focused gate in CI (e.g. only the islands audit on a PR that touched island code):

pyreon doctor --only islands-audit --ci

Legacy flags

The pre-v2 single-purpose flags still work — they map to --only <gate> shortcuts so existing CI scripts keep functioning unchanged:

Legacy flagEquivalent
--audit-tests--only audit-tests
--check-islands--only islands-audit
--check-ssg--only ssg-audit
--check-content--only content-audit
--check-native--only native-audit
pyreon doctor --check-islands                          # == --only islands-audit
pyreon doctor --audit-tests --audit-min-risk high      # filtered test-env audit
pyreon doctor --check-ssg --json                       # SSG audit, machine-readable
pyreon doctor --check-native                           # multiplatform PMTC hazards

When you pass multiple legacy flags together, they compose into a single --only set (e.g. --check-islands --check-ssg runs both). An explicit --only always wins over the legacy flags.

Gate deep-dives

A few gates are worth understanding in detail.

islands-audit

The same scanner as the MCP audit_islands tool. It flags:

  • duplicate-name (error) — two island() calls with the same name; only the first hydrates, the rest silently fail.

  • never-with-registry-entry (error) — a hydrate: 'never' island paired with a manual hydrateIslands({ … }) registry entry (which pulls the component into the client bundle, defeating the zero-JS strategy).

  • registry-mismatch (error) — a hydrateIslands({ X }) entry where X has no matching island() declaration (typo / removed island).

  • nested-island (error) — an island() whose loader-target file also contains an island() (the outer's hydration replaces the inner subtree before its loader runs).

  • dead-island (warning) — an island() no other source imports (declared but never wired up — warning because it might be intentional mid-refactor).

audit-tests

Scans every *.test.{ts,tsx} for the mock-vnode anti-pattern — tests that hand-construct { type, props, children } literals instead of going through real h() from @pyreon/core. Mock-only tests pass while the real rendering pipeline stays unexercised, which is exactly how a silent metadata-drop bug shipped once. Each file is classified HIGH / MEDIUM / LOW from the balance of mock-literal count, helper-definition count, helper-call count, and real-h()-call count. --audit-min-risk sets the floor (default medium, so LOW findings are hidden unless you pass --audit-min-risk low). Risk → severity: HIGH = error, MEDIUM = warning, LOW = info.

check-dedup

Parses bun.lock, package-lock.json, or pnpm-lock.yaml (whichever exists) and flags any @pyreon/* package resolving to more than one version. Two instances of the same Pyreon package break framework contracts — signal tracking, lifecycle hooks, context propagation — and the runtime singleton sentinel throws on first dual-load. This gate catches the installed-state mismatch statically, before deploy. The fix is to pin a single version and remove the conflicting transitive entries.

audit-types

A --full gate that walks every exported interface in the high-risk Pyreon packages (zero, router, core, server, runtime-server, vite-plugin) and counts non-type references. A public field with zero non-type references is the typed-but-unimplemented bug class — a config option that's documented and typed but the runtime never reads. It maps HIGH → error, MEDIUM → warning, LOW → info. Outside the Pyreon monorepo (no scripts/audit-types.ts) it skips gracefully.

pyreon context

The context command scans your project's structure and writes a machine-readable summary to .pyreon/context.json, designed to be fed to AI coding assistants and consumed by @pyreon/mcp.

pyreon context                          # writes .pyreon/context.json
pyreon context --out path/to/ctx.json   # custom output path

The scan (delegated to @pyreon/compiler's project scanner) detects:

  • Components — your Pyreon components.

  • Routes — file-system routes.

  • Islandsisland() declarations.

It writes the JSON, prints a summary, and ensures .pyreon/ is in your .gitignore (the directory is a generated artifact, not source). On success:

  ✓ Generated .pyreon/context.json (42 components, 7 routes, 3 islands)

The output JSON is stable and pipeable — drop it into an LLM prompt for project-aware assistance, or let @pyreon/mcp read it to answer questions about your codebase. Re-run it after structural changes (new routes, new islands) to refresh the snapshot.

pyreon info

The info command reports your environment and every installed @pyreon/* package version — and flags version skew before it bites.

pyreon info          # environment + installed @pyreon versions + skew check
pyreon info --json   # machine-readable report

It prints the CLI version, runtime (node / bun / platform), the project name (and whether it's a @pyreon/zero app), and the @pyreon/* packages in node_modules with their versions. Pyreon ships its packages on one synced version trajectory, so a healthy project has them all on the same version:

  ✓ All @pyreon packages on 0.37.0

When the installed set spans more than one version, info flags it — this is the condition that can trip the registerSingleton duplicate-instance guard ([Pyreon] Duplicate @pyreon/X detected) and split context/reactivity across instances at runtime:

  ! Version skew — 2 versions installed:
      0.37.0: @pyreon/core, @pyreon/router
      0.30.0: @pyreon/query

info is self-contained — it reads only your package.json and node_modules/@pyreon/*, so it works in any project (or none) without requiring the framework packages to be installed.

pyreon upgrade

The upgrade command is the fix for the skew pyreon info detects — it rewrites every @pyreon/* dependency range in package.json to a single version.

pyreon upgrade              # dry-run: print the alignment plan
pyreon upgrade --write      # apply (rewrite package.json), then install
pyreon upgrade --to 0.37.0  # target a specific version
pyreon upgrade --exact      # pin without the caret (0.37.0, not ^0.37.0)

By default the target is the highest @pyreon/* version already present (aligning laggards up); pass --to <version> to choose. It is dry-run by default — it prints what would change and applies nothing until you pass --write:

  pyreon upgrade → align 1 package(s) to 0.37.0

    @pyreon/core  ^0.30.0 → ^0.37.0

After --write, run your package manager's install to pull the aligned versions. workspace: / link: / file: / git specifiers and non-@pyreon dependencies are left untouched.

pyreon lint

pyreon lint is the unified front door to @pyreon/lint — it forwards every pyreon-lint flag verbatim.

pyreon lint                  # lint . with the recommended preset
pyreon lint src --fix        # auto-fix fixable findings
pyreon lint --preset strict  # any pyreon-lint flag works
pyreon lint --format json    # text (default) | json | compact
pyreon lint --watch          # re-lint on change
pyreon lint --lsp            # language-server mode (inlay hints + diagnostics)

pyreon lint and the standalone pyreon-lint binary share one implementation (the exported runCli entry), so the two can never drift. It exits non-zero when there are lint errors — drop it into CI exactly like pyreon-lint.

Programmatic API

@pyreon/cli exports its core functions so you can run the doctor or generate context from your own scripts (CI runners, custom dashboards, editor extensions):

import { doctor, generateContext } from '@pyreon/cli'
import type {
  DoctorOptions,
  DoctorReport,
  GateName,
  ContextOptions,
  ProjectContext,
} from '@pyreon/cli'

// Run the doctor and inspect the report
const exitCode = await doctor({
  cwd: process.cwd(),
  fix: false,
  json: false,
  ci: true,
  full: false,
  only: ['lint', 'react-patterns'] as GateName[],
})
// In --ci mode, the returned number is the count of non-advisory error
// findings (the CLI exits 1 when it's > 0). Without --ci, it's the total
// finding count and the CLI exits 0.

// Generate the project context programmatically
const context: ProjectContext = await generateContext({
  cwd: process.cwd(),
  outPath: '.pyreon/context.json',
})
console.log(context.components.length, 'components')

doctor(options) returns a Promise<number> (the exit-code-shaped count described above) and prints the chosen format to stdout. generateContext(options) returns the scanned ProjectContext and writes it to disk.

Reference

Commands

CommandDescription
pyreon check [paths] [--fix] [--json]Fast, file-scoped Pyreon/React anti-pattern scan (compiler detectors) with inline fixes. No paths → git-changed files. Exits non-zero on findings.
pyreon add <pkg...> [--dry-run] [--json]Install @pyreon/* packages (PM auto-detected) and print a tailored setup recipe for each.
pyreon new [name] [--native]Scaffold a new Pyreon project (delegates to @pyreon/create-zero, or -multiplatform with --native).
pyreon mcp [args]Launch the Pyreon MCP server (delegates to @pyreon/mcp; prefers the project-local install).
pyreon doctor [options]Project-wide health audit with a 0-100 score. Runs 12 fast gates by default; --full enables 2 slow gates.
pyreon context [--out <path>]Generate .pyreon/context.json for AI tools.
pyreon --help / -hShow usage.
pyreon --version / -vShow the CLI version.

doctor options

FlagTypeDefaultDescription
--fullbooleanoffInclude slow gates (audit-types, bundle-budgets).
--fixbooleanoffAuto-fix lint + react-patterns findings (writes to disk).
--only <gates>comma listRun ONLY these gates.
--skip <gates>comma listSkip these gates.
--format <fmt>text|json|ghatextOutput format.
--jsonbooleanoffShortcut for --format=json.
--ghabooleanoffShortcut for --format=gha.
--cibooleanoffExit non-zero on non-advisory error findings.
--audit-min-risk <r>high|medium|lowmediumMinimum risk for the audit-tests gate.
--audit-testsbooleanoffLegacy → --only audit-tests.
--check-islandsbooleanoffLegacy → --only islands-audit.
--check-ssgbooleanoffLegacy → --only ssg-audit.
--check-contentbooleanoffLegacy → --only content-audit.
--check-nativebooleanoffLegacy → --only native-audit.

Gates at a glance

GateSpeedDefault categorySeverity range
react-patternsfastcorrectnesserror / warning / info
pyreon-patternsfastcorrectnesswarning
lintfastper-ruleerror / warning / info
distributionfastarchitectureerror
doc-claimsfastdocumentationerror
islands-auditfastarchitectureerror / warning
ssg-auditfastarchitectureerror / warning
content-auditfastarchitectureerror / warning
native-auditfastarchitecturewarning
audit-testsfasttestingerror / warning / info
check-dedupfastarchitectureerror
audit-leak-classesfastbest-practices (advisory)info
audit-typesslow (--full)architectureerror / warning / info
bundle-budgetsslow (--full)performanceerror / warning

Score formula

ConceptValue
Error penalty10 points
Warning penalty3 points
Info penalty1 point
Category subscore100 - clamp(sum of penalties, 0, 100)
Overall scoremean of included category subscores
Advisory categorybest-practices — scored + shown, excluded from grade & --ci
Grade A / B / C / D / F90+ / 80-89 / 70-79 / 60-69 / <60

Exports

ExportKindDescription
doctor(options)functionRun the health audit; returns the exit-code-shaped finding count.
generateContext(options)functionScan the project and write .pyreon/context.json.
DoctorOptionstypeOptions for doctor().
DoctorReporttypeThe aggregated report shape (--json output).
GateNametypeUnion of all 14 gate names.
ContextOptionstypeOptions for generateContext().
ProjectContexttypeThe scanned project shape.
@pyreon/cli