openlore --helpgroups these commands by job — set up & run · navigate · govern a change · inspect · multi-repo · advanced/experimental — so the front door stays legible as the surface grows. The table below is the full alphabetical reference.
| Command | Description | API Key |
|---|---|---|
openlore init |
Initialize configuration | No |
openlore install |
One-command setup: wire agent surfaces (substrate MCP + hooks) and build the index |
No |
openlore connect [agent] |
Wire a specific coding agent to the MCP server + hooks | No |
openlore analyze |
Run static analysis (repository-scoped single flight — a second analyze reports the live owner instead of duplicating work) | No |
openlore analyze --wait |
Follow an analysis another process already owns and return its result | No |
openlore embed --local |
Enable on-device semantic embeddings (no API key; downloads ~23 MB model) and rebuild the index | No |
openlore embed --off |
Revert to the first-class keyword (BM25) default and rebuild | No |
openlore orient |
Relevant functions, callers, specs, and insertion points for a task (the flagship) | No |
openlore orient --inject |
Emit a bounded, ignorable task-scoped orientation block for a pre-turn hook | No |
openlore search <query> |
Search indexed code or specs; optionally explain one named target | No |
openlore generate |
Generate specs from analysis | Yes |
openlore generate --adr |
Also generate Architecture Decision Records | Yes |
openlore generate --dry-run |
List the stages and domains that would run, then stop — no provider call, no cost, no writes | No |
openlore generate --plan |
Explicit alias for the same free plan-only output | No |
openlore generate --preview |
Run the real pipeline in an isolated temporary workspace and show the candidate spec diff; provider cost occurs and the project tree is left byte-identical | Yes |
openlore mapping refresh |
Rebuild the deterministic spec→code link index from the anchors written in existing specs | No |
openlore verify |
Verify spec accuracy | Yes |
openlore drift |
Detect spec drift (static) | No |
openlore drift --use-llm |
Detect spec drift (LLM-enhanced) | Yes |
openlore drift --suggest-tests |
After drift, list test files covering affected domains | No |
openlore audit |
Report spec coverage gaps: uncovered functions, hub gaps, stale domains | No |
openlore test |
Report spec test coverage (scan test files for // openlore: annotation tags) |
No |
openlore test --min-coverage <n> |
Fail when effective spec coverage is below N% (CI gate) | No |
openlore digest |
Plain-English summary of all specs for human review | No |
openlore prove |
Measure OpenLore's token value on your repo (WITH vs WITHOUT agent pass) | Yes |
openlore prove --estimate |
Deterministic graph projection when ≥1 function has ≥2 callers; sparse refusals report the measured count — no agent, no key | No |
openlore prove --json|--markdown|--save |
CI-consumable scorecard / paste-ready block + badge / dated record under .openlore/prove/ |
Matches arm |
openlore decisions |
Manage architectural decisions: list, approve, reject, sync to specs and ADRs | No |
openlore decisions --gate |
Pre-commit gate check: exit non-zero if decisions await review (used by the hook) | No |
openlore setup --tools claude |
Install the decisions pre-commit hook (+ skills) that gates commits until decisions are reviewed | No |
openlore run |
Full pipeline: init, analyze, generate | Yes |
openlore view |
Launch interactive graph & spec viewer in the browser | No |
openlore setup |
Install workflow skills into the project (Vibe, Cline, GSD, BMAD, Pi) | No |
openlore federation add|remove|list |
Manage the multi-repo federation registry (index-of-indexes) | No |
openlore spec-store status |
Report the health of the spec-store binding (read-only, advisory) | No |
openlore working-set context |
Assemble the working-set briefing for an active change across its targets (read-only, advisory) | Targets indexed |
openlore impact-certificate |
Certify what the current diff opens into declared covering surfaces, before it lands (advisory; opt-in blocking) | Yes |
openlore enforce |
Unified finding-enforcement gate: blocks on blocking, new debt under frozen, or an unverifiable frozen baseline (advisory by default) |
Decisions/specs present |
openlore plugin-manifest emit|validate |
Inspect/validate the OpenSpec plugin manifest (distinct from the federation manifest) |
No |
openlore mcp |
Start MCP server (stdio, for Cline / Claude Code) | No |
openlore serve |
Start a warm local HTTP daemon exposing tools (loopback, for Pi / editors) | No |
openlore doctor |
Check environment and configuration for common issues | No |
openlore features |
List opt-in features, which are active, and the one command/snippet to turn on each (--json, --inactive) |
No |
openlore update |
Upgrade openlore to the latest published version (detects npm-global / Homebrew / npx); --check reports availability, --dry-run prints the command without running it |
No |
openlore refresh-stories |
Refresh story files with latest structural context after each commit | No |
openlore blast-radius |
Pre-flight structural blast-radius briefing for the current diff (advisory; --install-hook for a pre-commit hook) |
No |
openlore coverage-gaps |
Ranked structural test-coverage gaps: important code with NO reaching test (the inverse of select_tests), no runtime. Scope to a diff (--base/--symbols) or region (--file-pattern). Read-only, never blocks |
Yes |
openlore certify-public-surface |
Certify the public API surface (no --base) or the breaking-change verdict for the working-tree diff (--base <ref>): removed/renamed exports, incompatible signatures, each breaking change with its in-repo consumers. Read-only, deterministic, never blocks |
Yes |
openlore style-fingerprint |
Descriptive per-language idiom profile (function form, binding, conditional, async, string, naming case) for the repo, a region (--community <id>), or a file (--file <path>); --language filters, --json for machine output. Evidence-floor + enforcement-aware nulls. Read-only, deterministic, never blocks |
Yes |
openlore briefing-since |
Catch-up briefing of what changed since a base ref (--base <ref>), ranked by significance tier — surprising-change (a stable hub moved) > hub-change > chokepoint-change > ordinary-change — from existing labels (not a score); grouped by region, with tests-to-run and a no-silent-truncation receipt. Scope with --file-pattern, bound with --max, --json for machine output. Read-only, deterministic, never blocks |
Yes |
openlore find-clones |
Existing clones of ONE query — a function --symbol <name> (or name::path) in the index, or raw --snippet <code> (even code not yet written) — ranked exact > structural > near. The edit-time "does this already exist? reuse it" companion to the whole-repo get_duplicate_report. --min <ratio> sets the near floor (default 0.7), --max <n> bounds the list, --json for machine output. Read-only, deterministic, never blocks |
Yes |
openlore error-propagation |
Exceptions escaping vs. handled for TS/JS/Python/Java/C#, or returned error values and panic/recover flow for Go. Go results are explicitly value-shaped. Unanalyzable callees and discarded Go results are disclosed as boundaries. --max-depth <n> bounds traversal; --json emits the structured model. |
Yes |
openlore env-impact |
What breaks if an env var (--name <var>) is removed or renamed: the line-precise read sites (file/line/enclosing function; module-level reads disclosed), the upstream callers that transitively reach a read (the blast radius), the tests to run, and per-site required (no fallback = hard break) — the configuration analogue of analyze_impact, for TS/JS/Python/Go/Ruby. Unknown var → not-found + candidates; config-object keys out of scope. --max-depth <n> bounds the backward traversal (default 12), --json for machine output. Read-only, deterministic, never blocks |
Yes |
openlore review |
Deterministic structural PR review (structural delta + blast radius) as a Markdown/JSON briefing; pairs with the bundled GitHub Action | No |
openlore preflight |
CI staleness gate: fail when the analysis graph is stale relative to the working tree | No |
openlore export scip |
Export the analysis graph as an SCIP index for the Sourcegraph / Glean ecosystem | No |
openlore export bundle |
Export the persisted graph index as a portable integrity-stamped artifact (--out <path>), optionally authenticated with Ed25519 (--sign-key <pkcs8.pem>). Deterministic, offline. See Shareable bundle |
No |
openlore import <artifact> |
Import a portable graph artifact with separate integrity, optional producer-authentication, and currency receipts; clean ancestor bundles catch up through a bounded exact delta, unsafe gaps rebuild, and invalid/untrusted signatures are rejected | No |
openlore telemetry |
Analyze EpistemicLease cognitive-load telemetry | No |
openlore panic-* |
Agent behavioral-governance ("panic") commands — panic-check/panic-level/panic-validate/panic-hotspots/panic-calibrate/panic-replay. Opt-in, off by default; install hooks via openlore setup --hooks |
No |
openlore gryph-watch |
Background Gryph behavioral observer (opt-in; install via openlore setup --hooks) |
No |
--api-base <url> # Custom LLM API base URL (proxy / self-hosted)
--insecure # Disable SSL certificate verification
--config <path> # Config file path (default: .openlore/config.json)
-q, --quiet # Errors only
-v, --verbose # Debug output
--no-color # Plain text output (enables timestamps)Generate-specific options:
--model <name> # Override LLM model (e.g. gpt-4o-mini, llama3.2)openlore orient [options]
--task <task> # Natural-language task (e.g. "add rate limiting")
--directory <path> # Project directory to orient in (default: cwd)
--limit <n> # Number of relevant functions to return (default: 5)
--token-budget <n> # Cap relevantFunctions to ~this many tokens
--lean # Return only the navigation core (drop heavier sections)
--json # Emit the full result as JSON instead of the human view
--metrics # Report wall time + output size to stderr (opt-in)
--inject # Emit a bounded, ignorable task-scoped orientation block
# for a pre-turn agent hook; reads the task from --task
# or stdin. Fail-open: any failure degrades to a single
# pointer line at exit 0 (see docs/install.md).With no --task, orient prints a session-start primer. Requires openlore analyze
to have run at least once.
openlore search <query> [options]
--specs # Search requirements instead of code
--limit <n> # Result cutoff (default: 10)
--language <name> # Code language filter
--min-fan-in <n> # Code minimum-caller filter
--domain <name> # Spec domain filter
--section <name> # Spec section filter
--token-budget <n> # Cap ordinary code-search output
--explain <target> # Explain one surfaced or missing target
--target-kind <kind> # symbol | file | requirement (required with --explain)
--file <path> # Disambiguate a symbol target
--directory <path> # Project directory (default: cwd)
--json # Emit the shared handler response unchanged--explain uses the same requested-limit candidate window as ordinary retrieval. It reports
not-indexed, unsupported language, filter exclusion, no lexical term match, an exact rank within
that window, or candidate-window truncation. Use --specs --target-kind requirement for canonical
requirement IDs. Presentation token caps are not diagnosed.
openlore install [options] # wire agents (this repo AND your user scope),
# build the index, wire the decision trail
--agent <name> # Limit to one surface: claude-code, cursor, cline,
# continue, agents-md
--preset <name> # MCP tool preset to wire: substrate (default; navigation core +
# spec workflows + recall + verify_claim + blast_radius), navigation (lean escape),
# minimal, memory, verify, federation, coordination, or full
--all-tools # Wire the full 76-tool surface (alias of --preset full)
--dry-run # Print planned changes without writing any files
--force # Overwrite OpenLore-managed blocks even if hand-edited
--uninstall # Remove OpenLore-managed blocks and entries (both scopes)
--repo-only # Wire this repository only; write no user-scope entries
--no-analyze # Configure surfaces only; skip init + analyzeopenlore connect [agent] [options] # wire ONE agent (no index build by default)
openlore connect remove [agent] # disconnect that agent
<agent> # Positional: claude-code | cursor | cline | continue |
# agents-md (omit for an interactive picker)
--preset <name> # MCP tool preset to wire (same names as install)
--all-tools # Wire the full 76-tool surface (alias of --preset full)
--dry-run # Print planned changes without writing any files
--force # Overwrite OpenLore-managed blocks even if hand-edited
--no-analyze # Configure surfaces only; do not build the indexconnect takes the agent as a positional argument (openlore connect cursor), not
--agent, and disconnects via the remove subcommand rather than --uninstall.
A bare openlore install wires the substrate surface (15 tools — navigation core + governance reads + spec-workflow composites) and, for
Claude Code, both a SessionStart primer hook and a UserPromptSubmit task-scoped
injection hook. Use --preset navigation for the lean navigate-only core (10 tools), or
--preset full for all 76 tools.
openlore mcp [options] # start the stdio MCP server
--preset <name> # Expose a named preset (default: substrate, 15 tools — nav + governance + spec workflows)
--minimal # Expose only the core 6 governance tools
--all-tools # Expose the full surface — all 76 tools (alias --preset full)
--list-tools # Print the active surface grouped by capability family and exit
--watch-auto # Auto-detect + incrementally re-index the project dir
--no-watch-auto # Disable auto-watch (use for one-shot tool calls)
--daemon # Delegate tool calls to a shared `openlore serve` daemonWhen the lean default is active, the server advertises the opt-in presets once via
the MCP initialize instructions channel (no extra tool schemas).
openlore drift [options]
--base <ref> # Git ref to compare against (default: auto-detect)
--files <paths> # Specific files to check (comma-separated)
--domains <list> # Only check specific domains
--use-llm # LLM semantic analysis
--json # JSON output
--fail-on <severity> # Exit non-zero threshold: error, warning, info
--max-files <n> # Max changed files to analyze (default: 100)
--verbose # Show detailed issue information
--suggest-tests # List test files covering drifted domains
--install-hook # Install pre-commit hook
--uninstall-hook # Remove pre-commit hookopenlore generate [options]
--model <name> # LLM model to use
--dry-run # Free plan: no provider call, cost, or writes
--plan # Alias for the free plan
--preview # Paid isolated generation preview and candidate diff
--domains <list> # Only generate specific domains
--merge # Merge with existing specs
--no-overwrite # Skip existing files
--adr # Also generate ADRs
--adr-only # Generate only ADRs
--force # Re-run all LLM stages, clear generation cache, remove stale domains
--analysis <path> # Path to existing analysis directory
--output-dir <path> # Override openspec output location
-y, --yes # Skip confirmation promptsopenlore run [options]
--force # Reinitialize even if config exists
--reanalyze # Force fresh analysis even if recent exists
--model <name> # LLM model to use for generation
--dry-run # Show what would be done without making changes
-y, --yes # Skip all confirmation prompts
--max-files <n> # Maximum files to analyze (default: 500)
--adr # Also generate Architecture Decision Recordsopenlore analyze [options]
--output <path> # Output directory (default: .openlore/analysis/)
--max-files <n> # Max files (default: 500)
--include <glob> # Additional include patterns
--exclude <glob> # Additional exclude patterns
--shard <name> # Recompute one detected/configured workspace package against the
# retained whole graph. Repeatable. Requires an existing index;
# without one, performs and discloses a full build.
--force # Re-analyze from scratch: analyze even when the source is unchanged,
# and re-extract every file instead of reusing the extraction cache.
# An ordinary `analyze` already re-parses only what changed.
--reanalyze # Analyze even when the source is unchanged, but keep reusing the
# extraction cache — the cheap half of --force.
--ai-configs # Generate AI tool config files (CLAUDE.md, .cursorrules, .clinerules/openlore.md,
# .github/copilot-instructions.md, .windsurf/rules.md, .vibe/skills/openlore.md)
# Safe to re-run — skips files that already exist, marks pre-existing ones.
--no-embed # Force a keyword-only (BM25) index (keyword is the first-class default;
# semantic is built only when a provider is configured — see "openlore embed")
--reindex-specs # Re-index OpenSpec specs into the vector index without re-running full analysis
# (uses the configured embedding provider — local or remote — else keyword)Workspace shards are detected from npm/yarn/pnpm workspaces, Cargo members, Go modules,
pyproject.toml, Gradle settings, and Maven modules. Files outside every package belong to the
implicit root shard. A scoped run replaces only the selected package and its cross-package
resolution frontier in the SQLite graph. Repo-wide JSON and search artifacts are retained, never
rewritten from partial input; workspace-shards.json records recomputed and retained shards,
per-shard freshness, frontier files, and explicit stale regions. --shard with --force is a
full rebuild. An unknown name fails with the available and nearest shard names.
analyze also carries anchored memory across refactors: if a symbol with anchored memories/decisions was
renamed or moved since the last analysis, its anchors are re-pointed to the new symbol (deterministically,
no LLM) and the run logs Memory continuity: carried N symbol(s) across rename/move. See
docs/mcp-tools.md ("Memory survives refactors").
openlore embed [options]
--local # Enable the on-device, no-API-key local embedder and rebuild the index.
# Lazily downloads + caches a small pinned model (~23 MB) under ~/.openlore/models.
# An explicit local provider wins over any EMBED_* env.
--off # Revert to the keyword (BM25) default and rebuild
--model <id> # Override the local embedding model (advanced; default is a pinned small model)Keyword (BM25) search is the first-class default and needs no setup. openlore embed --local is the one-command semantic upgrade; for a remote OpenAI-compatible endpoint instead, set EMBED_BASE_URL/EMBED_MODEL (or an embedding block in .openlore/config.json) and run openlore analyze. See docs/semantic-search.md.
openlore prove [options]
--directory <path> # Repo to measure (default: current directory)
--runs <n> # Runs per arm per task (default: 2; non-numeric is rejected)
--model <name> # Agent model (default: sonnet)
--max-budget-usd <n> # Per-agent-call USD ceiling (default: 0.5; non-numeric is rejected)
--estimate # Deterministic, no-agent, no-API projection of the orientation tax
--dry-run # Synthetic numbers (no agent, no API key)
--json # Stable schemaVersion:1 scorecard on stdout (mutually exclusive with --markdown)
--markdown # Paste-ready scorecard block + shields.io badge (mutually exclusive with --json)
--save # Persist a dated, non-clobbering scorecard under .openlore/prove/The measured arm needs claude + an API key; --estimate and --dry-run need neither. A measured
run whose agent calls all fail exits non-zero rather than emitting a verdict over no data. The
--json shape is documented in AGENT-BENCHMARKS.md.
openlore setup [options]
--tools <list> Comma-separated tools to install: vibe, cline, claude, opencode, gsd, bmad, omoa (default: interactive prompt)
--force Overwrite existing files (use after upgrading openlore)
--dir <path> Project root directory (default: current directory)Installs workflow skills from the openlore package into the project. Skills are static assets — identical across projects — so this command only needs to be run once at project onboarding and again after upgrading openlore.
Files installed:
| Tool | Destination | Content |
|---|---|---|
vibe |
.vibe/skills/openlore-{name}/SKILL.md |
10 skills |
cline |
.clinerules/workflows/openlore-{name}.md |
7 workflows |
claude |
.claude/skills/openlore-{name}/SKILL.md + decisions pre-commit hook |
10 skills + commit gate |
opencode |
.opencode/skills/openlore-{name}/SKILL.md + .opencode/plugins/agent-guard.ts |
10 skills + guard plugin |
gsd |
.claude/commands/gsd/openlore-{name}.md |
2 commands |
bmad |
_bmad/openlore/{agents,tasks}/ |
2 agents, 4 tasks |
omoa |
.opencode/plugins/ + .opencode/prompts/ |
4 SDD plugins + Sisyphus prompt (oh-my-openagent) |
The omoa option is auto-detected and pre-checked in the interactive prompt when oh-my-openagent is found in the project or user config.
Never overwrites existing files. Combine with analyze --ai-configs for a complete agent setup:
openlore analyze --ai-configs # project-specific context files
openlore setup # workflow skillsopenlore decisions [options]
--list # List decisions, optionally filtered by --status
--status <status> # Filter by status: draft, consolidated, verified, approved, synced, phantom
# Note: synced/rejected/phantom are purged from store after --sync
--approve <id> # Approve a decision by ID (blocked if already synced)
--reject <id> # Reject a decision by ID
--reason <text> # Rejection reason (used with --reject)
--sync # Write approved decisions into specs and ADRs, then purge inactive entries
--dry-run # Preview sync without writing files
--gate # Run commit gate check (reads pending.json, no LLM — used by pre-commit hook)
# Gate reason codes: verified | approved_not_synced |
# drafts_pending_consolidation | no_decisions_recorded
--consolidate # Manually trigger LLM consolidation + diff verification of drafts
--json # Machine-readable output
--uninstall-hook # Remove decisions pre-commit hook (install via: openlore setup --tools claude)
# Decision autopilot (opt-in: { "governance": { "autopilot": true } } in .openlore/config.json):
# the gate auto-accepts verified decisions (distinct `auto-approved` status), syncs them to
# specs with an "Auto-accepted (unreviewed)" marker, and never blocks a commit. Every status
# transition — in every mode — lands on an append-only ledger.
openlore decisions log [--json] [--since <ref|ISO date>]
# Show the transition ledger, newest first
openlore decisions review [--promote <ids|all>] [--reject <ids|all>] [--note <text>] [--json]
# List auto-accepted decisions awaiting review; promote (drops the
# unreviewed marker) or reject (retires from specs, kept in history)openlore verify [options]
--samples <n> # Files to verify (default: 5)
--threshold <0-1> # Minimum score to pass (default: 0.7)
--files <paths> # Specific files to verify
--domains <list> # Only verify specific domains
--verbose # Show detailed prediction vs actual comparison
--json # JSON outputopenlore doctor runs a self-diagnostic and surfaces actionable fixes when something is misconfigured or missing:
openlore doctor # Run all checks
openlore doctor --json # JSON output for scriptingChecks performed:
| Check | What it looks for |
|---|---|
| Node.js version | ≥ 22.19 required, and node:sqlite probed as loadable |
| Git repository | .git directory and git binary on PATH |
| openlore config | .openlore/config.json exists and is parseable |
| Analysis artifacts | repo-structure.json freshness (warns if >24h old) |
| OpenSpec directory | openspec/specs/ exists |
| LLM provider | API key or claude CLI detected |
| Disk space | Warns < 500 MB, fails < 200 MB |
Run openlore doctor whenever setup instructions aren't working — it tells you exactly what to fix and how.
OpenLore needs zero config for its core value — orient, search, blast-radius, and the whole
structural graph work with no keys set. Everything beyond that core is an independent opt-in feature.
openlore features is the single answer to "where do I turn on X?": it reads .openlore/config.json and
a few on-disk markers and reports, for every opt-in feature, whether it is active and the one command or
config snippet that activates it. Deterministic and local — no LLM, no network.
openlore features # Every feature, its state, and how to enable it
openlore features --inactive # Only what is not yet turned on
openlore features --json # Machine-readable inventory (for scripts / agents)Features reported (active/inactive detected from config + markers):
| Feature | Activate with |
|---|---|
| Semantic embeddings | openlore embed --local |
| Task-scoped context injection | on by default (contextInjection.mode) |
| MCP tool surface | openlore connect --preset <name> |
| Architecture invariants | .openlore/architecture.json (layer, boundary, cycle, reachability, orphan, and instability rules) |
| Change-impact certificate | impactCertificate.surfaces in config |
| Enforcement policy | enforcement.policy in config |
| Blast-radius blocking | blastRadius.block in config |
| Commit gate (pre-commit hook) | openlore enforce --install-hook |
| Agent behavioral governance (panic) | openlore setup --panic |
| Spec-store binding | specStore in config |
| Federation registry | openlore federation add <path> --name <name> |
openlore review composes the structural delta (structural_diff) and the blast radius
(computeBlastRadius — hubs, layers, tests to run, and the spec/memory/decision drift the change
introduces) for a base..head range into one deterministic, conclusion-shaped briefing — no LLM,
no new MCP tool. It is the same distinctive structural output OpenLore already produces, rendered for a
human reviewer and bundled as a GitHub Action that posts it as one sticky PR comment.
openlore review # markdown briefing for the current diff (auto-detected base)
openlore review --base main --head HEAD # explicit range
openlore review --format json # schemaVersion:2 briefing on stdout
openlore review --out review.md # write the markdown to a file (used by the Action)
openlore review --hook # gate orphan enforcement and invalid candidate config| Option | Description |
|---|---|
--base <ref> |
Base ref to compare against (default: auto-detected — requested → main → master → HEAD~1) |
--head <ref> |
Head ref (default: working tree). Blast radius is computed against the working tree; in CI the runner checks out the head SHA so they align (a caveat is printed when an explicit --head could differ) |
--format <fmt> |
markdown (default, for PR comments) or json (schemaVersion: 2, for programmatic consumers); unknown value exits 2 |
--out <path> |
Write the briefing to a file instead of stdout |
--hook |
Opt-in gating: exit non-zero on blocking, frozen-new, uninitialized, or unverifiable blast-radius orphan enforcement (legacy blastRadius.block lowers to the same policy), or an invalid candidate enforcement config. |
Advisory by default — it informs, it never fails the check. Degrades honestly: with no analysis index
it shows the structural delta and says "run openlore analyze"; a non-git directory or unreachable
base is disclosed rather than emitted as a misleading empty briefing. The structural delta works
without an index (it builds the old/new graphs from just the changed files).
Review is read-only: it never initializes or shrinks a frozen baseline. To adopt existing debt or
record progress, run openlore enforce locally, inspect the baseline change, then stage the config,
baseline, and generated ignore exception:
git add .gitignore
git add .openlore/config.json .openlore/enforcement-baseline.jsonlWhen a complete review sees resolved baseline identities, its receipt reports how many would retire
and directs you to run openlore enforce locally. An incomplete or unavailable blast-radius
assessment or an unverifiable baseline fails closed for orphan codes already mapped frozen.
An invalid candidate config gates independently; the candidate cannot disable a trusted frozen policy
by corrupting or removing config, and a deliberate downgrade must leave that code's baseline records unchanged.
GitHub Action. The repo ships .github/actions/openlore-review (composite action: build the
SHA-pinned Action source with its committed lockfile → openlore analyze → openlore review → one
sticky comment matched by a hidden <!-- openlore-review --> marker, created once and updated in
place — duplicate-proof via paginated comment lookup) and a copy-paste workflow
(.github/workflows/openlore-review.yml.example). Adoption is one file; it needs a full-history
checkout (fetch-depth: 0) and pull-requests: write permission. Dependencies are installed with
npm ci from the reviewed Action revision in an isolated npm configuration with lifecycle scripts
disabled; the Action does not resolve or execute a mutable openlore npm selector. Advisory by
default; gate mode fails the job when review finds a blocking orphan, new debt under frozen, an
uninitialized or unverifiable frozen baseline, or invalid candidate enforcement config (legacy
blastRadius.block lowers to the same policy). A missing openlore/review or an unreachable range
never produces a false-positive red check. A comment-post
failure never fails the check either: on a fork PR GitHub gives pull_request a read-only token, so
the comment can't be posted for external contributors — the Action warns and leaves the briefing in the
job log. Do not combine pull_request_target with a checkout of the pull-request head; use a split
unprivileged analysis workflow and trusted artifact-posting workflow if fork comments are required.
The briefing is always clamped to GitHub's 65,536-char comment limit.
Federation is an index-of-indexes: each repo keeps its own independently-built .openlore index, and a project-local registry (.openlore/federation.json) references them. Adding or removing a repo edits only the registry plus that repo's own build — never a global rebuild. No merged cross-repo graph is ever materialized; federated queries load only the per-repo indexes they need, on demand.
openlore federation add <path> [--name <name>] # Register a repo (default name: its basename)
openlore federation remove <nameOrPath> # alias: rm
openlore federation list # alias: ls — shows each repo's index stateIndex state per repo: ✓ indexed, ⚠ stale (re-run openlore analyze there), ∅ unindexed, or ✗ missing path. Once a repo is registered, the four cross-repo conclusion tools — analyze_impact, find_dead_code, select_tests, find_path — accept an opt-in federation (or federationRepos) flag and report which repos were consulted vs skipped. The registry-status tool federation_status is exposed only under openlore mcp --preset federation. See docs/federation.md.
A spec-store binding points OpenLore at an external spec repository (one that holds specs/changes) and declares the code repositories its plans targets and references. The declared names resolve against the federation registry above. Configure it in .openlore/config.json (see Configuration), then check its health:
openlore spec-store status # human-readable: per-target resolution + index state, references, store path
openlore spec-store status --json # stable finding codes for an orchestrator (see docs/mcp-tools.md)Read-only and advisory — it reports binding health and always exits 0; it never blocks. Findings carry stable codes (target-unresolved, index-stale, reference-missing, registry-unreadable, …) each with a pasteable remediation. The matching MCP tool spec_store_status is exposed under openlore mcp --preset federation.
Once a binding is sound, assemble the structural briefing an active change actually needs — orient, generalized from one repo to the change's targets:
openlore working-set context --change <id> # human-readable: per-target items, callers, anchored intent
openlore working-set context --change <id> --json # documented JSON for an orchestrator to splice into its agent brief
openlore working-set context --change <id> --token-budget 4000 # cap the merged briefingIt reads the change's proposal under the bound store, orients each resolved+indexed target on that intent, and returns one budgeted, per-target-attributed briefing plus fresh in-scope anchored intent (orphaned withheld, drifted flagged). Read-only and advisory — always exits 0, never blocks. Findings carry stable codes (change-not-found, target-not-briefable, no-briefable-targets, …). The matching MCP tool working_set_context is exposed under openlore mcp --preset federation.
Certify what the current diff touches — and, crucially, what it newly reaches — before it lands. You declare covering surfaces (semantic/governance boundaries) under impactCertificate.surfaces in .openlore/config.json; the certificate reports the paths the change opens into each surface (reachable after the diff but not before), plus blast radius, drifted specs, and tests to run:
openlore impact-certificate # human-readable certificate for the working tree vs HEAD
openlore impact-certificate --base main --json # documented JSON (stable surface + path codes) for an orchestrator
openlore impact-certificate --change <id> --save # record the change id + persist for later decay re-checks
openlore impact-certificate --install-hook # install the ADVISORY pre-commit hook (never blocks by default)
openlore impact-certificate --uninstall-hook # remove the pre-commit hook block (coexists with other openlore hooks)Advisory by default — it emits the certificate and exits 0; an infrastructure failure (no index, not a repo) never blocks. A repository MAY opt into blocking specific surface severities with impactCertificate.block: ["critical"], in which case the --hook exits non-zero only when the diff opens a new path into a surface of that severity. Newly-opened-path detection is differential and deterministic (no LLM): only the changed files are re-parsed, renamed files read their base-ref content, untracked files are folded in, and an ambiguous added callee is reported, never guessed. The certificate decays via the freshness lease — when an anchored symbol later moves, openlore spec-store status re-fires it as a certificate-stale finding. The matching MCP tool change_impact_certificate is exposed under openlore mcp --preset federation.
Report important code with no reaching test, ranked by hub/chokepoint significance — the structural inverse of select_tests over the whole graph, with no test run, no coverage tool, and no runtime. It is gaps-only and honest: it reports "no reaching test" and never claims a symbol is "tested":
openlore coverage-gaps # whole repo, ranked (default 100, capped 500)
openlore coverage-gaps --max 50 # cap the list
openlore coverage-gaps --file-pattern src/core/auth # region scope
openlore coverage-gaps --base main # diff scope: gaps among symbols changed vs main
openlore coverage-gaps --symbols parseConfig,login # diff scope: only these changed symbols
openlore coverage-gaps --json # machine-readable (stable shape) for CI / an orchestratorA gap with no caller at all is labeled also-dead (distinct from find_dead_code); an untested entry point is untested-not-dead. A scope that resolves to nothing returns an explicit note ("nothing matched", not a reassuring "0 gaps"), and the counts (analyzedSymbols / reachableFromTest) range over the in-scope set. Read-only and advisory — it is a report and never blocks. The matching MCP tool report_coverage_gaps is exposed under openlore mcp --preset full. See coverage-gaps.md.
Answer the edit-time question "does a near-duplicate of this already exist that I should reuse?" — scoped to one query, where get_duplicate_report is the whole-repo audit. Reuses the same detector (exact / structural / near), one-vs-all so it finds near-clones even on repos large enough that the whole-repo near pass is skipped:
openlore find-clones --symbol getPyParser # clones of an indexed function
openlore find-clones --symbol 'getPyParser::src/core/analyzer/call-graph.ts' # disambiguate by path
openlore find-clones --snippet "$(cat candidate.ts)" # clones of code you're about to write
openlore find-clones --symbol handleFoo --min 0.6 --max 5 # lower the near floor, cap the list
openlore find-clones --symbol handleFoo --json # machine-readable (stable shape)Honest by construction: an unknown symbol is an explicit not-found (with candidates), never an empty "unique"; an ambiguous bare name lists name::path candidates; a query below the evidence floor reports "too small to compare", not "no clones"; the query never matches itself. The matching MCP tool find_clones is exposed under openlore mcp --preset full.
Answer “what error can leave this function, and is any already handled?” The error-handling analogue of analyze_impact supports exception flow for TypeScript, JavaScript, Python, Java, and C#, plus Go's returned-error and panic/recover model:
openlore error-propagation --symbol handleRequest # exceptions that escape this function
openlore error-propagation --symbol 'handleRequest::src/api/handler.ts' # disambiguate by path
openlore error-propagation --symbol parseConfig --max-depth 5 # bound the callee traversal (default 10)
openlore error-propagation --symbol parseConfig --json # machine-readable (stable shape)For exception-shaped languages, escapes lists each escaping type, origin, call path, and kind (direct, Java declared, or propagated); handledInternally lists exceptions caught before reaching callers. Java/C# typed handlers are exact-name lower bounds; a definitely abrupt finally suppresses try-body escapes, while uncertain finally/resource-cleanup effects are disclosed. Go results instead carry errorModel: go-value: entries use value with returned_error, propagated_error, or panic, while handledInternally contains proven checked_error and recovered_panic sites. Lexically scoped and named error results are tracked. Discarded or ambiguous results, complex deferred unwind ordering, malformed or stale source, unresolved callees, and traversal limits appear in boundaries, never as a guessed clean result.
All models are honest sound lower bounds. Exception containment is byte-precise, unknown thrown types remain <dynamic>, and unsupported languages return an explicit unsupported result. The analysis is computed live from the cached call graph plus confined source re-reads; it creates no persisted artifact. The matching MCP tool analyze_error_propagation is exposed under openlore mcp --preset full.
openlore env-impact --name DATABASE_URL # what breaks if this env var is removed
openlore env-impact --name PORT --max-depth 5 # bound the backward (caller) traversal (default 12)
openlore env-impact --name SECRET_KEY --json # machine-readable (stable shape)readSites locates each read of the env var to a file/line and its enclosing function (a read outside any function is reported module-level — it runs at import time, so its effective blast radius is every importer, disclosed in boundaries); each site carries required (true when no site-local fallback ??/|| or strict subscript means removing the var is a hard break there). affectedFunctions is the upstream callers that transitively reach a read (the blast radius, with distance); reachingTests is the tests to run. Honest by construction — a sound lower bound: an unknown var returns an explicit not-found with candidates (never an empty "unused"); config-object key reads (config.x.y) are an explicit out-of-scope boundary, never guessed; the call graph's resolution limits (dynamic dispatch, reflection) are disclosed; and a stale index (read-site lines come from the current source but map to cached function spans) is disclosed via a staleness marker + boundary, never presented as clean. Scope is env-var reads in TS/JS/Python/Go/Ruby (exactly what the env extractor scans); per-site required reflects the actual fallback (TS ??/||, Python/Ruby defaultless get/getenv/fetch vs. a positional or block default). Computed live from the cached graph plus a re-read of the var's files — no new persisted artifact. The matching MCP tool analyze_env_impact is exposed under openlore mcp --preset full; it is the conclusion companion to the get_env_vars inventory.
Certify whether the working-tree diff breaks the package's exported contract. With no --base it prints the public surface (exported symbols + signatures); with --base <ref> it prints a deterministic breaking-change verdict — each changed export classified breaking / non-breaking / potentially-breaking, and each breaking one paired with the in-repo consumers it breaks:
openlore certify-public-surface # print the public surface (exported symbols + signatures)
openlore certify-public-surface --base main # breaking-change verdict for the working tree vs main
openlore certify-public-surface --max 50 # cap the surface listing in surface mode (default 200, cap 500)
openlore certify-public-surface --base HEAD --json # machine-readable verdict (stable shape) for CI / an orchestratorThe closed classification rules: a removed or renamed export, an added required parameter, a parameter made required, or a narrowed parameter/return type is breaking; an added trailing optional parameter, a new export, or a widened return type is non-breaking; anything that cannot be proven compatible from the available signatures (untyped/dynamically-typed, or an incomparable type change) is potentially-breaking — never silently safe, since there is no type checker and no build in the loop. A renamed export is reported as a rename (via symbol-identity continuity), not a remove+add. A symbol still defined but no longer exported is reported as visibility-reduced (public → private); a re-export (export { X } from …) follows its definition — it is tracked at the definition site, not separately at the barrel, so a barrel'd symbol is never double-counted. External/unindexed consumers are disclosed as a known-unknowable boundary, not implied absent. Signature classification covers TypeScript/JavaScript/Python; other languages fail-soft to surface membership only (a removed/added export is still reported, without a signature classification). Test files are excluded from the surface. Read-only and advisory — it is a report and never blocks. The matching MCP tool certify_public_surface is exposed under openlore mcp --preset full.
openlore briefing-since # what changed since the auto base (main → master → HEAD~1)
openlore briefing-since --base HEAD~20 # brief everything that changed in the last 20 commits
openlore briefing-since --file-pattern src/core/auth # region scope
openlore briefing-since --max 25 # bound the briefing (default 50, capped 200)
openlore briefing-since --base main --json # machine-readable (stable shape) for CI / an orchestratorbriefing-since answers the reviewer / catch-up / onboarding question the other change tools don't — not "what does my pending diff do?" but "a lot changed since I last looked; which of it structurally matters?" Each changed production symbol is labeled with exactly one tier, ordered highest-first: surprising-change (a high-fan-in hub whose file rarely changed before — a normally-stable, widely-depended-on symbol that suddenly moved) > hub-change (a broad high-fan-in/high-fan-out hub) > chokepoint-change (a high-fan-in funnel) > ordinary-change. The tiers come entirely from existing classifiers (landmark-signals hub/orchestrator/chokepoint + the volatilityLevel churn classifier) plus raw evidence (fan-in, fan-out, prior churn) — there is no weighted significance score and no new tuning constant; the caller makes the final judgment from the evidence. Honest by construction: changed symbols are at file granularity (every function in a changed file is briefed, disclosed in a caveat); the surprising-change label is withheld when git history is too shallow (< 2 non-bulk commits) to say "rarely changed before"; a bounded briefing always carries a truncation receipt (omitted count + per-tier breakdown + lowest tier reached) and never drops a higher tier for a lower one; a silent base-ref fallback is disclosed — a --base that git cannot resolve (a typo) is reported (baseRefFallback) and you are told which base was actually used, rather than briefing against main unannounced; because per-file churn is matched by exact path (git history does not follow renames), a just-renamed file that reads as low-churn carries a caveat so its surprising-change label isn't trusted blindly; and the scope is hand-authored source code — infrastructure (IaC) resources and generated/vendored files are excluded (the same candidate set coverage-gaps ranks), so the briefing stays about the code whose call-graph significance the tiers actually measure. The cursor is the base ref, never wall-clock time. It also includes the tests to run for the whole change set (via select_tests), grouped by region/community. Deterministic and offline. The matching MCP tool briefing_since is exposed under openlore mcp --preset full.
openlore enforce is the unified finding-enforcement gate. It collects governance findings from every in-scope source, resolves each finding's enforcement class through the single declared enforcement.policy (with the legacy blastRadius.block / impactCertificate.block sugar lowered onto it), and — in --hook mode — fails the commit on an ordinary blocking finding, a new finding under frozen, an invalid baseline, or an unstaged ratchet update:
openlore enforce # human-readable gate report for the working tree (advisory)
openlore enforce --json # schemaVersion:3 JSON: gated, blocking[], new[], frozen[], advisory[], off[], ratchet{}, caveats[]
openlore enforce --hook # hook mode: exit 1 on policy or frozen-baseline gate failures
openlore enforce --agent-hook --git-root # agent-loop mode from any repo subdirectory; exit 2 only for configured blocking findings
openlore enforce --install-hook # install the unified pre-commit hook (coexists with the decisions gate)
openlore enforce --uninstall-hookSources: the stale-decision-reference check always runs (a cheap walk of the decision graph + anchored references — it flags a live, authoritative artifact that still cites a superseded decision); the blast-radius orphan patterns and impact-certificate surfaces are collected only when the repository has configured them (those analyses are diff-heavy). Only successfully assessed sources may initialize or shrink a frozen baseline. Advisory by default: a repository that declares no enforcement.policy never blocks, and an off-classed finding is still listed (silenced, not invisible). To adopt brownfield debt, map a code to frozen, run openlore enforce outside hook mode, review the resulting config and baseline, then stage .gitignore plus both .openlore/config.json and .openlore/enforcement-baseline.jsonl. Hook and PR-review modes refuse to initialize a missing baseline; PR review is read-only and never shrinks one. Deterministic, no LLM.
For opt-in Stop hooks, run openlore setup --agent-enforcement-hook claude, codex, or all. The installer merges one idempotent entry into .claude/settings.json and/or .codex/hooks.json; openlore setup --agent-enforcement-hook none removes only those OpenLore entries. Installed commands use --git-root, so enforcement still reads the repository-level configuration when an agent session starts in a subdirectory. Hook-settings updates coordinate through owner-only runtime state under ~/.openlore/runtime/settings-writes/; repository settings remain ownership-checked and are never overwritten after a concurrent change. Codex requires its normal project-hook trust review before a new hook runs. In agent-hook mode, remediation is printed before the finding conclusion when the code declares a static template. frozen stays advisory in the agent loop, and configuration or source failures emit caveats and exit 0.
The version 3 JSON envelope adds disjoint new[] and frozen[] ratchet partitions plus ratchet metadata while retaining blocking[] as the complete list of finding-driven gate causes. Non-finding failures, such as baseline integrity, configuration, or staging failures, are reported in ratchet and caveats. It keeps the closed finding-severity vocabulary info | warning | error | critical. Version 2 consumers can continue reading the existing partitions; version 1 consumers that matched the legacy value warn must migrate to warning.
openlore plugin-manifest inspects and validates the OpenSpec plugin manifest
OpenLore publishes — the "openspec" key in its package.json — so the OpenSpec
marketplace can discover, surface, gate, and invoke OpenLore as a subprocess
without importing its code. It is distinct from openlore manifest (the
federation .well-known/openlore.json); the names never collide.
openlore plugin-manifest emit # human-readable summary
openlore plugin-manifest emit --json # the manifest as JSON, stdout only
openlore plugin-manifest validate # schema + semantic check of OpenLore's own manifest
openlore plugin-manifest validate <dir> # validate another package's manifestvalidate exits 0 when the manifest is valid, 1 on a schema/semantic failure
(missing required field, no bin/binArgs, non-token namespace, a traversing
skill dir/source), and 2 when no manifest is found. The manifest declares the
namespace (lore), the executable (bin: openlore), openspecCompat (kept
coherent with the @fission-ai/openspec peer-dep range), the help-only surfaced
commands, the contributed skill, and ownsConfigKeys: ["openlore"]. See
OPENSPEC-INTEGRATION.md for the full marketplace contract.
Node-version guard. OpenLore requires Node ≥22.19 (the supported dependency floor where the built-in
node:sqliteis available without runtime flags). The CLI checks this before any command runs; under an older Node it prints one stderr line naming the required and actual versions and exits with the stable code 78 — never a stack trace — so a host delegating to OpenLore (e.g.openspec lore generateon Node 20) surfaces a clean, legible failure.
openlore serve runs a long-lived loopback HTTP daemon that keeps openlore's
caches warm across calls and, with --watch (default), keeps the analysis
continuously fresh — signatures/vector live, plus a debounced full call-graph
re-analyze after each edit burst. It exposes the same tools as the MCP server
over plain HTTP so non-MCP clients (e.g. the Pi extension in
examples/pi/) can hit them with fetch — no JSON-RPC, no subprocess-per-call.
openlore serve # substrate preset, ephemeral port, watch on
openlore serve --preset all --port 7077 # all 76 tools on a fixed port
openlore serve --no-watch # transport only, no freshness lane
openlore serve --stop # stop the daemon serving this directory| Option | Description |
|---|---|
-d, --directory <path> |
Project root to serve; discovery file written here (default: cwd) |
-p, --port <number> |
Port to bind (default: ephemeral free port) |
--host <host> |
Host to bind (default: 127.0.0.1) |
--preset <name> |
Callable surface enforced at dispatch and reported by /health: substrate (default), navigation (the lean navigate-only escape), minimal, or all/full (the full surface — full matches the openlore mcp selector name). Requests for registered tools outside the active preset return 403 before validation or side effects |
--token <token> |
Require this token as the x-openlore-token header (default: $OPENLORE_SERVE_TOKEN) |
--no-watch |
Disable the freshness watcher + re-analyze lane |
--stop |
Stop a running daemon for --directory and exit |
Endpoints: GET /health, authenticated POST /shutdown, and POST /tool/:name
with body { "directory": "...", "args": { ... } }. Discovery: the daemon writes
.openlore/serve.json { port, pid, host, token? } (removed on clean shutdown).
/health reports presetDispatchEnforced, root, pid, preset, tools,
tokenProtected, and tokenAuthenticated. Clients MUST require the semantic marker,
exact root, and authenticated token proof before trusting the descriptor or callable
surface. --stop uses the authenticated shutdown endpoint; it never signals the
descriptor PID.
If a shared-daemon connection fails after a non-idempotent write is sent, OpenLore
reports an unknown outcome instead of replaying the write locally.
PORT=$(jq .port .openlore/serve.json)
curl 127.0.0.1:$PORT/health
curl -XPOST 127.0.0.1:$PORT/tool/orient -d '{"args":{"task":"add rate limiting"}}'openlore install --agent pi (or openlore connect pi) installs the Pi extension
to .pi/extensions/openlore.js — a re-export shim pointing into the openlore
package, at parity with every other agent surface. Two
equivalent routes: pi install npm:openlore (Pi's own package route, via the "pi"
field in openlore's package.json) and openlore setup --tools pi --global
(~/.pi/agent/extensions/, every project). It auto-starts and talks to the
serve daemon, injecting structural context and exposing Pi's curated tool surface.
Pi starts a full-preset backing daemon and curates the model-visible tools itself;
if an existing narrow daemon owns the repository, stop it before starting Pi.
Its openlore_prepare_spec_generation and openlore_prepare_spec_repair tools
compose the same deterministic daemon evidence used by MCP hosts; Pi writes or
reconciles the specification itself and OpenLore makes no internal LLM call.
See examples/pi/README.md.
| Script | Description |
|---|---|
npm run bench |
EdgeStore micro-benchmark (node lookup, BFS, orient path) — requires openlore analyze |
npm run bench:mcp |
MCP handler benchmark — measures cold vs warm path for readCachedContext, handleOrient, handleSearchCode. Requires openlore analyze. Pass a project dir: npm run bench:mcp -- /path/to/project |