Skip to content

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.

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.

Terminal window
miucr review --staged # staged changes vs the index
miucr 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
  • --staged reviews staged changes, diffed against HEAD (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 / --to review <to> against the merge-base of the two refs (merge-base(from,to)..to), matching what a PR introduces.
  • --commit reviews one commit against its first parent.

--gate makes review CI-friendly: when a finding’s severity reaches the gate, the process exits non-zero (exit code 2).

Terminal window
miucr review --from main --to HEAD --gate high
  • Severities, low → high: info, low, medium, high, critical.
  • --gate accepts those plus none. Default is high.
  • --gate none reports findings but never fails the build.
  • An unrecognized gate is rejected up front; a typo can never silently disable gating.

-o / --output is a global flag: json (default), pretty, or sarif.

Terminal window
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 / IDEs

pretty 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.

-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.

Terminal window
miucr review --staged --sarif-out miucr.sarif # JSON on stdout + SARIF file
miucr review --pr owner/repo#123 --post --sarif-out miucr.sarif

It 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 (default diff_context) selects which findings are eligible for inline PR comments on --pr:

ModeInline-eligible findings
addedonly findings on added (+) diff lines
diff_context (default)findings on any added or context diff line
filefindings on any file present in the diff
nofilterevery 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 (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.

FormatComment 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.
minimalDrops 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 (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 formatStructure
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.
markdownThe 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.codeWhenretryable
agent.auth_failedbad/invalid API key (401/403)false
agent.auth_expiredexpired OAuth token (401/403, incl. codex still-401-after-refresh)false
provider.rate_limitedprovider returned 429true
agent.unavailableprovider returned 5xx / 529true
review.timeoutthe review exceeded --timeouttrue
review.stalledthe review emitted no progress for [review].stalled_timeouttrue
review.canceledinterrupted (Ctrl-C / SIGINT), exit 130false
config.invalidmalformed 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/servefalse
internal.errorany 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.

CodeMeaning
0Success; no finding reached the gate.
1Operational error (missing credentials, internal failure).
2Gate failed (a finding reached --gate) or an invalid invocation (bad gate, conflicting modes, bad --output).

Narrow what gets reviewed:

Terminal window
miucr review --staged --ext go,ts # only these extensions
miucr review --staged --include 'internal/**' # doublestar globs a path must match
miucr 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.

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 (default 5; 0 disables).
  • --token-budget <n>: approximate token budget; over budget, context degrades through the truncation ladder (default 0, no budget cap).
  • --timeout <dur>: operation timeout. The root default is 30s, but review uses 900s by default unless you set --timeout or [review].timeout.
  • [review].stalled_timeout: no-progress watchdog for review runs. Default 5m; set "0s" only to disable while debugging.
  • [review.provider_retry]: provider API retry policy. Defaults to 10 retries, 5s initial backoff, 2m max backoff, and 10m max retry elapsed time inside the review timeout.
  • --deep-context: heavier defaults for large reviews (--expand 20, auto related-file hop depth, and root AGENTS.md / CLAUDE.md context from the reviewed revision when present). Token budget and timeout are already capability-first by default.
  • --context-hops <n>: include related-file context up to n hops from the changed files (0 disables, max 5). This overrides the --deep-context auto 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 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 = 2
max_turns = 24
retry_backoff = "250ms"
[review.tools.symbol_context]
max_bytes = 16000
max_files = 2000
max_parallel = 8

max_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:

  1. Use document_symbols on a changed file to map functions, classes, components, models, and blocks before reading full files.
  2. Use definition to jump from a changed call site to the declaration that defines the contract.
  3. Use references, incoming_calls, and outgoing_calls when a changed symbol may affect incoming callers, outgoing calls, or implementations outside the diff.
  4. Use dependencies for dbt/SQL model lineage and table-like references.
  5. Use file_read only after symbol_context or grep narrows 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.

[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 = 2
require_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.

review resolves a provider from flags or the environment. See Providers for the full matrix.

  • --provider anthropic|openai|auto (default auto).
  • --api-key, --base-url, --auth-token, --model: all optional overrides, never persisted.

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.

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-post is the default for --pr). Review-mode posting reacts 👀 and creates a Review running summary 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: submit APPROVE by policy. clean requires zero findings; threshold allows findings at or below --approval-max-priority (default P4: 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.

Terminal window
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.