Usage
miucr has these commands: init, login, whoami, logout, review, config, mcp, serve, rules, history, eval, upgrade, and version. This page covers review, the day-to-day loop. See the dedicated pages for serve & action, rules, history, evaluation, providers (config show + the [review] defaults table), and credentials; for the MCP server see MCP integration.
Review modes
Section titled “Review modes”Pick exactly one mode per run. The same contract is enforced by the CLI and the MCP review_run tool, so an ambiguous invocation always fails loudly.
miucr review --staged # staged changes vs the indexmiucr review --from main --to HEAD # a ref range (--from and --to are required together)miucr review --commit HEAD~1 # a single commit vs its parent--stagedreviews staged changes, diffed againstHEAD(git diff --cached) with the new-side content read from the index blob, i.e. exactly what you are about to commit (not your unstaged working tree).--from/--toreview<to>against the merge-base of the two refs (merge-base(from,to)..to), matching what a PR introduces.--commitreviews one commit against its first parent.
The severity gate
Section titled “The severity gate”--gate makes review CI-friendly: when a finding’s severity reaches the gate, the process exits non-zero (exit code 2).
miucr review --from main --to HEAD --gate high- Severities, low → high:
info,low,medium,high,critical. --gateaccepts those plusnone. Default ishigh.--gate nonereports findings but never fails the build.- An unrecognized gate is rejected up front; a typo can never silently disable gating.
Output formats
Section titled “Output formats”-o / --output is a global flag: json (default), pretty, or sarif.
miucr review --staged # JSON envelope (default)miucr review --staged -o pretty # local reporter: jumpable file:line, excerpt, patch (color on a TTY)miucr review --staged -o sarif # SARIF 2.1.0 document for code-scanning / IDEspretty is a real local reporter: each finding shows an editor-jumpable file:line (or file:start-end), a severity glyph + severity/category, the rationale, a quoted-code excerpt, and a suggested-patch preview. ANSI color is emitted only when stdout is a terminal; piped/CI output is plain.
sarif emits a schema-pinned SARIF 2.1.0 document (stdlib JSON; tool driver miucr, ruleId = category, level from severity, region from the anchored line range, snippet = quoted code, fixes from the suggested patch). Paths are repo-relative only, never absolute or secret. It is review-only (other commands keep the JSON envelope). Upload it to the GitHub code-scanning Security tab with github/codeql-action/upload-sarif; see Action: SARIF.
--sarif-out <file>
Section titled “--sarif-out <file>”-o sarif makes SARIF the only output. To get SARIF alongside the normal JSON envelope (or a posted PR review), pass --sarif-out <file> instead; the same single review run also writes a SARIF 2.1.0 document to that path, with no second LLM pass.
miucr review --staged --sarif-out miucr.sarif # JSON on stdout + SARIF filemiucr review --pr owner/repo#123 --post --sarif-out miucr.sarifIt is written only on a successful review (atomically: temp file + rename), so a failed run leaves no file. This is what the GitHub Action uses to publish to the Security tab; see Action: SARIF.
--filter-mode
Section titled “--filter-mode”--filter-mode (default diff_context) selects which findings are eligible for inline PR comments on --pr:
| Mode | Inline-eligible findings |
|---|---|
added | only findings on added (+) diff lines |
diff_context (default) | findings on any added or context diff line |
file | findings on any file present in the diff |
nofilter | every finding |
file and nofilter never widen the inline set past the diff (GitHub rejects an off-diff inline comment); they surface the extra findings in the summary, SARIF, and local output instead.
--format
Section titled “--format”--format (default full) selects the presentation of the posted review comment on --pr. It is render-only — it never changes which findings are produced or posted, only how the comment looks.
| Format | Comment shape |
|---|---|
full (default) | The complete output: the ## Code Review Summary section (result line, “What changed” walkthrough, changed-files table, review reference) plus severity/priority badges — the result chips and the per-finding P3 · bug badge on each inline comment. |
minimal | Drops the ## Code Review Summary section and all shields badges (both the summary chips and the inline P3 · bug badge). Inline findings, the footer, and the hidden tracking markers are kept, so re-runs still upsert the same comment. |
Set it per-invocation (--format minimal) or as a default in [review].format (CLI config) / the host review.format (serve config). An out-of-set value is rejected with flags.invalid_format / config.invalid (exit 2). The set is extensible: new named formats are added to the renderer’s format registry.
--prompt-format
Section titled “--prompt-format”--prompt-format (default xml) selects the structure of the prompt sent to the model — orthogonal to --format, which is the posted-comment presentation. It does not change which findings surface.
| Prompt format | Structure |
|---|---|
xml (default) | Untrusted payloads (diffs, new-content, project-context files, repo rules, conversation) are wrapped in entity-escaped XML tags (<file path="…"><diff>…</diff>…). A planted </file> or === File: === inside a diff is escaped to inert text, so attacker-controlled content in a fork PR cannot forge a file boundary or smuggle instructions. |
markdown | The prior fenced form: === File: <path> === / --- Diff --- / --- New content --- delimiters with triple-backtick fences around untrusted blocks. Byte-identical to releases ≤ 0.65; available as opt-out. |
xml is the default for injection-hardening: on the labeled eval suite it holds parity with markdown (no quality regression), and a unit test proves a forged delimiter stays inert. Pin the prior form with --prompt-format markdown or [review].prompt_format = markdown (also review.prompt_format on the host). An out-of-set value is rejected with flags.invalid_prompt_format / config.invalid (exit 2).
The default JSON is a stable v1 envelope (api_version: "miucr.cli/v1") so a host agent can branch without parsing prose:
{ "ok": true, "api_version": "miucr.cli/v1", "kind": "review.result", "command": "review", "request_id": "req_...", "summary": { "findings": 2, "gate": "high" }, "data": { "findings": [ { "file": "internal/foo/bar.go", "line": 42, "end_line": 42, "severity": "high", "category": "bug", "rationale": "…why this is a problem (may cite a convention the model can see, e.g. \"differs from mapWriteError\")…", "suggested_patch": "…optional minimal fix…", "quoted_code": "…verbatim source the finding anchors to…" } ], "stats": { "files_changed": 3, "files_reviewed": 2, "findings_total": 2, "findings_dropped": 1, "max_severity": "high", "gate": "high", "truncation_level": "full" }, "review_id": "rev_…" }}findings_dropped counts findings rejected by line-anchoring drift (see How it works). truncation_level is full, hunks_only, or filenames_only depending on how much context fit the token budget. review_id is the id of the saved review in the local history store (every review is saved by default; opt out with --no-save).
Errors use the same envelope with ok: false and an error object carrying a stable code, a redacted message, and a hint.
The day-1 provider/auth/timeout failures classify into a stable taxonomy (the same code regardless of backend, anthropic/openai/codex), each with an actionable hint and a correct retryable:
error.code | When | retryable |
|---|---|---|
agent.auth_failed | bad/invalid API key (401/403) | false |
agent.auth_expired | expired OAuth token (401/403, incl. codex still-401-after-refresh) | false |
provider.rate_limited | provider returned 429 | true |
agent.unavailable | provider returned 5xx / 529 | true |
review.timeout | the review exceeded --timeout | true |
review.stalled | the review emitted no progress for [review].stalled_timeout | true |
review.canceled | interrupted (Ctrl-C / SIGINT), exit 130 | false |
config.invalid | malformed config.toml, a bad enum/auth value, or an openai-kind gateway profile with an api key but no base_url (which would leak the key to api.openai.com); exit 2, consistent across review/history/serve | false |
internal.error | any unclassified failure (the conservative default) | false |
An unrecognized failure stays internal.error; it is never mislabeled as retryable. Classified messages are redacted: no token fragment ever appears.
On miucr review --pr --post, these failures also update the PR summary comment:
operational/provider/infrastructure errors render as GitHub [!WARNING]
alerts, while unknown internal.error failures render as [!CAUTION]. The
alert keeps the stable error.code and hint so a later agent can decide whether
to retry, wait, or inspect host logs.
Provider calls retry transient overload/rate-limit failures before surfacing an error: Anthropic/OpenAI-compatible backends retry 429, any 5xx including 529, and temporary transport failures; the codex backend also retries response.failed stream events and honors Retry-After/resets_in_seconds. The retry loop uses [review.provider_retry], aborts promptly on cancel/timeout, and never retries auth or invalid-request failures. On a persistent codex usage cap, provider.rate_limited carries error.details.resets_in_seconds (or retry_after_seconds) with a hint like usage cap reached, resets in ~2h.
Exit codes
Section titled “Exit codes”| Code | Meaning |
|---|---|
0 | Success; no finding reached the gate. |
1 | Operational error (missing credentials, internal failure). |
2 | Gate failed (a finding reached --gate) or an invalid invocation (bad gate, conflicting modes, bad --output). |
Selection flags
Section titled “Selection flags”Narrow what gets reviewed:
miucr review --staged --ext go,ts # only these extensionsmiucr review --staged --include 'internal/**' # doublestar globs a path must matchmiucr review --staged --exclude '**/*_test.go' # doublestar globs to drop--repo <dir>: repository directory (default.).--ext: restrict to a comma-separated list of file extensions.--include/--exclude: repeatable doublestar globs.
Context & budget flags
Section titled “Context & budget flags”All of these can also be set under [review] in ~/.config/miu/cr/config.toml;
an explicit CLI flag still wins.
--expand <n>: context lines added above/below each changed hunk in the new-content window (default5;0disables).--token-budget <n>: approximate token budget; over budget, context degrades through the truncation ladder (default0, no budget cap).--timeout <dur>: operation timeout. The root default is30s, butreviewuses900sby default unless you set--timeoutor[review].timeout.[review].stalled_timeout: no-progress watchdog for review runs. Default5m; set"0s"only to disable while debugging.[review.provider_retry]: provider API retry policy. Defaults to 10 retries,5sinitial backoff,2mmax backoff, and10mmax retry elapsed time inside the review timeout.--deep-context: heavier defaults for large reviews (--expand 20, auto related-file hop depth, and rootAGENTS.md/CLAUDE.mdcontext from the reviewed revision when present). Token budget and timeout are already capability-first by default.--context-hops <n>: include related-file context up tonhops from the changed files (0disables, max5). This overrides the--deep-contextauto depth. The hop walker reads the reviewed revision, follows Go package imports/reverse imports and basic relative JS/TS/Python imports, and caps files/bytes before the prompt. On fork PRs, root project context and related-file hop context are skipped.--instruction <text>: extra free-text steer for THIS review (e.g. “focus on the auth changes”); injected fenced, context-only, and length-capped, so it never redefines the finding schema.
Symbol Context
Section titled “Symbol Context”symbol_context is a core read-only reviewer tool. It runs inside miucr against
the reviewed git revision; [review.tools.symbol_context] tunes its bounds:
[review.tools]max_retries = 2max_turns = 24retry_backoff = "250ms"
[review.tools.symbol_context]max_bytes = 16000max_files = 2000max_parallel = 8max_retries applies to transient tool execution failures only and is capped at
5; invalid tool arguments and missing files are returned to the model without
retry. max_turns caps the number of model tool turns before miucr withdraws
tools and forces a final JSON answer; 0 or unset uses the default 24, and the
config value is capped at 64.
Use symbol_context for concrete cross-file questions such as
document_symbols, definition, references, incoming_calls,
outgoing_calls, implementations, or dependencies. It covers common Go,
TypeScript/JavaScript, Python, PHP, SQL, Rust, Java/C#, C/C++, and lightweight
Astro/Vue/Svelte component symbols. The scanner is best-effort, bounded, and
read-only; failures stay local to that tool call.
Recommended reviewer flow:
- Use
document_symbolson a changed file to map functions, classes, components, models, and blocks before reading full files. - Use
definitionto jump from a changed call site to the declaration that defines the contract. - Use
references,incoming_calls, andoutgoing_callswhen a changed symbol may affect incoming callers, outgoing calls, or implementations outside the diff. - Use
dependenciesfor dbt/SQL model lineage and table-like references. - Use
file_readonly aftersymbol_contextorgrepnarrows the exact implementation lines needed for a finding.
Every review also gets a compact changed-symbol prelude before the LLM turn.
It summarizes detected symbols in selected changed files first, then leaves the
model-initiated symbol_context tool available for deeper targeted lookup. The
automatic prelude is capped to a small first-pass slice of changed files and at
most the configured max_bytes value, so it cannot replace targeted tool use on
large PRs. Repo-wide definition, implementations, and dependencies scans
use up to max_parallel bounded file readers when the file set is large enough;
set max_parallel = 1 to force serial reads.
Configurable subagents
Section titled “Configurable subagents”[review.subagents] can split large reviews into scoped in-process LLM passes by
glob. This is still one miu-cr review from the outside: each subagent returns
candidate findings, then the normal engine re-anchors quoted code, drops drift,
dedupes, gates, saves history, and posts. Use mode = "auto" to fan out only
when a review crosses the configured file or context-byte threshold, or
mode = "always" for repos where scoped prompts are always useful.
[review.subagents]mode = "auto"max_parallel = 2require_all = true
[[review.subagents.agents]]name = "go"include = ["**/*.go"]system_prompt = "Focus on correctness, concurrency, error handling, and API compatibility."When require_all = true, a failed subagent marks the run as degraded, so
--approval will not approve and checks-mode will not report success.
Provider flags
Section titled “Provider flags”review resolves a provider from flags or the environment. See Providers for the full matrix.
--provider anthropic|openai|auto(defaultauto).--api-key,--base-url,--auth-token,--model: all optional overrides, never persisted.
Project rules
Section titled “Project rules”Every review gets a built-in baseline plus any project rules under .miu/cr/rules/*.md (and ~/.config/miu/cr/rules/*.md) that match the changed files. Rules are review context only; they never gate. Scaffold one with miucr rules init and inspect selection with miucr rules check <path>. See the Project rules guide for the format, trust model, and how rules flow through local / --pr / serve.
PR review
Section titled “PR review”review can review a GitHub pull request directly and (with --post) publish results back to it:
--pr <url|owner/repo#N>: review a GitHub PR (no PAT needed for public repos in dry-run).--post/--no-post: publish inline comments + a summary, or dry-run (--no-postis the default for--pr). Review-mode posting reacts 👀 and creates aReview runningsummary only when no prior summary exists; later commits keep the prior result visible with a temporary reviewing status until the new result replaces it.--token <pat>: GitHub PAT, required only for--post.--mode review|checks: inline review comments (default) or a GitHub CheckRun (survives force-push, works on fork PRs).--suggest: emit one-click GitHub suggestions for proven fixes: single-line replacements and wrap/guard/insert fixes (a multi-line patch on a QuotedCode-proven single-line anchor).--approval off|clean|threshold: submitAPPROVEby policy.cleanrequires zero findings;thresholdallows findings at or below--approval-max-priority(defaultP4: only P4 findings allowed, P0-P3 blocked). Approval reviews include short LGTM-style text with a link to the summary by default. Approval is head-SHA scoped, so a later push can be approved again. Clean re-approvals can stay bodyless; threshold re-approvals include the threshold note. Tune body text with--approval-note.--conversation: on--pr, also fetch the prior PR conversation (the miucr summary, review overviews, finding threads, and developer replies) and inject it fenced/context-only as Untrusted context (dropped on fork PRs); one extra read pass, no extra LLM call (default OFF).--force: re-review even when the head SHA is unchanged since the last saved review. By default an unchanged head SHA short-circuits (skipped_unchanged, no LLM pass); a new commit always re-reviews. See GitHub PR review.
These (and --filter-mode above) only apply on --pr. See GitHub PR review and Serve & action for the full workflow.
version
Section titled “version”miucr version # {"ok":true,"data":{"version":"v0.x.y"}, ...}miucr version -o pretty # the same JSON envelope, indented (not a different format)For non-review commands, -o pretty simply indents the JSON envelope rather than producing a distinct human layout.