Documentation
The full reference for the mind CLI: install, commands, configuration, hooks, and suppressions. New here? Start with the quick start.
Install
Install the mind CLI on macOS or Linux:
curl -fsSL https://mindrealm.ai/install.sh | shOn Windows, in PowerShell:
iwr -useb https://mindrealm.ai/install.ps1 | iexRe-run the same command any time to update. The installer places a single mind binary in one location and touches nothing else: /usr/local/bin or ~/.local/bin on macOS and Linux, %LOCALAPPDATA%\Programs\mind on Windows.
Install manually
Prefer to install by hand? Download the archive for your platform from the releases page:
- macOS / Linux: download
mind-<os>-<arch>.tar.gz, extract it withtar -xzf, make it executable withchmod +x mind, and movemindonto a directory in yourPATH(for example/usr/local/bin). - Windows: download
mind-windows-<arch>.zip, extractmind.exe, and add its folder to yourPATH.
Authenticate
However you installed it, sign in once before reviewing:
mind loginBehind a proxy or signing in from a remote session? Keep the terminal command running and open its printed URL in your configured browser:
mind login --no-browserThe browser must bypass the proxy for localhost, 127.0.0.1, and ::1 so authentication can return to the waiting CLI.
Review
From inside a repository connected to your GitHub App, review your changes (diff-only by default, or --full-scan for the whole repo):
mind reviewCommands
| Command | What it does |
|---|---|
mind login | Authenticate. Opens your browser. |
mind login --no-browser | Print the sign-in URL without opening a browser. Use it for proxies and remote sessions. |
mind setup | Detect your agent(s) and set up the stop-hook auto-fix loop. |
mind review | Review your changed files. Diff-only by default. |
mind config | Show the resolved configuration and which layer supplied each value. |
mind hook | Dispatch agent lifecycle hook events. The entry point your agent calls when it stops; you won’t run it by hand. |
mind feedback | Review past runs and record feedback on findings, or report a bug or feature request. |
mind logout | Log out and remove stored credentials. |
mind update | Update the mind CLI to the latest release. |
mind version | Print the CLI version. |
mind --help | List every command and flag. |
Review categories
Mindrealm reviews Go, Python, TypeScript, and Rust across these categories:
| Category | Looks for |
|---|---|
| Hygiene | Dead code, unused exports, magic values, leftover markers, naming. |
| Reliability | Unhandled errors, swallowed exceptions, resource leaks, missing timeouts. |
| Architecture | Coupling, cohesion, duplication, oversized types and interfaces. |
| Performance | Work inside loops, unbounded growth, repeated expensive calls. |
| Concurrency | Unawaited calls, detached tasks, blocking work in async paths. |
| Security | Hardcoded secrets, SQL/command/code injection, dangerous taint flow. |
| Structure | Long functions, deep nesting, long parameter lists, large files. |
| Testing | Missing or weak assertions, skipped tests, mock abuse, test pollution. |
| Language gates | Your toolchain linters and type checks (go vet, golangci-lint, ruff, tsc, eslint, cargo clippy). |
Every review is deterministic: the same code returns the same findings every run. No model decides what gets flagged.
Review scope
mind review reviews your changed files by default (diff-only), matching what the stop hook reviews. To scan the whole repository:
mind review --full-scan--diff-only and --full-scan are mutually exclusive, and --commit implies diff-only. On a trial, reviews are diff-only by default; whole-repo --full-scan is a paid-plan feature, though a trial organization can ask Mindrealm to enable it. Both are config-backed: set diff_only or full_scan in mind.yaml.
mind review must run from inside a git repository whose origin is a GitHub repo connected to the Mindrealm GitHub App. Outside a git repo, or with no GitHub origin, it fails fast with a clear message. . After the GitHub App is installed for your organization, public repos the server can clone work as-is. A private repo not connected to that installation returns a clear not-connected message rather than a raw clone error.
| Flag | Effect |
|---|---|
--full-scan | Scan the entire repository, not just changed files (paid plan). |
--diff-only | Only report findings in changed files (the default). |
--commit <sha> | Review files changed in a specific commit. Implies diff-only. |
--staged | Only staged changes. |
--unstaged | Only unstaged changes. |
mind review [paths...] | Scope the review to specific files or directories. |
--analyzers <id,...> | Run only these rules, comma-separated. |
--profile <name> | Run only the rules in a named profile. Overrides the mind.yaml profile; --analyzers adds to it. |
--min-severity / --max-severity | Bound the findings shown. One of info, minor, major, blocker. |
--show-suppressed | Include suppressed findings in the output. |
--format text|json | Output format. |
--enable-go-test | Run the go-test gate (off by default for speed). |
--enable-pytest | Run the pytest gate (off by default for speed). |
--enable-js-test | Run the JS test gate, jest or vitest (off by default for speed). |
--enable-security-gates | Run the security gates, gitleaks secrets and osv-scanner dependency CVEs (off by default). |
--no-gates | Disable all gates (linters, type checks). |
On a very large repository, a --full-scan can exceed the review time budget (10 minutes by default). When it does, the review returns a partial result: the findings produced before the deadline, shown with a “Partial analysis” banner. It still blocks on any blocker or major finding, does not fail with zero findings, and the findings it reports are real. For a faster review on a huge repo, narrow the scope with --diff-only, --analyzers, or --profile.
Suppressions
Disagree with a finding? Silence one check with a mind:disable comment on the line above the code. It stays recorded and reviewable, never hidden. Every suppression is meant to be temporary.
// mind:disable Go, TypeScript, Rust
# mind:disable Python
// mind:disable unused-export silence one rule by id
// mind:disable unused-export - reason goes hereSuppress a range with a begin/end pair:
// mind:disable-begin generated block
// ... lines here are not reviewed ...
// mind:disable-endA bare mind:disable silences every check on that line; naming an id silences just that one. Use Python's # comment prefix, and // for Go, TypeScript, and Rust.
See suppressed findings with mind review --show-suppressed, and require a reason on every directive with require_reason in mind.yaml.
mind:disable silences check findings in your code. Gate output (linters, type checks, tests) is turned off separately, with disabled_analyzers or --no-gates. See Gates.
Feedback
mind feedback opens an interactive menu to tell us when a finding was right or wrong, and to report bugs or request features. Run it with no arguments from anywhere you are signed in:
mind feedbackGive feedback on findings
Pick one of your recent reviews (run mind review first if you have none yet), drill into a finding, and record your call. Each verdict takes an optional reason and is keyed to the finding's stable fingerprint, so it sticks even after you re-run the review.
| Your call | Means |
|---|---|
agree | You agree with the finding. |
disagree | You disagree with the finding. |
request-changes | You want changes before this finding is resolved. |
manual-fix-applied | You fixed it by hand rather than as suggested. |
skip | Move on without recording a verdict. |
Report a bug or request a feature
The same menu lets you file a bug report or a feature request: give a one-line summary, then a few lines of detail. Mindrealm tags it with your CLI version and current repository so we have the context to act on it.
Custom rules
MindQL lets you encode your team's standards as rules that run across Go, Python, TypeScript, and Rust: no build, no database, and deterministic findings on every run. Rules live in .mind/rules/*.mql and run in every mind review, right alongside the built-in checks.
One rule, four languages
This rule keeps your API layer from importing a database package directly. The same .mql fires on Go, Python, TypeScript, and Rust, with no per-language rewrite:
rule api_no_db {
match import i
where i.file.path ~ "internal/api/" and i.imports_package("db*")
block "api must not import a db package directly"
}MindQL reads every language through the same import graph, so one rule covers them all. No model decides what gets flagged; the same code produces the same findings on every run.
What a rule looks like
A rule names what to match, filters it with a condition, then emits a finding at a fixed severity:
| Part | What it does |
|---|---|
match | Pick what to look at: an import, function, call, type, symbol, file, or the whole review. |
where | Filter the match with a condition over import paths, call reachability, structure, and names. |
def | Name a reusable condition once and share it across rules. |
| action | When the match passes the filter, emit a finding at this severity (the verbs are below), followed by the message your team sees. |
match: what you can read
match picks what to look at. Each kind exposes attributes you filter on in where. Glob arguments (in quotes) match names or paths; every entity also carries file and line.
| Match | What you can read |
|---|---|
import | module, module_path, last_segment, segments, imports_package("glob") |
func | name, exported, is_method, receiver, body_lines, callers, caller_count, callees, calls("glob"), reachable_from("glob"), is_entrypoint("kind") |
call | name, in_loop, inside("kind") — a call site |
type | name, exported, method_count, field_count, exported_field_count, method_lines, method_names, is_god_object |
symbol | name, symbol_kind, exported, receiver |
file | path, language, is_test, is_generated, is_third_party, is_config, class, imports, symbols |
node | type, text, name, in_loop, in_test, inside("kind"), has("rule") — raw AST, for shapes the typed kinds do not cover |
review | diff.files (path, status, changed_lines, is_test), changed("glob") — the whole change |
where: filtering the match
where keeps only the matches that satisfy a condition, built from the matched entity's attributes with these operators and helpers:
| Operator / form | What it does |
|---|---|
~ | Regex match against a string: path ~ "internal/api/". |
and or not | Combine conditions. |
== != > < | Compare values: body_lines > 80. |
. | Read a nested attribute: i.file.path. |
any(list, cond) | True if any item matches: any(diff.files, .path ~ "src/"). |
def: reusable conditions
def names a condition once so you can share it across rules. It takes the matched entity as a parameter and returns a boolean:
def is_handler(f) = f.is_entrypoint("http")
rule big_handler {
match func f
where is_handler(f) and f.body_lines > 80
emit minor "split this handler; it is doing too much"
}action: emit a finding
The action fires when a match passes the filter: a rule emits at one fixed severity, so a finding's weight is part of the rule, not a guess:
| Keyword | Severity | Meaning |
|---|---|---|
emit blocker | Blocker tier. Stops the change at a gate. | |
emit major | Major tier. Must be fixed before merge. | |
emit minor | Minor tier. Worth addressing. | |
emit info | Info tier. A small remark. |
More examples
Flag oversized handlers
Flag an oversized HTTP handler: a function reached from a *Handler whose body runs over 80 lines.
rule long_http_handler {
match func f
where f.reachable_from("*Handler") and f.body_lines > 80
emit minor "split this handler; it is doing too much"
}Catch queries in loops
Catch a database query that runs inside a loop, where it should be lifted out.
rule query_in_loop {
match call c
where c.name ~ "Query" and c.in_loop
emit minor "move this query out of the loop"
}Add a rule
Drop a .mql file in .mind/rules/ and run mind review. Rules are checked in with your code, so the whole team gets them:
// .mind/rules/detection_suite_reminder.mql
// A rule we run on our own repo: when we change a check,
// it reminds us to add a test for it in the same change.
rule detection_suite_reminder {
match review
where any(diff.files,
(.path ~ "internal/software_code_" and not .is_test)
or .path == "internal/analysis/rule_ids.go")
and not any(diff.files, .path ~ "internal/detectionsuite")
emit minor "detection changed; add a detectionsuite regression fixture"
}Commit .mind/ to version control so the whole team gets your rules.
What works today
| Available now | Coming next |
|---|---|
| Match imports, functions, calls, types, symbols, files, and the whole review. | Metavariable patterns for matching code shapes. |
| Filter with conditions over import paths, call reachability, structure, and file class. | Autofix that rewrites the flagged code for you. |
| Classify each file (code, test, config, docs, generated) and flag god-object types. | A package entity for module coupling, instability, and dependency cycles. |
| Reusable conditions, fixed severities, deterministic findings, four languages. | Rules that carry their own tests. |
Want your team's standards encoded as rules without doing it yourself? I'll build them with you.
Gates
Gates run your own toolchain on the machine doing the review: the linters, type checks, builds, and (optionally) tests you already use. They run alongside the deterministic checks and merge into one report, so a compile error, a lint violation, and a design finding all land together. A failing gate can block the review.
Because they run where your environment lives, gates catch what your machine already knows: real build and test failures, not guesses. The trade-off is speed. Linters and type checks are quick and run by default; the test gates are the slow ones and stay off until you opt in.
Why let Mindrealm run them instead of asking the agent to? Agents are unreliable at running a whole toolchain. Asked to run several linters and the test suite, an agent often runs one, a couple, or none, then reports success anyway. The stop hook runs every gate every time and blocks on any failure, so the checks you depend on actually happen on every change.
| Gate | Tools | Default |
|---|---|---|
| Build and type checks | go build, go vet, tsc, cargo check | on |
| Linters | golangci-lint, ruff, vulture, eslint, cargo clippy | on |
| Module hygiene | go mod tidy, go mod verify | on |
| Test runners (slow) | go test, cargo test, pytest, jest/vitest | off |
Test gates
Turn the test gates on per project (or globally) when the extra seconds are worth it:
# .mind/mind.yaml
gates:
go_test: true
cargo_test: true
pytest: true
js_test: trueThe JS test gate auto-detects jest or vitest from your package.json. Or enable a test gate for a single run with --enable-go-test, --enable-pytest, or --enable-js-test. To skip every gate for a faster, checks-only pass, use mind review --no-gates or turn them off in config with disabled_analyzers (see Configuration).
The JS test gate detects jest and vitest today; node:test, mocha, and other runners are not supported yet. If a different test runner, another toolchain gate, or a project-specific custom gate would help your team, tell us what you run.
Security gates
Two extra gates run dedicated security scanners over your repo: one for leaked secrets, one for vulnerable dependencies. They are off by default. Unlike the build, lint, and type gates, which key off the change in front of them, these scan the whole tree and are built for a deep, point-in-time security review, so they ship dark until you ask for them.
| Security gate | Tool | What it finds | Default |
|---|---|---|---|
| Secrets | gitleaks | Hardcoded API keys, tokens, and credentials across any language (regex + entropy). | off |
| Dependency CVEs | osv-scanner | Known vulnerabilities in your dependencies. Reads Go, npm, PyPI, and Cargo lockfiles. | off |
Turn both on for a project (or globally) in config:
# .mind/mind.yaml
gates:
security: trueOr enable them for a single run with --enable-security-gates. The scanners are optional local tools: if gitleaks or osv-scanner is not installed on the machine running the review, that gate simply skips. It never fails the run. These gates are primarily meant for the in-depth security pass of a consulting audit, not the per-change review loop.
Hook setup
mind setup wires Mindrealm into your agent so it reviews every change automatically. It detects which agents are present (Claude Code, Codex, Kimi Code, and Antigravity), asks which to set up, and asks where: globally in your home config (the default), or just the current project with --local. Kimi Code is the exception: it reads hooks only from ~/.kimi-code/config.toml, so it is always set up globally. Each agent is covered below.
Why a hook? Getting an agent to reliably review its own work or follow a rule does not work. It forgets, skips, or calls itself done. A stop hook runs every time the agent finishes, no matter what. When Mindrealm flags findings it blocks the stop and feeds them back as the agent's next instruction, so the agent fixes them before the work reaches you. That auto-fix loop works on Claude Code, Codex, Kimi Code, and Antigravity.
Skip the prompts with flags, for example mind setup --platform claude-code,codex,kimi,antigravity --global --yes.
Claude Code
mind setup --platform claude-code writes a Stop hook and the Bash(mind:*) permission to ~/.claude/settings.json (or .claude/settings.json with --local). The hook runs as soon as it is written, with no approval step:
{
"hooks": {
"Stop": [
{ "hooks": [{ "type": "command", "command": "mind hook" }] }
]
},
"permissions": { "allow": ["Bash(mind:*)"] }
}Codex
mind setup --platform codex writes a Stop hook to ~/.codex/hooks.json (or .codex/hooks.json with --local):
{
"hooks": {
"Stop": [
{ "hooks": [{ "type": "command", "command": "mind hook" }] }
]
}
}One extra step: Codex requires you to trust hooks before they run. After setup, start Codex and run /hooks to review and trust the Mindrealm Stop hook. It stays inactive until trusted, and Codex re-prompts whenever the command changes.
Kimi Code
mind setup --platform kimi splices a managed hooks block into ~/.kimi-code/config.toml ($KIMI_CODE_HOME/config.toml when that is set). Kimi Code reads hooks only from that file, so setup is always global and --local does not apply. The block runs mind hook when the agent stops, same as the other agents. Re-run setup any time to refresh the block; it replaces only what it manages.
Antigravity
mind setup --platform antigravity writes a mindrealm-review Stop hook to ~/.gemini/config/hooks.json (or .agents/hooks.json with --local). The Stop hook runs mind hook when the agent finishes, same as the other agents.
Removing the hook
Run mind setup --uninstall to remove it (add --platform claude-code to target one agent, or --local for the current project only). It removes only what setup added, leaving your other hooks and permissions intact, and is a no-op when nothing is installed. On Codex you can instead disable the hook from inside the agent with /hooks, without editing any files.
To wire it by hand, drop the matching block into the file above. The reviewer itself is agent-agnostic: mind review and the GitHub App review code from any agent or author.
Continuous integration
mind login is interactive, so for CI generate a CI token from your dashboard and give it to the CLI headless. The token is organization-scoped, review-only, and revocable, and it stays valid for long runs, so a --full-scan never expires mid-review.
Provide it as the MIND_CI_TOKEN environment variable:
MIND_CI_TOKEN=mind_ci_xxxxx mind review --full-scanOr store it once with mind login --token for repeated local headless use:
mind login --token mind_ci_xxxxx
mind reviewThe repository still needs the Mindrealm GitHub App installed: the token authenticates the caller, the app authorizes the repo.
GitHub Actions
Add the token as a repository secret, then run mind review as a step. fetch-depth: 0 gives mind the history it needs to compute the diff:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # so mind can diff against the base commit
- run: curl -fsSL https://mindrealm.ai/install.sh | sh
- run: mind review
env:
MIND_CI_TOKEN: ${{ secrets.MIND_CI_TOKEN }}Exit codes
mind review sets its exit code so a CI step fails the build on a blocking finding:
| Code | Meaning |
|---|---|
0 | Review passed. |
1 | A blocker or major finding fails the build. Also the fallback for an untyped error. |
2 | Config or credential problem, for example a revoked token. |
3 | Network or service problem. |
4 | Invalid arguments. |
Machine-readable output
For machine-readable output use mind review --format json (the default is text); each finding carries severity, filePath, startLine, description, remediation, and rule_id.
If your pipeline already runs its own linters, type checks, and tests, mind review --no-gates skips Mindrealm's gate checks while the rest of the review still runs and still blocks on a must-fix.
Configuration
mind reads YAML config from two locations: a project file at .mind/mind.yaml and a global file at ~/.config/mind/mind.yaml. The project file overrides the global one.
Precedence, highest first: a command-line flag, then environment variables (MIND_*), then .env in the current directory, then the project .mind/mind.yaml, then the global ~/.config/mind/mind.yaml, then built-in defaults.
Only the highest layer that sets a key counts, so an edit to a lower one has no effect. mind config prints every resolved key with the layer that supplied it, which is the fastest way to find out why an edit did nothing.
$ mind config
user config file: /home/you/.config/mind/mind.yaml
project config file: .mind/mind.yaml
KEY VALUE SOURCE
grpc_url localhost:50051 environment MIND_GRPC_URL
task_grpc_url localhost:50051 .env MIND_TASK_GRPC_URL
auth_token (set, not shown) credentials file
timeout 60s defaultCredentials are never printed. An address a client could never dial — a port outside 1-65535, or a value with no port — fails immediately and names the layer that supplied it, rather than being silently ignored.
Top-level keys
| Key | Default | Meaning |
|---|---|---|
timeout | 60s | Request timeout. |
diff_only | false | Review only changed files. With neither key set, reviews are diff-only. |
full_scan | false | Review the whole repository instead (paid plan). |
profile | none | Run only the rules in a named profile. A --profile flag overrides it. |
debug | false | Debug logging. MIND_DEBUG and --debug also turn it on. |
min_severity | none | Hide findings below this severity (info, minor, major, blocker). |
max_severity | none | Hide findings above this severity. |
disabled_analyzers | none | A list of rules to turn off, by full ID, short name, or concern. |
require_reason | false | Require a reason on every mind:disable directive. |
show_suppressed | false | Include suppressed findings in output. |
gates.go_test | false | Run the go-test gate. |
gates.cargo_test | false | Run the cargo-test gate. |
gates.pytest | false | Run the pytest gate. |
gates.js_test | false | Run the JS test gate (jest or vitest, auto-detected). |
gates.security | false | Run the security gates (gitleaks secrets, osv-scanner dependency CVEs). |
output | none | Output template overrides. See Output templates below. |
thresholds | none | Per-rule thresholds. See Thresholds below. |
Linters and type checks for all four languages run automatically. Each language also has an opt-in test gate: go-test, cargo-test, pytest, and a JS test gate that runs jest or vitest. See Gates for the full list.
disabled_analyzers accepts a full rule ID, a short name, or a concern that matches a whole group. The gate concern turns off every gate at once:
# .mind/mind.yaml
disabled_analyzers:
- gate # all gates
- software:code:gate:go-test # one rule by full id
- golangci-lint # short name also matchesEnvironment variables
These override the matching config keys.
| Variable | Default | Overrides |
|---|---|---|
MIND_TIMEOUT | 60s | timeout |
MIND_DEBUG | false | debug |
MIND_FINDING_TEMPLATE | default | output.finding_template |
MIND_HEADER_TEMPLATE | default | output.header_template |
Thresholds
Thresholds set when the size, coupling, and duplication checks fire. They all live under thresholds in mind.yaml. A few common ones:
| Key | Default | Meaning |
|---|---|---|
long_function_threshold | 75 | Lines before a function is flagged. |
large_file_threshold | 1500 | Lines before a file is flagged. |
interface_size_threshold | 10 | Methods before an interface is flagged. |
method_count_major | 20 | Methods before a type must be split. |
type_cohesion_major | 600 | Cohesion score before a type is flagged. |
duplicate_code_block_min_lcs_similarity | 75 | Minimum percent similarity to flag duplicate blocks. |
More keys exist under thresholds to adjust coupling, duplication, and complexity limits.
Output templates
Customize what your agent sees in the stop-hook output. finding_template and header_template (under output) each accept default or a path to a Go text/template .tmpl file. analyzer_templates maps a rule short name to its own template.
Template keys, per finding: Severity, ShortName, RuleID, FilePath, StartLine, EndLine, Description, Remediation, Value, Threshold. For the header: TotalCount, BlockerCount, MajorCount, MinorCount, InfoCount, BatchSize, HasMore, Findings.
The default finding template:
[{{.Severity}}] {{.ShortName}} — {{.FilePath}}:{{.StartLine}}: {{.Description}}{{if .Remediation}}
fix: {{.Remediation}}{{end}}