Skip to content

CLI

Bifrost can run a single tool once and print the JSON result:

Terminal window
bifrost --root /path/to/project --tool search_symbols --args '{"patterns":["MyClass"]}'

--tool uses the same named tool implementations exposed by the MCP searchtools catalog. Use it when you want the MCP tool surface from a shell script or terminal session without starting a long-lived MCP server.

--args is inline JSON matching the selected tool’s MCP argument object. Omit it for tools that accept an empty object, such as get_active_workspace.

The diff tools can compare exact Git commits or tree objects. For snapshot trees that exist only in a separate Git object store, launch Bifrost with --diff-snapshot-object-dir. The value is a trusted path to a Git objects directory, given as either an absolute path or one relative to the launch working directory; Bifrost resolves it to an absolute path and rejects a missing or non-directory path before serving requests. It is launch configuration, never a diff-tool argument, so callers cannot select an arbitrary filesystem object store.

The flag is valid only with --tool and MCP server modes. For example, a review host that captured two private tree objects can compare them without consulting the current checkout, index, or .gitattributes:

Terminal window
bifrost --root /path/to/project \
--diff-snapshot-object-dir /path/to/turn-snapshot/objects \
--tool analyze_diff \
--args '{"base":"0123456789abcdef0123456789abcdef01234567","target":"89abcdef0123456789abcdef0123456789abcdef"}'

Each explicit endpoint may be a commit-ish or tree-ish (commit resolution wins when both apply). Results label tree endpoints as tree:<oid>. A tree supplied only as target is rejected because it has no parent; provide both base and target for a tree-to-tree comparison.

Run a complete RQL or JSON query_code query from a workspace file without the generic tool wrapper:

Terminal window
bifrost --query-file queries/audit.rql
bifrost --root /path/to/project --query-file queries/audit.json

For example, a saved hierarchy query can use (members (subtypes :transitive true (enclosing-decl (class :name "Service")))), or the equivalent JSON steps enclosing_decl, transitive subtypes, then members.

--query-file accepts .rql and .json files only. The default workspace root is the current directory; query-file paths must stay inside that workspace, including after symlinks are resolved. The file contains the complete query, so it cannot be combined with --tool, --args, or --sources.

A saved query may select planning-only explain or measured profile mode with (explain QUERY), (profile QUERY), or the JSON execution_mode field. Explain does not access analyzer data while lowering and selecting the query plan, although the one-shot CLI still initializes and indexes its workspace before it runs the request. Profile returns the ordinary result and a versioned telemetry report. See Explain and Profile CodeQuery.

Current execution boundary: Policy execution supports :type match, :type taint, :type typestate, and :type assertion. Taint compiles compatible source and sink sets into bounded shared solves. Missing bindings, unsupported semantics, cancellation, or exhausted budgets remain non-clean completion states rather than empty successful results.

bifrost scan is the shipped product’s canonical entry point: it evaluates every built-in policy pack on one project path with zero configuration — no --policy-file, no selectors:

Terminal window
bifrost scan /path/to/project

The path defaults to the current directory. Before the report, the scan witnesses the activated pack set on stderr — one builtin-policy-pack <id>@<version> policies=<count> line per shipped pack plus the catalog SHA-256, the exact line shape bifrost --version prints — so an external evaluation can record exactly which shipped catalog decided. In a --format json report, each run additionally records its policy ID and manifest semantic hash. A build that ships no packs scans to a clean, empty result and says so instead of erroring.

bifrost scan --list-builtin-policies prints the shipped catalog as JSON without running anything (the same document as --list-policies). A scan accepts --format, --fail-on, --evaluation-date, --output, --verbose, and --color with the meanings documented below, shares the policy exit contract (0 clean, 1 gated findings, 2 unreliable), and reads the default suppression, scope, and baseline documents beneath the scanned root. For explicit selection, custom suppression files, baselines, or diff gating, use the policy options on the flag surface — the scan subcommand never changes their behavior.

Evaluate the built-in policy packs with zero configuration on the flag surface — a policy invocation with no --policy-file and no built-in selector activates every shipped pack:

Terminal window
bifrost --root /path/to/project --policy

Run one or more workspace-relative .rqlp policy roots and emit one combined canonical report — an explicit selection replaces the built-in default:

Terminal window
bifrost --root /path/to/project \
--policy-file policies/security.rqlp \
--policy-file policies/correctness.rqlp \
--evaluation-date 2026-07-27 \
--format sarif \
--fail-on warning \
--output reports/bifrost.sarif

List the policies embedded in the installed binary, then select the whole pack, one category, or one stable policy ID:

Terminal window
bifrost --list-policies
bifrost --root /path/to/project \
--policy-pack bifrost.code-smells \
--evaluation-date 2026-07-28 \
--format json
bifrost --root /path/to/project \
--policy-category performance \
--policy-id bifrost.correctness.dynamic-evaluation

--policy-pack, --policy-category, and --policy-id are repeatable and form one deduplicated union in manifest order. They can be combined with --policy-file; built-in and workspace policies share one analyzer snapshot, budget, suppression audit, report, and exit status. --list-policies prints the deterministic manifest without constructing an analyzer and cannot be combined with evaluation options. --list-row-schemas prints the versioned bifrost_relation_schema/v1 catalog of row domains, fields, and expansions a relational policy may bind, under the same rules; the two listings exclude each other. --no-builtin-policies keeps a controlled run free of shipped policies: it requires at least one --policy-file, refuses the built-in selectors, and turns an accidentally input-free invocation into an error instead of the built-in default. bifrost --version records the shipped pack identities (id, version, policy count) and a catalog SHA-256 beneath the version line, so a benchmark can pin exactly which built-in catalog a run activated.

--policy-file is repeatable. Every root must be a (policy ...) document; passing a diagnostic-neutral (endpoint ...) as a root is a status-2 report. Policies may still load endpoints and saved .rql selectors as explicit dependencies. The one-shot CLI starts with empty catalog and endpoint registries. A workspace semantic-pack policy uses the shared .bifrost/packs.json contract: an absent document selects compatible dependency packs for languages present in the workspace, a configured document selects its named ecosystems, and an empty ecosystems array explicitly disables that route. A configured catalog is workspace-relative; without one, activation is ephemeral. The catalog and activation contract do not download packs or dependencies, and compatibility and review_required gates remain authoritative. The released brokk-bifrost facade separately opts into fetching the immutable matching public release bundle only for an exact generated production miss; set BIFROST_SEMANTIC_PACK_DOWNLOAD=off to disable that path. A catalog-backed policy requires a library embedding which explicitly populated TaintCatalogRegistry. A policy that uses only (match-endpoints :ids [...]) also requires an embedding to pre-register those endpoint IDs; in a normal CLI run, the same policy can discover endpoints through a match-directory closure before selecting exact IDs. The CLI does not scan for workspace policies or endpoint catalogs on its own; .bifrost/packs.json is the explicit shared semantic-pack configuration. Built-in policies run by default only when no explicit policy input is given; otherwise they are selected through the explicit selectors above. Policy reports include dependency activation mode and the decisions that explain selected, missing, incompatible, disabled, or incomplete packs.

By default, policy evaluation reads .bifrost/suppressions.json beneath the workspace root. Pass --suppressions-file reviews/accepted.json to select one different workspace-relative JSON file. A missing file means no project suppressions; an invalid, unsafe, oversized, or escaping file produces a canonical diagnostic and status 2 instead of silently running unsuppressed.

Suppression expiration uses --evaluation-date YYYY-MM-DD. Omit it for today’s UTC date, resolved once by the CLI, or provide it explicitly for reproducible JSON/SARIF and stable expiry behavior. A decision remains current on its expires_at date and expires the following day. These options are valid only in policy mode.

Policy mode cannot be combined with --query-file, --tool, --args, --sources, server/REPL modes, --no-line-numbers, or --force-semantic-cpu.

--format accepts human (the default), json, or sarif. All three are rendered from the same canonical report and preserve the same rule/finding IDs, resolved schema and dependency manifests, locations, severity, certainty, completion, classifications, evidence, witnesses, and CVSS variants. SARIF uses Unicode-code-point columns and strong finding IDs as stable partial fingerprints; weak IDs are labeled inconclusive and are not emitted as stable fingerprints.

Human output is concise by default: applied suppressed findings are counted but omitted from the active list. Add --verbose to retain every finding and print suppression reasons, acceptance provenance, and stale/expired/drifted review records. Canonical JSON always retains the complete finding and audit; SARIF retains the result as an external accepted suppression and preserves its strong partial fingerprint. --color auto|always|never controls ANSI severity colors and Unicode status symbols; auto uses them only for a terminal and respects NO_COLOR. Redirected and file output is plain and deterministic by default. These two options are rejected with JSON or SARIF output.

--output PATH writes the bounded report to a temporary file beside the destination, synchronizes it, and atomically replaces the destination. A serialization, write, or replacement failure leaves an existing destination untouched and exits 2. Without --output, the complete bounded encoding is prepared before stdout is written.

--fail-on accepts:

ValueA complete batch exits 1 for
neverNo finding threshold.
findingAny active unsuppressed finding, including unrated.
noteAn active unsuppressed note, warning, or error.
warningAn active unsuppressed warning or error (default).
errorAn active unsuppressed error only.

The process status is:

StatusMeaning
0Every requested policy completed and no active unsuppressed finding met the threshold.
1Every requested policy completed and at least one active unsuppressed finding met the threshold.
2A policy, suppression, schema, composition, evaluation, completeness, serialization, or output failure made the batch unreliable. Status 2 takes precedence over status 1.

--fail-on never disables only the finding threshold; it cannot turn an invalid, cancelled, incomplete, failed, or unsupported policy into a clean run. --require-explicit-schema-versions rejects compatible inference for the root and every loaded endpoint or RQL dependency. Omitted versions otherwise select only the newest compiled-in compatible lineage.

Gate only on new findings (--diff-base, --no-incremental)

Section titled “Gate only on new findings (--diff-base, --no-incremental)”

--diff-base REV evaluates the same policies twice: once against the committed content of REV (any revision git rev-parse accepts, peeled to a commit) and once against the working tree. Findings are joined by their stable identities, each head finding is classified new or persisting, fixed base findings are summarized, and the --fail-on threshold counts only the new findings. A pull request that introduces one finding into a repository with hundreds of pre-existing ones fails with exactly that one finding gating.

Terminal window
bifrost --root . \
--policy-pack bifrost.code-smells \
--format sarif --output out.sarif \
--diff-base origin/main

The CLI does not compute merge bases; pass the pull request’s merge base explicitly (git merge-base HEAD origin/main, or the base SHA GitHub provides). If the workspace root is not inside a git repository or the revision does not resolve, the run exits 2. If the base revision resolves but its evaluation is unreliable, the run degrades to full gating with a diff-base-unreliable diagnostic, so a broken base can never hide new findings. See Static-Analysis Policies for the join semantics and CI Gating with GitHub Actions for the pull-request recipe.

--diff-base reads and writes the repository’s analyzer cache under .bifrost/cache. It stores what each policy concluded over the base revision and what every part of that evaluation read, so a later run against the same base skips the base export and build, and recomputes only the parts of the working tree whose inputs changed. Findings, identities, diagnostics, completion tiers, and the exit status do not depend on what the cache holds.

--no-incremental turns that reuse off for one run: every policy is evaluated in full on both revisions, and nothing is looked up or stored. Reuse is on by default and produces the same report, so use this switch only to compare a run against the full dual evaluation when you are diagnosing a difference. The report’s incremental section then reports every policy as evaluated in full with the reason incremental_disabled.

Accept every existing finding (--accept-current, --baseline-file)

Section titled “Accept every existing finding (--accept-current, --baseline-file)”

--accept-current runs the selected policies and writes a bulk-acceptance baseline document containing every current strong finding identity, so later runs of the same selection gate only on findings introduced afterwards:

Terminal window
bifrost --root . \
--policy-pack bifrost.code-smells \
--accept-current

The document is written to .bifrost/baseline.json (or the workspace-relative path given by --baseline-file, which also selects the document every evaluation reads). Acceptance forces --fail-on never internally and writes only on a clean status: an unreliable or non-exhaustive run exits 2 and writes nothing, because an identity the run could not prove cannot be accepted. Weak-identity findings are never written and their count is reported on stderr. Findings already claimed by a suppression or directory scope are not written either; they stay governed by their own mechanism. --accept-current cannot be combined with --fail-on or --diff-base, and regeneration is always an explicit re-run — the baseline never refreshes itself.

On later runs, baselined findings stay in the report with a baseline decision, stop counting toward --fail-on, and are audited like suppressions: a malformed or oversized document is a diagnostic and status 2, a policy edit marks its entries drifted without reactivating them, and an entry whose finding an exhaustive run proves absent is reported stale. See Static-Analysis Policies for the semantics and CI Gating with GitHub Actions for the onboarding recipe.

match, taint, query-local typestate, and assertion evaluation are available now. Typestate compiles resolved subject/event selectors into the semantic protocol engine and preserves finding identity, locations, witnesses, and completeness across all three report formats. Taint resolves typed endpoint bindings, batches compatible source/sink demand, runs the production data-flow engine, and projects one retained report. Source-backed analysis works in the ordinary CLI; external procedure summaries can use the shared workspace pack activation contract, with an embedding still able to supply an explicit semantic-model catalog and activation request. See Data Flow, Taint, and Typestate and Static-Analysis Policies for execution boundaries, endpoint composition, completeness, finding identity, and CVSS rules.

For the available tool families and tool names, see MCP Server. For a single tool’s description and parameters, ask the CLI directly:

Terminal window
bifrost --help scan_usages_by_location
bifrost --help scan_usages_by_reference

Tool mode mirrors MCP’s structured result shape, but keeps stdout machine-oriented by omitting rendered text content:

{
"structuredContent": {},
"isError": false
}

Tools whose normal MCP response is text-only return only:

{
"isError": false
}

Use the MCP page as the catalog for what each tool does. Use bifrost --help <tool> for the exact input schema accepted by the installed binary.

Whole-workspace analysis honors root and nested .bifrostignore files. Matching tracked or untracked files are excluded from code intelligence but remain visible to text-level tools. See Workspace Scope for syntax and the complete visibility contract.

Use --sources when a one-shot query only needs part of a repository. Each value can be a file, directory, or glob under the selected root:

Terminal window
bifrost --root /path/to/project --tool get_symbol_sources --sources src --sources 'tests/**/*.rs' --args '{"symbols":["src/main.rs"]}'

An explicit --sources selection overrides .bifrostignore for the selected files.

A --sources run answers cross-file questions over only the files you named, so its results say so. scan_usages_by_reference, scan_usages_by_location, usage_graph, most_relevant_files, search_symbols, query_code, and get_active_workspace each report "session_subset": {"files": N} giving the number of files the session covers — on scan_usages inside its scope block, elsewhere at the top level. Read “no results” from such a run as “nothing in those N files”, not “nothing in the repository”. A run without --sources omits the field entirely.

File-bearing CLI tool arguments also accept git history paths in <commit-ish>:<path> form, such as HEAD~2:src/main.rs. Parser-backed tools build the one-shot analyzer workspace with that historical content.

Tool mode prints JSON by default. Pass --no-line-numbers to remove rendered line and line-range prefixes from text previews while keeping structured line metadata unchanged.

List modes and toolsets:

Terminal window
bifrost --help

The repository also builds the most_relevant_files helper binary:

Terminal window
cargo build --bin most_relevant_files
./target/debug/most_relevant_files --root /path/to/project path/to/seed_file.py

Pass --exclude-tests to omit files classified as tests or test support from the ranking without allowing them to consume the result limit.

Run bifrost --install to register a user-level MCP server named brokk with installed Codex, Claude Code, OpenCode, Kimi Code, Hermes, and Oh My Pi clients. The action registers the current executable with --mcp core. It does not install skills, instruction files, host applications, or the original Pi extension. See Install Bifrost for details.