Generated banner for OpenLore
Generated logo for OpenLore

OpenLore

Memory server

Created by Clay Good

Published in clay-good/OpenLore. Publisher page

362 GitHub stars

What it is

Persistent architectural memory and structural cognition for AI coding agents.
— the description by Clay Good

AutoGineer overview, from the brain’s own files

OpenLore is a memory system designed to solve the amnesia and lack of governance in AI coding agents. By performing a one-time static analysis of a repository, it builds a persistent knowledge graph that encompasses application code, infrastructure-as-code, and architectural decisions. Unlike standard RAG systems, OpenLore operates deterministically without an LLM in the hot path, ensuring consistent answers and providing agents with an 'Epistemic Lease' to warn them when facts have become stale. It serves as both a memory layer for agent orientation and a governance engine that certifies changes, flags breaking API surface modifications, and validates sensitive boundary crossings before they are committed.

How it works

Memory model: Hybrid

AutoGineer overview, from the brain’s own files

OpenLore functions as an MCP server that indexes your repository into a local knowledge graph. Upon installation, it maps code structures, dependencies, and architectural specs. During agent sessions, it injects a compact architecture digest into the agent's context. The agent then uses MCP tools to query this graph for tasks like orienting to a codebase, tracing execution paths, or verifying claims. Governance tools analyze diffs against the graph to provide deterministic verdicts on breaking changes or boundary violations without requiring an LLM.

Folders
FolderRoleFiles
.claudeagent-config1
.githubagent-config12
docsdocs62
examplesexamples95
scriptsscripts27
skillsagent-instructions25
srcsource-code1284
benchfolder15
benchmarksfolder1
openspecfolder1261
packagingfolder2
schemasfolder2
stubsfolder1

The agent reads

The agent writes

Requirements

Runtimes: Node.js ^22.22.0 || >=23.5.0

Databases: LanceDB

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Google Antigravity, Kiro, OpenCode, Amp, Roo Code, Cline, JetBrains Junie, Devin (ex-Windsurf)

Set it up

  1. This brain runs code automatically: 1 script. Review package.json before opening the folder in Claude Code. No step below turns a hook on for you.

    • package.json: node scripts/postinstall.mjs
  2. Not scanned by AutoGineerAutoGineer could not scan everything it runs. https://www.npmjs.com/package/openlore
  3. Created by Clay Good. Published in clay-good/OpenLore. Licence: MIT License.

Usage

AutoGineer overview, from the brain’s own files

  • Orienting an agent to a new codebase at the start of a task.
  • Validating that a proposed code change does not break public API contracts.
  • Checking if a diff introduces a path into a sensitive architectural boundary.
  • Verifying claims about code safety or dead code before refactoring.
  • Maintaining architectural specifications that stay synchronized with code changes.
  • Selecting relevant tests to run based on the specific impact of a code modification.
“Orient me to the user authentication flow in this repository.”
The agent calls orient() and receives a summary of relevant functions, callers, and specs, allowing it to start the task immediately without manual file exploration.
“Review my current changes for breaking API surface modifications.”
The agent uses certify_public_surface to identify changed exports and lists the specific consumers that would be affected by the proposed changes.
“Verify if the function 'deleteUser' is safe to remove.”
The agent uses find_dead_code or verify_claim to determine if the function has any remaining callers or dependencies, providing a deterministic verdict.
“Check if this diff opens a path into the sensitive 'payment-gateway' module.”
The agent uses change_impact_certificate to confirm whether the new code creates a reachable path into the protected boundary.

Documentation by Clay Good

<h1 align="center">OpenLore</h1>

<p align="center"> <strong>Deterministic, local-first memory and guardrails for AI coding agents — no LLM in the hot path.</strong><br> Install once. From then on it runs in the background, keeping your agent oriented and its changes safe.<br> No API key. Same answer every time. </p>

<p align="center"> <a href="https://www.npmjs.com/package/openlore"><img src="https://img.shields.io/npm/v/openlore?color=2563eb&label=npm&logo=npm&logoColor=white" alt="npm version"></a> <a href="https://github.com/clay-good/OpenLore/actions/workflows/ci.yml"><img src="https://github.com/clay-good/OpenLore/actions/workflows/ci.yml/badge.svg" alt="CI status"></a> <a href="LICENSE"><img src="https://img.shields.io/npm/l/openlore?color=22c55e" alt="MIT License"></a> <img src="https://img.shields.io/node/v/openlore?color=339933&logo=node.js&logoColor=white" alt="Node >=22.22"> <br> <img src="https://img.shields.io/badge/MCP-ready-7c3aed?logo=anthropic&logoColor=white" alt="MCP ready"> <img src="https://img.shields.io/badge/languages-21%20%2B%2013%20IaC-f97316" alt="21 languages + 13 IaC ecosystems"> <img src="https://img.shields.io/badge/tests-5500%2B-success" alt="5500+ tests"> <img src="https://img.shields.io/badge/API_key-not_required-0ea5e9" alt="No API key required"> <a href="https://github.com/clay-good/OpenLore/stargazers"><img src="https://img.shields.io/github/stars/clay-good/OpenLore?style=social" alt="GitHub stars"></a> </p>

<p align="center"> <code>install</code> wires your agent and indexes the repo → <code>orient</code> returns the code a task touches → <code>review</code> catches the change that breaks a contract. All local, no API key. </p>

<p align="center"> <strong><a href="#install-once-then-forget-it">Install</a> · <a href="#what-you-get">What you get</a> · <a href="#value-scorecard--does-it-pay-for-itself">Benchmarks</a> · <a href="#openlore-vs-alternatives">vs. Alternatives</a> · <a href="#documentation">Docs</a></strong> </p>


AI coding agents are amnesiac and ungoverned. Every task restarts by re-reading the same files, long sessions drift onto stale assumptions, and nothing warns the agent when a change is about to break a contract or cross a boundary.

OpenLore fixes both halves, and then gets out of your way. A one-time static analysis builds a live knowledge graph of your repo — call structure, types, tests, decisions, IaC. From then on your agent is already oriented at the start of every task and its changes are certified before they land — automatically, deterministically, with no LLM in the hot path, so the same question returns the same grounded answer and the agent is told when a fact goes stale instead of served a confident guess.

Install once, then forget it

npm install -g openlore && openlore install

That is the only command most people ever run. It auto-detects your agent (Claude Code, Cursor, Cline, Continue, Pi, AGENTS.md), wires it, registers the MCP server, and builds the index — no API key, no config, no prompts. After that, it's on in the background and you don't think about it:

  • 🔄 Every task starts oriented, automatically — a compact architecture digest is injected into your agent's context at the start of each session, and your agent calls orient() and the other tools itself over MCP. You don't type anything.

  • 🌱 Every repo, not just this one — install wires your user account too, so each new git repo you open indexes itself in the background on first touch. "Install once" is literal.

  • 🩹 Always fresh — a file watcher re-indexes as you edit and converges to a full analysis; anything it can't recompute in time is marked explicitly stale, never silently wrong.

  • 🛡️ Changes get checked before they land — the governance tools (and openlore review on a PR) run deterministically with no API key.

The CLI below exists for humans and CI — prove, review, doctor, features. The everyday path is: install, then let your agent use it.

Zero config. Run openlore features to see every opt-in capability (embeddings, the commit gate, the spec store…), whether it's on, and the one command to enable it. openlore doctor verifies the wiring.


What you get

Two things, both deterministic and local:

🧠 Memory — your agent starts every task already oriented

  • Orient in one call — orient(task) returns the relevant functions, callers, specs, tests, and insertion points (~430µs p50 on a 15k-node graph) instead of a dozen exploratory file reads.

  • Survives refactors — anchored notes and decisions carry forward when a symbol is renamed or moved, instead of orphaning.

  • One graph for everything — application code, Infrastructure-as-Code, and architectural decisions share one graph, so a single query spans all three.

  • Told when a fact is stale — every response carries a brief, factual freshness note (the Epistemic Lease) once a cached fact has aged or the repo moved, so a long session can't drift onto a confident guess.

🛡️ Governance — guardrails on what the agent changes

  • Breaking-change verdict — certify_public_surface classifies every changed export breaking / non-breaking / potentially-breaking and names the consumers each break hits. Never silently "safe."

  • Sensitive-boundary check — change_impact_certificate flags when a diff opens a new path into a boundary you declared (reachable after the change, not before).

  • Grounded claims — verify_claim returns confirmed / refuted / unverifiable with a citation, before an agent asserts "X is dead" or "Y is safe to change."

  • One commit gate — openlore enforce blocks on findings you mark blocking. Advisory by default, no API key.

The one line to remember: an agent's expensive failure mode isn't ignorance — it's confidence. A model that doesn't know a function exists will go look. A model that "knows" a stale fact builds on it, and you pay at review time. OpenLore is built so the agent can be told "that fact is stale" and "this change opens a path you said was sensitive" — deterministically, with no second model guessing about the first.


Value Scorecard — does it pay for itself?

OpenLore only earns its place if an agent with it reaches a correct answer for less total cost than the same agent without it. We measure that and publish it — wins and losses. Numbers from the Spec 14 benchmark (claude -p, sonnet, N=4 medians, pinned SHAs), measured 2026-06-01.

ScenarioCost ΔRound-trips ΔVerdict
Large/unfamiliar repo · deep "how does X flow through Y" (its target)−7% to −21%−26%✅ helps — and the win grows with repo size
Small/familiar repo · shallow "who calls X"task-dependent (Round 1: +43%)+38%❌ often adds overhead — measure first

The win scales with codebase size (round-trips without → with):

Repo (size)Cost ΔRound-trips
excalidraw (~640 files)−21%25 → 16
tokio (~790 files)−21%17 → 13
okhttp−13%13 → 11
django (~3k files)−7%21 → 15
gin (110 files, smallest)+4% (≈even)10 → 9

Re-confirmed live 2026-06-03: the deep-task win reproduces (okhttp −13%); the small/familiar case is task-dependent, not a flat loss (chalk −32% win vs. express +59% loss). Don't guess from our repos — run openlore prove --estimate on yours (seconds, no API key).

Honesty contract. We never publish a savings number the benchmark didn't produce, we always show the losses next to the wins, and every claim traces to a command you can run. Methodology: docs/AGENT-BENCHMARKS.md.


Is OpenLore for you?

✅ Strong fitA codebase too big to hold in your head — and the model's. Private or niche code the model never memorized. Long sessions where stale assumptions compound. Polyglot repos, or code plus the IaC that deploys it.
🤔 Try it, measureMid-size repos and mixed workloads. Run openlore prove --estimate (seconds, no key) before you commit.
❌ Probably notA small repo the model already knows, answering shallow questions — your agent's built-in search is cheaper, and we publish the measurement that says so. Also: OpenLore locates and certifies, it never edits your code.

What your agent reaches for (you rarely type these)

Once wired, your agent calls these over MCP on its own. The default surface is the substrate preset — 15 tools: the navigation core, the highest-value governance reads (recall, verify_claim, blast_radius), and the spec composites. The lean navigation preset (10 tools) and the full 83 tools (--preset full) are one flag away.

When the agent needs to…Tool
Start any taskorient(task) — functions, callers, specs, insertion points in one call
Find which file/function handles Xsearch_code
Know the blast radius of a changeanalyze_impact — risk + up/downstream chain + governing decisions
Trace how request X reaches function Ytrace_execution_path
Pick the tests a change should runselect_tests — backward reachability to the reaching tests
Find dead code / what dies if X is deletedfind_dead_code — cross-language reachability
Check a whole diff before commitblast_radius — callers, tests, specs that drift
Tell if a diff opens a sensitive boundarychange_impact_certificate
Tell if a change breaks a consumer's APIcertify_public_surface — verdict, consumers named
Verify a fact before asserting itverify_claim — deterministic verdict + citation
Record an architectural choicerecord_decision before writing the code

Full reference — all 83 tools: docs/mcp-tools.md. Ships as a Claude Code Skill at skills/openlore-orient/.


OpenLore vs. alternatives

Everyone in this category answers the same first question: how does the agent see the codebase without reading it file by file? LSP toolkits answer with symbols, graph MCP servers with a parsed graph, search platforms with an index. OpenLore answers it too — then keeps going into the second question almost nobody answers: what happens when the agent starts writing? A retrieval layer makes an agent informed; it doesn't make it safe. That second half — governance on the same graph, no LLM in the loop — is what OpenLore is for.

Agent built-insLSP toolkitsGraph MCPSearch platformsOpenLore
Structural context instead of file reads❌ grep✓ symbols✓ graph✓ index✓ call graph + IaC + decisions on one graph
Local, no API key, deterministicPartial✓✓❌ hosted✓ no LLM in the hot path
Cross-session memory anchored to code❌Partial✓ notes❌✓ carried across renames
Told when a cached fact goes stale❌❌❌❌✓ Epistemic Lease
Breaking-change verdict over a diff❌❌Partial❌✓ per export, consumers named
"Did this diff open a sensitive path?"❌❌❌❌✓ differential, pre-commit
Cost effect published with the losses❌❌❌❌✓ −26% round-trips on deep tasks

Use the other tool when it's the right one: symbol-level edits (rename across files) are an LSP toolkit's home turf — OpenLore is deliberately read-only, and the two compose. Org-wide search across hundreds of repos is a search platform. Just-fast-graph-retrieval is a smaller graph MCP server. And a small, familiar repo with shallow questions is cheaper with your agent's built-in search. OpenLore also exports SCIP, so it sits alongside these tools, not against them.

Comparisons reflect each project's publicly documented capabilities as of July 2026; a correction PR is always welcome.


What it costs

Measured on a fresh clone of ripgrep with published [email protected]:

What it costsOn ripgrep (232 files)
One-time index build13.6 s, entirely local — no API key, no network
Disk27 MB under .openlore/ (gitignorable; rebuildable from source)
Per-query latency~430 µs p50 in-process via the MCP server
Your source codenever leaves the machine — no account, no telemetry (opt-in only)
Lock-innone — delete .openlore/ and nothing about your repo has changed

Large monorepos take minutes rather than seconds, stated plainly in Known limitations. Platform: Linux and Windows are exercised in CI; macOS is supported.

Fully local — nothing phones home. No account, no network call; your source, the index, and every query stay on your machine. The only diagnostics that exist are opt-in and local-only, off by default and never transmitted: OPENLORE_TELEMETRY=1 records tool calls, agent identity, latency, error messages, decision titles, and lease events to the gitignored .openlore/telemetry/ directory, and OPENLORE_LLM_LOGS=1 stores prompts and responses (after secret redaction) in the gitignored .openlore/logs/ directory. → docs/configuration.md


Languages, Infrastructure-as-Code & contracts

Languages: TypeScript · JavaScript · Python · Go · Rust · Ruby · Java · C++ · Swift · C# · Kotlin · PHP · C · Scala · Dart · Lua · Elixir · Bash — call graphs ride the same primitives for every language. → docs/language-support.md

Infrastructure-as-Code: Terraform/HCL · Kubernetes · Helm · CloudFormation · Ansible · Pulumi · AWS CDK · CDKTF · Dockerfile · Docker Compose · GitHub Actions · Azure Bicep · Serverless Framework — IaC resources project onto the same graph as application code, so orient, search_code, and analyze_impact answer "what's the blast radius of changing this security group / IAM role / base image / CI job?" with zero new tooling. → docs/iac.md

Interface contracts: OpenAPI 3.x · Swagger 2.0 — each declared operation projects onto the same graph, bound to the route handler that serves it and the function that calls it, so orient and analyze_impact cross the API boundary. Matching is literal, and everything unbound is disclosed. → docs/contracts.md


Known limitations

We'd rather you know these up front.

  • Static analysis only. Polymorphic dispatch, event channels, and route→handler bindings are recovered (provenance-labeled synthesized). What isn't: reflective invocation with a non-literal target (getattr(o, name)()), eval, and DI/plugin registries with no visible binding — silently absent from the graph.

  • LLM spec quality varies. Generated spec prose reflects the model's understanding — review complex business logic. Structure, format, coverage, and drift are checked deterministically.

  • Keyword (BM25) search answers first; local semantic follows in the background. orient/search_code work immediately with no key. Background builds then add on-device vectors (CPU-only, ~23 MB model) when the optional package is installed; opt out with "embedding": { "provider": "none" }.

  • Large monorepos analyze one package at a time after the first full build (openlore analyze --shard <name>); the initial index is still a full analysis.

  • Incremental updates converge or flag, never silently diverge. When a change exceeds the per-save budget, the rest is marked explicitly stale and self-heals on later edits or a background re-analyze.


Documentation

Start here: the documentation index maps every task to the one canonical page that answers it. The essentials:

I want to…Doc
Install & wire my agent (one command)install.md
Understand why wiring an agent helpsagent-setup.md
See all 83 MCP tools + parametersmcp-tools.md
Run the full CLI referencecli-reference.md
Configure .openlore/config.json (incl. enforcement.policy)configuration.md
Choose / configure an LLM provider (optional)providers.md
Fix a setup problemTROUBLESHOOTING.md
Read the benchmark methodology + per-task numbersAGENT-BENCHMARKS.md
Search code by meaning (semantic / GraphRAG)semantic-search.md
Pick the tests a change should runtest-impact-selection.md
Find dead code / coverage gapsreachability-dead-code.md · coverage-gaps.md
Detect spec ↔ code driftdrift-detection.md
Enforce architecture / import rulesarchitecture-invariants.md
Gate commits / wire CI & PR reviewci-cd.md · preflight.md
Understand IaC & cross-domain impactiac.md · cross-domain-impact.md
See declared API contracts in the graphcontracts.md
Query across repos (federation)federation.md
Share the index as a bundleshareable-bundle.md
Export to Sourcegraph / SCIP toolsscip-export.md
Read the internalsARCHITECTURE.md · ALGORITHMS.md · PHILOSOPHY.md

Development

npm install
npm run build
npm run test:run  # 5500+ unit tests, one-shot (npm test is watch mode)
npm run typecheck

New contributor? See CONTRIBUTING.md for setup, MCP wiring, and the commit gate. Please also read our Code of Conduct; to report a vulnerability, see SECURITY.md.


Community

If OpenLore saves your agents from re-reading the same files — or catches one risky change before it lands — star the repo. It's the signal that tells us to keep building, and it helps other engineers find it.


  • OpenSpec — spec-driven development framework

  • AGENTS.md — system prompt for direct LLM prompting

  • Examples — BMAD, Vibe, GSD, drift-demo, spec-kit integrations

docs/reachability-dead-code.md

Reachability & Dead-Code Analysis

Spec 20. Cross-language. Deterministic, offline, no API key. Candidates, never deletion authority.

find_dead_code answers three graph-reachability questions grep can't (it sees text, not reach) and the model burns tokens guessing at:

  • "What code is unreachable / dead?" — mark-and-sweep from roots; the unreached remainder.

  • "Is anything calling X?" — reachability from every entry point.

  • "What becomes dead if I delete X?" — the set reachable only through X.

Prior art is knip / ts-prune (mark-and-sweep from entry points) — but TypeScript/JavaScript-only. This is the cross-language version over the unified tree-sitter graph (15+ languages).

Read this first — honest limits

Static reachability cannot see dynamic entry points, framework magic (routes, DI, plugin registries), reflection, or public API consumed outside the repo. All of these produce false "dead" positives. So find_dead_code:

  • treats tests, imported symbols, route handlers (including every framework route in the route inventory), main, and files a config invokes as roots,

  • returns confidence-tagged candidates with a reason, never a verdict,

  • and never auto-deletes.

Every response carries soundness.posture: "candidates-not-authority" and explicit caveats. Treat it as a lead generator for a human/agent to verify — not a delete list.

The blind spot is now named per answer, not per README

The paragraph above is a standing caveat: it cannot say which answer is affected. The analyzer now records each construct the resolver cannot follow — a dynamic-boundary site — during the same parse that builds the graph, so a conclusion can name the specific line that bounds it:

  • a candidate with a site that can name it is capped at low, and the stated reason is a computed member dispatch at src/cli/commands/doctor.ts:742 can reach this symbol without a graph edge rather than only "dynamic language";

  • the answer's confidenceBoundary.knownUnknowable carries a dynamic-boundary crossing listing the file, line and kind of every site inside the subgraph the answer traversed — bounded, deduplicated by kind and file, with the omitted count stated;

  • report_coverage_gaps withholds its also-dead label for such a symbol (that label asserts the absence of any caller), and verify_claim resolves a dead or safe-to-change claim to unverifiable instead of confirmed.

Scope is computable, not repository-wide: a site qualifies a candidate only in the candidate's own file or in a file whose transitive import closure can name it, in the candidate's own language, and only for the kinds that can hide a caller (reflective-invoke, computed-member, container-resolution). Sites outside that closure are left to the standing caveat above.

It is disclosure only — never resolution, and never the opposite conclusion. A boundary can withhold a negative claim; it can never report a symbol as live, tested, or unsafe.

The statically provable subset is recovered as edges instead (literalReflection in docs/language-support.md): a stable module-private dispatch table binds each entry by its same-file declaration, as synthesized edges labeled literal-reflective. A construct that binds stops being a site; one that does not stays a site with its refusal reason (over-cap, unresolved-in-file-scope, …), so recovery shrinks the disclosed boundary without removing the disclosure. A directResolvedOnly answer ignores those edges, so it folds the bound constructs back in as synthesized-binding sites and is never less cautious than it was before the edges existed. A language with no matcher records no site and is reported as unsupported by the capability registry, never as containing no dynamic dispatch.

How roots and confidence work

A node is a root (assumed live) if it is a test, is imported by name from another file, serves a declared interface element (an HTTP route, a gRPC RPC, or a GraphQL field), is deployed by an infrastructure resource, is main-like, or lives in a file a config invokes. Reachability is a forward BFS from those roots.

A node is a root (assumed live) if it is a test, is imported by name from another file, is a detected HTTP route handler, is a handler the route inventory pins to it, is main-like, or lives in a file a config invokes. Reachability is a forward BFS from those roots.

Route-inventory roots

Edge-derived roots alone are not enough. http_endpoint, deploys_symbol, implements_endpoint and synthesized route-handler edges all need something to bind to — a cross-language call site, a deploy resource, a declared OpenAPI operation, a statically-paired registration. A framework-decorator route with no declared contract (contractSource: "none") produces none of them, so a @Get('broadcast') method that only Nest invokes used to read as high-confidence dead while get_route_inventory listed it as a route — two contradictory answers from one build. route-inventory.json is already written by analyze, so this is a join, not new analysis (change: root-framework-routes). Each inventoried route is resolved to a graph node by file + handler name, and a handler is rooted only when it pins to exactly one declaration in that file:

OutcomeWhat happens
exactly one declarationliveness root, counted in rootKinds.httpHandlers, receipt in routeInventoryRoots
several declarations (ambiguous)not rooted; every candidate of that name is capped low and told the inventory names it
none in that file (handler-not-in-file)not rooted, counted
file not in the index (file-not-indexed)not rooted, counted
no handler name, or one over 256 chars (handler-not-named)not rooted, counted
an absolute path or one escaping the root (outside-repository)not rooted, counted
past 50,000 routes (over-cap)not rooted, counted
Nothing is guessed — there is deliberately no basename or path-suffix fallback. **A root attributed
to the wrong node is worse than a disclosed unresolved handler, because it makes real dead code look
live.** Every refusal is counted by reason in routeRootsNotAssessed (with up to 10 routes listed)
and summarised as a caveat, exactly the way contractRootsNotAssessed discloses an unresolved
contract binding.
The join is O(routes + nodes): the file → name → ids index is built once and each route is a
constant-time lookup. The artifact is read through the bounded untrusted-artifact reader; an absent,
oversized, truncated, or non-JSON inventory degrades to the edge-only roots with a
routeInventoryRoots.boundary, never a crash and never a silent "no routes". Because an inventoried
route is a decorator literally above the declaration rather than a synthesized edge, these roots
apply in directResolvedOnly mode too.
Every framework the inventory names is an HTTP routing framework (nestjs, express, fastify,
nextjs-app, fastapi, flask, django, starlette, spring, jaxrs, …), so all of these roots count under
rootKinds.httpHandlers; routeInventoryRoots.byFramework breaks them down. No finer root label is
clearly true from that field alone.
Declarations and generated files are not code that can be dead

Two whole categories used to be rated high where the verdict is a category error, not a false negative:

  • TypeScript ambient declarations. Every function-shaped symbol in a .d.ts / .d.mts / .d.cts is bodyless (public static create(properties?: ITrace): Trace;). There is no code there to be dead, so such a symbol is not a candidate at any confidence — excluded from the dead set that find_dead_code, report_coverage_gaps, get_landmarks and verify_claim all share, and counted in declarationsExcluded. For the exported contract's shape, use certify_public_surface.

  • Generated files. A *.pb.go file's Descriptor / ProtoMessage / ProtoReflect / GetXxx, and an UnimplementedFooServer.mustEmbedUnimplementedFooServer that exists only to be embedded, are driven by protobuf reflection and Go embedding — never by a direct in-repo call. Each candidate's file is classified through the generated-file guard and a candidate in a generated, vendored, or lockfile file is capped at low with the guard's rule, match and certainty named in the reason. They stay candidates (nothing here proves a caller); they are just never asserted.

Config-wired roots

Code that only configuration invokes has no importer, so without evidence it reads as orphaned (change: add-framework-entry-point-adapters). Deterministic adapters read:

ConfigWhat counts as wiring
root package.jsonbin, main, module, exports targets; files run by scripts; jest setup files
declared workspace manifeststhe same keys, in the package.json of every member the root workspaces field (array or { packages: [...] }) or pnpm-workspace.yaml packages: declares; each reference resolves against that manifest's own directory and is receipted with its own path
tsconfig.jsonfiles; outDir → rootDir maps a build output (dist/cli/index.js) back to its source
vitest/vite/jest configliteral setupFiles / globalSetup values
.github/workflows/*.ymlfiles run by run: steps, relative to the step's working-directory

A command wires a file only when it executes it: the script after a runner (node, tsx, python, bash, …, past wrappers such as cross-env or npx), a --require / --import preload, or a path in command position. A path passed as an argument (eslint src/x.ts), a redirect target (> dist/out.js), or heredoc content is not wiring.

Every function in a wired file becomes an externally-wired root: the graph has no node for module-scope code, so a wired file's load-time calls cannot be told from its helpers, and the bias stays false-live over false-dead. The report counts them in rootKinds.externallyWired and lists each wired file with its receipts (config file and key) in externalWiring.files.

A reference the adapters cannot resolve is listed in externalWiring.boundaries with a reason and counted in a caveat, never guessed: dynamic-reference (a variable, ${{ }} expression, or glob), unsupported-form (a module run by name, a path after a cd, or a config with too many references), target-not-found, build-output-unmapped (a build output tsconfig does not map to a source), outside-repository, unreadable-config (a link, a non-regular file, or one over 1 MB), and unparsed-config. Only the DECLARED workspace set is read — the tree is never searched for manifests — so a package.json outside it contributes nothing; a repository that declares workspaces carries that limit as a standing externalWiring.boundaries entry, together with any pattern that leaves the repository (outside-repository), a brace list / character class / extglob pattern, and an exact count of members left unread past the cap of 256. A member manifest's own workspaces field is not followed. Framework routing conventions and other config formats are not read, and only POSIX shell workflow steps are tokenized (a PowerShell or cmd step is an unsupported-form boundary). On this repository, 36 of 1,018 entry points are in config-wired files.

A root is labelled by the protocol it serves

A function that serves a declared interface element is a root: whoever calls that element invokes it, in this repository or outside it. That binding is one edge kind (implements_endpoint) with three producers, so the count names the protocol rather than assuming HTTP:

CountWhere the root comes from
rootKinds.grpcHandlersa gRPC RPC — a method a protobuf service RPC is bound to through a server registration
rootKinds.graphqlResolversa GraphQL field — a resolver a literal resolver map names for a schema field
rootKinds.httpHandlersan HTTP route — an OpenAPI/Swagger operation a route registration serves, a cross-language http_endpoint edge, or a synthesized framework route handler (the last only outside strict directResolvedOnly mode)

A gRPC or GraphQL binding is identified POSITIVELY, by the bound contract node's own format tag (Protobuf / GraphQL) — never inferred from the absence of the other protocols. The three are made disjoint by precedence (gRPC → GraphQL → HTTP), as deployed already takes precedence over all of them: a function that serves both a proto RPC and a GraphQL field is counted once, under grpcHandlers. httpHandlers is the fallback lane as well as the HTTP lane — a binding whose contract node carries no readable format tag is counted there, so read it as "an HTTP route, or a contract binding whose format could not be read".

rootKinds is not a partition, and this split does not make it one — externallyWired carries no exclusions at all, so a jest setup file's functions are counted under both tests and externallyWired, and an imported route handler under both imported and httpHandlers. The split only stops a non-HTTP binding being labelled HTTP.

Confidence is deliberately conservative — the bias is toward false-live over false-dead:

ConfidenceWhen
highstatic language · no internal caller · not imported by name · and its module is not imported anywhere
mediumreachable only from other dead code, or no dependency-graph signal available
lowdynamic language (Python/Ruby/PHP/…), or its module is imported elsewhere (namespace/default/re-export usage the named-import scan can't resolve), or a dynamic-boundary site can name it, or contract wiring names it and the binding was refused, or the route inventory names it as a handler it could not pin, or its file is classified generated/vendored
(not a candidate)a bodyless declaration in a .d.ts / .d.mts / .d.cts file — counted in declarationsExcluded

That last rule matters: on a real repo it cut high-confidence candidates from ~470 to ~35 — a symbol living in a module something else imports is never flagged high, because the specific usage may be a namespace or default import this static scan doesn't resolve.

Contract wiring the projection refused to bind

A handler bound to a declared operation is a liveness root. A binding the contract projection refused roots nothing — so a gRPC RegisterOrderServiceServer call or a GraphQL resolver map can name a function literally while that function reaches no root and looks dead. Each refusal is disclosed as precisely as it can be, and a candidate it names is capped at low with the reason; contractRootsNotAssessed names the registration site.

The refusal namesCapped
a function (a grpc-js map entry, a resolver entry)every candidate of that name
an implementation class (a Go/Python servicer type)the candidates of that class — in one directory where the projection's scope rule proves it (Go, unqualified type), by name alone otherwise
no function at all — server.addService(Echo.service, serviceImplementation) hands over a variableevery candidate in the file that declares the registration

File scope is the honest granularity for the third row: the registration does not contain the handler names at all, and such an object is written where it is registered. A refusal that names no function and whose implementation was readable — an inline handler, or an unknown field or service with an all-inline map — caps nothing: it left no named symbol unrooted, so capping would print a reason that is false about the repository.

The cap reads the complete refusal list, not the 20-item reader slice. cg.contracts carries unboundHandlerNames — deduplicated symbol, class and file names over every refusal — because a display cap must not decide a correctness question (grpc-go discloses 43 refusals, so an items-only rule would give up that repository's entire high tier). An index written before that field existed, or one past the field's own 2,000-name cap, cannot name every refusal; then and only then the whole repository gives up high, stated once in contractRootsNotAssessed.refusalsNotNamed and in soundness.caveats, not only on a row that happened to reach the high branch.

What this cap does NOT cover

It is a lower bound, and soundness.caveats says so in the response:

  • an implementation object assembled in another module than the registration;

  • a registration style the scan does not read at all — its RPC is listed in unboundRpcs, and its handler can still be rated high;

  • a GraphQL resolver-plumbing key (__resolveType), which produces no refusal row;

  • OpenAPI unresolvedBindings, which name no symbol (a route's handlerName falls back to the placeholders handler / unknown when extraction fails, so it cannot ground a match).

Where the refusal names only a file, unrelated candidates in that file are capped too — precision lost, never soundness.

Re-export / barrel resolution raises recall (fewer false-dead)

A reachability conclusion is only as sound as the call graph is complete. The TS/JS import resolver follows re-export chains — export { x } from './impl', export * from './x' (and TypeScript's ESM .js specifiers) — through any depth of barrel to a symbol's true definition, and that resolution runs on every cross-file call edge (not just base classes). A call imported through a barrel resolves to the real target (labelled re_export) instead of stalling at the barrel and falling through to the ambiguous first-same-named-candidate (name_only). Concretely, dogfooding this repo moved 29 symbols off the false-dead / false-entry-point list — e.g. EdgeStore.open went from a reported zero callers to its real 22 — because a method/static call through an imported receiver now binds to its definition. See openspec/changes/add-call-resolution-recall/.

Tool contract

// Candidate dead-code report
{ "directory": "/abs/path", "maxResults": 100, "filePattern": "src/" }

// "What becomes dead if I delete X?"
{ "directory": "/abs/path", "ifDeleted": "parseConfig" }

Report output:

{
  "stats": { "analyzed": 1455, "roots": 399, "reachable": 790, "candidateDead": 665 },
  "rootKinds": { "tests": 0, "imported": 393, "grpcHandlers": 0, "graphqlResolvers": 0, "httpHandlers": 0, "externallyWired": 4, "deployed": 0 },

  "rootKinds": { "tests": 0, "imported": 393, "httpHandlers": 0, "externallyWired": 4 },
  "routeInventoryRoots": { "routesRead": 494, "rooted": 486, "byFramework": { "nestjs": 483, "express": 2, "fastify": 1 } },
  "routeRootsNotAssessed": {
    "total": 8, "byReason": { "ambiguous": 5, "handler-not-in-file": 3 },
    "items": [{ "method": "GET", "path": "/x", "framework": "nestjs", "file": "src/a.controller.ts", "handler": "get", "reason": "ambiguous" }],
    "note": "An inventoried route whose handler pins to exactly one declaration is a liveness root. …"
  },
  "declarationsExcluded": { "count": 912, "files": ["packages/proto/generated/cjs/protobuf.d.ts"], "note": "…" },
  "externalWiring": {
    "files": [{ "file": "src/cli/index.ts", "receipts": [{ "config": "package.json", "key": "bin.openlore" }], "roots": 4 }],
    "boundaries": [{ "config": "package.json", "key": "scripts.run", "reference": "$SCRIPT", "reason": "dynamic-reference" }]
  },
  "byConfidence": { "high": 35, "medium": 35, "low": 595 },
  "candidateDead": [
    { "name": "isValidEmail", "file": "src/utils/validation.ts", "language": "TypeScript",
      "fanIn": 0, "confidence": "high",
      "reason": "no internal caller; not imported by name from any other file; not a test, route handler, or main entry" }
  ],
  "coverage": { "languages": ["TypeScript", "Python", "Go"], "exportSignal": "dependency-graph" },
  "soundness": { "posture": "candidates-not-authority", "caveats": ["These are CANDIDATES…", "…"] }
}

Delete-impact output:

{
  "target": "handler",
  "becomesDeadIfDeleted": [{ "name": "helper", "file": "src/app.ts", "language": "TypeScript", "fanIn": 1 }],
  "count": 1,
  "note": "These nodes are reachable only through the target. Deleting it orphans them — verify before removing."
}

How it works

Pure read over the existing graph — no schema change:

  • Reachability — forward BFS over buildAdjacency's forward map from the root set; candidate-dead = code nodes not reached. External and infrastructure (IaC) nodes are excluded.

  • Liveness signals — tests + HTTP route handlers (edge-derived and joined from route-inventory.json, see route-inventory-roots.ts) + main + config-wired files, plus the dependency graph's imported names (symbol-level) and imported files (module-level) for the cross-language "used elsewhere" signal.

  • Delete-impact — recompute reachability with the target removed from both seeds and the graph, and diff against the baseline reached set.

Implementation: reachability.ts. Tested over a two-language fixture with known live regions, a dead orphan, a dead cluster, and a delete-impact diff in reachability.test.ts. The route-inventory roots, the declaration and generated-file rules, and the cross-tool invariant "no symbol is both in get_route_inventory and a high dead candidate" are pinned in reachability-routes.test.ts.

Accuracy depends on a current analyze_codebase that includes tests and produces the dependency graph. Without test nodes as roots, test-only code is flagged; without the dependency graph, confidence is reduced and the response says so.

Index integrity. When the persisted index does not reconcile against its build-time attestation — materially smaller than the build committed (degraded) or built at a different schema (mismatched) — the response carries that verdict in confidenceBoundary.integrity and is not marked complete. A "dead" conclusion over a half-built index is the most dangerous false negative, so "looks dead to a broken index" is labeled, never asserted. Re-run analyze_codebase to rebuild.

512 files · 450 not served here

Advantages

  • Deterministic, local-first operation with no LLM in the hot path ensures consistent, grounded answers. evidence: README.md
  • Provides governance guardrails that flag breaking changes and sensitive boundary violations before code is committed. evidence: README.md
  • Maintains persistent memory across sessions that survives code renames and refactors. evidence: README.md
  • Reduces agent round-trips and costs by providing structural context instead of relying on exploratory file reads. evidence: README.md

Limitations

  • Static analysis cannot recover reflective invocations, eval, or dynamic DI/plugin registries with no visible bindings. evidence: README.md
  • Small, familiar repositories may experience overhead compared to native agent search. evidence: README.md
  • Large monorepos require incremental analysis strategies after the initial full build. evidence: README.md

Security

Risk level: Low risk

This brain runs

Agent instructions that run or fetch something (169)

  • .claude/commands/first-run-hardening.md: Your job: find and fix the bugs that break a NEW USER's first-run experience — the way earlier passes found "unknown command 'orient'", "--no-embed kills BM25", "watcher EMFILE on target/", and "install never builds the index".
  • .claude/commands/first-run-hardening.md: BUILD FIRST so you test current code, not the published version: `npm run build` → drive `node dist/cli/index.js`, never bare `openlore`/`npx openlore` (those resolve the OLD published version).
  • .claude/commands/first-run-hardening.md: When comparing to published behavior, do it explicitly with `npx --yes openlore@latest` and label it.
  • .claude/commands/first-run-hardening.md: Run the REAL first-run flow as a user would, end to end, checking exit codes AND actual output of each step: openlore install # the one-command path
  • .claude/commands/first-run-hardening.md: Also exercise: init → analyze → analyze --no-embed → analyze --force → orient (no task / with task / bad --limit) → install --no-analyze → install --dry-run → install --uninstall → mcp server over stdio (initialize → tools/call orient).
  • .claude/commands/first-run-hardening.md: Idempotency / re-runs: running install or analyze twice must be a clean no-op, not a duplicate or a clobber.
  • .claude/commands/first-run-hardening.md: Treat other people's repos as READ-ONLY fixtures: after each dogfood run, remove ALL openlore artifacts (.openlore, openspec/, CLAUDE.md, ARCHITECTURE.md) and `git checkout` any modified .gitignore / settings.json.
  • .claude/commands/first-run-hardening.md: Verify the repo is clean (`git status`) before moving on.
  • .claude/commands/first-run-hardening.md: Verify honestly: run `npm run typecheck` and `npm run test:run` and report the REAL counts.
  • .claude/commands/first-run-hardening.md: confirm with `git rev-list --left-right --count origin/main...main` (want `0 0`) before and after.
  • .claude/commands/first-run-hardening.md: Branch pruning: only delete branches already merged into origin/main (verify with `git branch -r --merged origin/main`);
  • AGENTS.md: Before committing code (pre-commit hook via `openlore drift --install-hook`)
  • AGENTS.md: openlore drift --install-hook # Install as git pre-commit hook
  • AGENTS.md: [ ] No spec drift — run `openlore drift` to verify specs match code
  • AGENTS.md: - Run `openspec validate --all` to verify structure
  • AGENTS.md: - Run `openlore drift --install-hook` to catch future drift
  • AGENTS.md: Always run `openlore analyze` first.
  • AGENTS.md: Or install the Vibe skill (creates `.vibe/skills/openlore.md` as a `/openlore` slash command):
  • AGENTS.md: Because the output limit is 4 096 tokens (vs 16 000 for Claude), generation produces **shorter specs per run**.
  • AGENTS.md: **One domain at a time** — run `openlore generate` with `--domain <name>` so each invocation focuses on a single domain and stays within the output budget.
  • AGENTS.md: **Use the analysis artifacts** — always run `openlore analyze` first;
  • AGENTS.md: **Expect multi-pass generation** — large codebases may require multiple `generate` runs (one per domain) rather than a single all-domains run.
  • AGENTS.md: Then run `openlore decisions --sync` and retry `git commit`.
  • AGENTS.md: **`reason: "approved_not_synced"` — decisions approved but not written to specs:** Run `openlore decisions --sync` then retry `git commit`.
  • AGENTS.md: If yes: run `openlore decisions --consolidate --gate` and handle the result.
  • AGENTS.md: If no: retry with `git commit --no-verify` to skip the gate.
  • AGENTS.md: If yes: run `openlore decisions --consolidate --gate` and handle the result.
  • AGENTS.md: If no: retry with `git commit --no-verify` to skip the gate.
  • CLAUDE.md: CLI: `openlore coverage-gaps` | | "What's the blast radius of my diff before I commit?" | `blast_radius` — one advisory briefing: callers/layers, tests to run, anchored memories/decisions that will drift, stale specs | | "Post a deterministic structural review on a PR" (CLI, no MCP/agen …[+99 chars]
  • CLAUDE.md: Call `orient "<task description>"` (via the openlore MCP server, or `npx openlore orient --json`) **before touching a module you have not yet read in this session**, and when a task spans several modules.
  • CLAUDE.md: re-run `openlore install --preset <name>` to change it and regenerate these instructions.
  • examples/mistral-vibe/skills/openlore-analyze-codebase/SKILL.md: Suggest running `/openlore-plan-refactor` once the user has enough context to act, then `/openlore-execute-refactor` to apply the plan
  • examples/mistral-vibe/skills/openlore-analyze-codebase/SKILL.md: To activate `search_specs`, run `openlore analyze --embed` or `openlore analyze --reindex-specs`.
  • examples/mistral-vibe/skills/openlore-brainstorm/SKILL.md: Using Domain Sketch — structural analysis will be available after `openlore analyze` is run on the first implementation."
  • examples/mistral-vibe/skills/openlore-brainstorm/SKILL.md: - First implementation — run `openlore analyze && openlore generate` after
  • examples/mistral-vibe/skills/openlore-debug/SKILL.md: **Prerequisite**: openlore analysis must exist (`openlore analyze` has been run).
  • examples/mistral-vibe/skills/openlore-debug/SKILL.md: If `orient` returns `"error": "no cache"` → run `analyze_codebase` first, then retry.
  • examples/mistral-vibe/skills/openlore-debug/SKILL.md: Run it.
  • examples/mistral-vibe/skills/openlore-debug/SKILL.md: **Apply the fix**, then run the test again.
  • examples/mistral-vibe/skills/openlore-debug/SKILL.md: Run the full test suite.
  • examples/mistral-vibe/skills/openlore-debug/SKILL.md: If the domain spec does not exist yet (`uncovered` from Step 8), note the invariant in the story/issue instead and flag it for the next `openlore generate` run.
  • examples/mistral-vibe/skills/openlore-execute-refactor/SKILL.md: "execute the planned refactoring"
  • examples/mistral-vibe/skills/openlore-execute-refactor/SKILL.md: explicit command `/openlore-execute-refactor`
  • examples/mistral-vibe/skills/openlore-execute-refactor/SKILL.md: **Prerequisite**: the `openlore-plan-refactor` skill must have been run and the plan confirmed.
  • examples/mistral-vibe/skills/openlore-execute-refactor/SKILL.md: If a coverage tool is available, run it on the target file and compare against the coverage baseline in the plan.
  • examples/mistral-vibe/skills/openlore-execute-refactor/SKILL.md: git status # must show: nothing to commit, working tree clean
  • examples/mistral-vibe/skills/openlore-execute-refactor/SKILL.md: git log --oneline -1 # note this commit hash — your restore point
  • examples/mistral-vibe/skills/openlore-execute-refactor/SKILL.md: For **each change** in the plan, execute the full mini-development cycle below.
  • examples/mistral-vibe/skills/openlore-execute-refactor/SKILL.md: **4 — TEST** Run the test gate from the plan entry.
  • examples/mistral-vibe/skills/openlore-execute-refactor/SKILL.md: Run the full test suite one final time to confirm the refactored state is clean.
  • examples/mistral-vibe/skills/openlore-execute-refactor/SKILL.md: Apply renames one file at a time, run tests after each, and respect the ≤ 50-line edit constraint.**
  • examples/mistral-vibe/skills/openlore-generate/SKILL.md: OpenLore does not validate OpenSpec structure, so run the `openspec validate` follow-up the receipt names.
  • examples/mistral-vibe/skills/openlore-generate/SKILL.md: **If validation cannot run — the CLI is missing, broken, or fails for any reason — say so explicitly and report the spec as NOT validated.** Never present an unvalidated spec as complete;
  • examples/mistral-vibe/skills/openlore-generate/SKILL.md: Finalize: after validation, run `openlore mapping refresh` when shell access is available so the persisted link index matches the spec you wrote.
  • examples/mistral-vibe/skills/openlore-generate/SKILL.md: If you cannot run it, say so explicitly — correctness is unaffected, because audit and Repair re-derive the index in memory, and only the cache is stale.
  • examples/mistral-vibe/skills/openlore-implement-story/SKILL.md: **Prerequisite**: openlore analysis must exist (`openlore analyze` has been run).
  • examples/mistral-vibe/skills/openlore-implement-story/SKILL.md: If `orient` returns `"error": "no cache"` → run `analyze_codebase` first, then retry.
  • examples/mistral-vibe/skills/openlore-implement-story/SKILL.md: | Situation | Approach | |---|---| | `risk_context` present, risk 🟢 < 40 | Skip to Step 3 — use insertion point from context | | `risk_context` present, risk 🟡 40–69 | Run Step 2 impact check, then proceed | | `risk_context` present, risk 🔴 ≥ 70 | Stop — a blocking refactor story mus …[+81 chars]
  • examples/mistral-vibe/skills/openlore-implement-story/SKILL.md: Run `/openlore-execute-refactor` on the blocking function first, or create a blocking refactor task and return to this story once the risk is resolved.
  • examples/mistral-vibe/skills/openlore-implement-story/SKILL.md: Run the `openlore-repair` host skill before implementing.
  • examples/mistral-vibe/skills/openlore-implement-story/SKILL.md: Only run once tests are green.
  • examples/mistral-vibe/skills/openlore-implement-story/SKILL.md: | Drift type | Resolution | |---|---| | `uncovered` on new files | Note it — propose `openlore generate` post-sprint | | `gap` on existing domain | Run the `openlore-repair` host skill | | `stale` | Fix the reference | | No drift | Done |
  • examples/mistral-vibe/skills/openlore-plan-refactor/SKILL.md: To apply the plan, use the `openlore-execute-refactor` skill.
  • examples/mistral-vibe/skills/openlore-plan-refactor/SKILL.md: Each change = one atomic edit → one diff verification → one full test run → ✅ or rollback.
  • examples/mistral-vibe/skills/openlore-plan-refactor/SKILL.md: If a recent analysis already exists, skip unless the user explicitly requests a fresh run.
  • examples/mistral-vibe/skills/openlore-plan-refactor/SKILL.md: | Ecosystem | Command | |---|---| | Node.js | `npm test -- --coverage --collectCoverageFrom="<files>"` | | Python | `pytest --cov=<module> --cov-report=term-missing` | | Rust | `cargo tarpaulin --include-files <files>` | | Go | `go test -cover ./...` |
  • examples/mistral-vibe/skills/openlore-plan-refactor/SKILL.md: **Test gate**: exact test command to run after this change
  • examples/mistral-vibe/skills/openlore-plan-refactor/SKILL.md: Workflow: /openlore-plan-refactor → /openlore-execute-refactor
  • examples/mistral-vibe/skills/openlore-plan-refactor/SKILL.md: - `git diff --stat` shows only the expected files
  • examples/mistral-vibe/skills/openlore-plan-refactor/SKILL.md: Hash: <to be filled by the execute workflow>
  • examples/mistral-vibe/skills/openlore-write-tests/SKILL.md: **Prerequisite**: openlore analysis must exist (`openlore analyze` has been run).
  • examples/mistral-vibe/skills/openlore-write-tests/SKILL.md: If `orient` returns `"error": "no cache"` → run `openlore analyze` first, then retry.
  • examples/mistral-vibe/skills/openlore-write-tests/SKILL.md: | File found | Framework | |---|---| | `vitest.config.*`, `vitest.config.ts` | Vitest — runner: `npx vitest run <file>` | | `jest.config.*` | Jest — runner: `npx jest <file>` | | `pytest.ini`, `pyproject.toml` (with `[tool.pytest]`), `setup.cfg` | pytest — runner: `pytest <file> -v` | …[+147 chars]
  • examples/mistral-vibe/skills/openlore-write-tests/SKILL.md: Run the test file with `$TEST_RUNNER`.
  • examples/opencode-skills/openlore-analyze-codebase/SKILL.md: Suggest running `/openlore-plan-refactor` once the user has enough context to act, then `/openlore-execute-refactor` to apply the plan
  • examples/opencode-skills/openlore-analyze-codebase/SKILL.md: To activate `search_specs`, run `openlore analyze --embed` or `openlore analyze --reindex-specs`.
  • examples/opencode-skills/openlore-brainstorm/SKILL.md: Using Domain Sketch — structural analysis will be available after `openlore analyze` is run on the first implementation."
  • examples/opencode-skills/openlore-brainstorm/SKILL.md: - First implementation — run `openlore analyze && openlore generate` after
  • examples/opencode-skills/openlore-debug/SKILL.md: **Prerequisite**: openlore analysis must exist (`openlore analyze` has been run).
  • examples/opencode-skills/openlore-debug/SKILL.md: If `orient` returns `"error": "no cache"` → run `analyze_codebase` first, then retry.
  • examples/opencode-skills/openlore-debug/SKILL.md: Run it.
  • examples/opencode-skills/openlore-debug/SKILL.md: **Apply the fix**, then run the test again.
  • examples/opencode-skills/openlore-debug/SKILL.md: Run the full test suite.
  • examples/opencode-skills/openlore-debug/SKILL.md: If the domain spec does not exist yet (`uncovered` from Step 8), note the invariant in the story/issue instead and flag it for the next `openlore generate` run.
  • examples/opencode-skills/openlore-execute-refactor/SKILL.md: "execute the planned refactoring"
  • examples/opencode-skills/openlore-execute-refactor/SKILL.md: explicit command `/openlore-execute-refactor`
  • examples/opencode-skills/openlore-execute-refactor/SKILL.md: **Prerequisite**: the `openlore-plan-refactor` skill must have been run and the plan confirmed.
  • examples/opencode-skills/openlore-execute-refactor/SKILL.md: If a coverage tool is available, run it on the target file and compare against the coverage baseline in the plan.
  • examples/opencode-skills/openlore-execute-refactor/SKILL.md: git status # must show: nothing to commit, working tree clean
  • examples/opencode-skills/openlore-execute-refactor/SKILL.md: git log --oneline -1 # note this commit hash — your restore point
  • examples/opencode-skills/openlore-execute-refactor/SKILL.md: For **each change** in the plan, execute the full mini-development cycle below.
  • examples/opencode-skills/openlore-execute-refactor/SKILL.md: **4 — TEST** Run the test gate from the plan entry.
  • examples/opencode-skills/openlore-execute-refactor/SKILL.md: Run the full test suite one final time to confirm the refactored state is clean.
  • examples/opencode-skills/openlore-execute-refactor/SKILL.md: Apply renames one file at a time, run tests after each, and respect the ≤ 50-line edit constraint.**
  • examples/opencode-skills/openlore-generate/SKILL.md: OpenLore does not validate OpenSpec structure, so run the `openspec validate` follow-up the receipt names.
  • examples/opencode-skills/openlore-generate/SKILL.md: **If validation cannot run — the CLI is missing, broken, or fails for any reason — say so explicitly and report the spec as NOT validated.** Never present an unvalidated spec as complete;
  • examples/opencode-skills/openlore-generate/SKILL.md: Finalize: after validation, run `openlore mapping refresh` when shell access is available so the persisted link index matches the spec you wrote.
  • examples/opencode-skills/openlore-generate/SKILL.md: If you cannot run it, say so explicitly — correctness is unaffected, because audit and Repair re-derive the index in memory, and only the cache is stale.
  • examples/opencode-skills/openlore-implement-story/SKILL.md: **Prerequisite**: openlore analysis must exist (`openlore analyze` has been run).
  • examples/opencode-skills/openlore-implement-story/SKILL.md: If `orient` returns `"error": "no cache"` → run `analyze_codebase` first, then retry.
  • examples/opencode-skills/openlore-implement-story/SKILL.md: | Situation | Approach | |---|---| | `risk_context` present, risk 🟢 < 40 | Skip to Step 3 — use insertion point from context | | `risk_context` present, risk 🟡 40–69 | Run Step 2 impact check, then proceed | | `risk_context` present, risk 🔴 ≥ 70 | Stop — a blocking refactor story mus …[+81 chars]
  • examples/opencode-skills/openlore-implement-story/SKILL.md: Run `/openlore-execute-refactor` on the blocking function first, or create a blocking refactor task and return to this story once the risk is resolved.
  • examples/opencode-skills/openlore-implement-story/SKILL.md: Run the `openlore-repair` host skill before implementing.
  • examples/opencode-skills/openlore-implement-story/SKILL.md: Only run once tests are green.
  • examples/opencode-skills/openlore-implement-story/SKILL.md: | Drift type | Resolution | |---|---| | `uncovered` on new files | Note it — propose `openlore generate` post-sprint | | `gap` on existing domain | Run the `openlore-repair` host skill | | `stale` | Fix the reference | | No drift | Done |
  • examples/opencode-skills/openlore-plan-refactor/SKILL.md: To apply the plan, use the `openlore-execute-refactor` skill.
  • examples/opencode-skills/openlore-plan-refactor/SKILL.md: Each change = one atomic edit → one diff verification → one full test run → ✅ or rollback.
  • examples/opencode-skills/openlore-plan-refactor/SKILL.md: If a recent analysis already exists, skip unless the user explicitly requests a fresh run.
  • examples/opencode-skills/openlore-plan-refactor/SKILL.md: | Ecosystem | Command | |---|---| | Node.js | `npm test -- --coverage --collectCoverageFrom="<files>"` | | Python | `pytest --cov=<module> --cov-report=term-missing` | | Rust | `cargo tarpaulin --include-files <files>` | | Go | `go test -cover ./...` |
  • examples/opencode-skills/openlore-plan-refactor/SKILL.md: **Test gate**: exact test command to run after this change
  • examples/opencode-skills/openlore-plan-refactor/SKILL.md: Workflow: /openlore-plan-refactor → /openlore-execute-refactor
  • examples/opencode-skills/openlore-plan-refactor/SKILL.md: - `git diff --stat` shows only the expected files
  • examples/opencode-skills/openlore-plan-refactor/SKILL.md: Hash: <to be filled by the execute workflow>
  • examples/opencode-skills/openlore-write-tests/SKILL.md: **Prerequisite**: openlore analysis must exist (`openlore analyze` has been run).
  • examples/opencode-skills/openlore-write-tests/SKILL.md: If `orient` returns `"error": "no cache"` → run `openlore analyze` first, then retry.
  • examples/opencode-skills/openlore-write-tests/SKILL.md: | File found | Framework | |---|---| | `vitest.config.*`, `vitest.config.ts` | Vitest — runner: `npx vitest run <file>` | | `jest.config.*` | Jest — runner: `npx jest <file>` | | `pytest.ini`, `pyproject.toml` (with `[tool.pytest]`), `setup.cfg` | pytest — runner: `pytest <file> -v` | …[+147 chars]
  • examples/opencode-skills/openlore-write-tests/SKILL.md: Run the test file with `$TEST_RUNNER`.
  • skills/openlore-analyze-codebase/SKILL.md: Suggest running `/openlore-plan-refactor` once the user has enough context to act, then `/openlore-execute-refactor` to apply the plan
  • skills/openlore-analyze-codebase/SKILL.md: To activate `search_specs`, run `openlore analyze --embed` or `openlore analyze --reindex-specs`.
  • skills/openlore-brainstorm/SKILL.md: Using Domain Sketch — structural analysis will be available after `openlore analyze` is run on the first implementation."
  • skills/openlore-brainstorm/SKILL.md: - First implementation — run `openlore analyze && openlore generate` after
  • skills/openlore-debug/SKILL.md: **Prerequisite**: openlore analysis must exist (`openlore analyze` has been run).
  • skills/openlore-debug/SKILL.md: If `orient` returns `"error": "no cache"` → run `analyze_codebase` first, then retry.
  • skills/openlore-debug/SKILL.md: Run it.
  • skills/openlore-debug/SKILL.md: **Apply the fix**, then run the test again.
  • skills/openlore-debug/SKILL.md: Run the full test suite.
  • skills/openlore-debug/SKILL.md: If the domain spec does not exist yet (`uncovered` from Step 8), note the invariant in the story/issue instead and flag it for the next `openlore generate` run.
  • skills/openlore-execute-refactor/SKILL.md: "execute the planned refactoring"
  • skills/openlore-execute-refactor/SKILL.md: explicit command `/openlore-execute-refactor`
  • skills/openlore-execute-refactor/SKILL.md: **Prerequisite**: the `openlore-plan-refactor` skill must have been run and the plan confirmed.
  • skills/openlore-execute-refactor/SKILL.md: If a coverage tool is available, run it on the target file and compare against the coverage baseline in the plan.
  • skills/openlore-execute-refactor/SKILL.md: git status # must show: nothing to commit, working tree clean
  • skills/openlore-execute-refactor/SKILL.md: git log --oneline -1 # note this commit hash — your restore point
  • skills/openlore-execute-refactor/SKILL.md: For **each change** in the plan, execute the full mini-development cycle below.
  • skills/openlore-execute-refactor/SKILL.md: **4 — TEST** Run the test gate from the plan entry.
  • skills/openlore-execute-refactor/SKILL.md: Run the full test suite one final time to confirm the refactored state is clean.
  • skills/openlore-execute-refactor/SKILL.md: Apply renames one file at a time, run tests after each, and respect the ≤ 50-line edit constraint.**
  • skills/openlore-generate/SKILL.md: OpenLore does not validate OpenSpec structure, so run the `openspec validate` follow-up the receipt names.
  • skills/openlore-generate/SKILL.md: **If validation cannot run — the CLI is missing, broken, or fails for any reason — say so explicitly and report the spec as NOT validated.** Never present an unvalidated spec as complete;
  • skills/openlore-generate/SKILL.md: Finalize: after validation, run `openlore mapping refresh` when shell access is available so the persisted link index matches the spec you wrote.
  • skills/openlore-generate/SKILL.md: If you cannot run it, say so explicitly — correctness is unaffected, because audit and Repair re-derive the index in memory, and only the cache is stale.
  • skills/openlore-implement-story/SKILL.md: **Prerequisite**: openlore analysis must exist (`openlore analyze` has been run).
  • skills/openlore-implement-story/SKILL.md: If `orient` returns `"error": "no cache"` → run `analyze_codebase` first, then retry.
  • skills/openlore-implement-story/SKILL.md: | Situation | Approach | |---|---| | `risk_context` present, risk 🟢 < 40 | Skip to Step 3 — use insertion point from context | | `risk_context` present, risk 🟡 40–69 | Run Step 2 impact check, then proceed | | `risk_context` present, risk 🔴 ≥ 70 | Stop — a blocking refactor story mus …[+81 chars]
  • skills/openlore-implement-story/SKILL.md: Run `/openlore-execute-refactor` on the blocking function first, or create a blocking refactor task and return to this story once the risk is resolved.
  • skills/openlore-implement-story/SKILL.md: Run the `openlore-repair` host skill before implementing.
  • skills/openlore-implement-story/SKILL.md: Only run once tests are green.
  • skills/openlore-implement-story/SKILL.md: | Drift type | Resolution | |---|---| | `uncovered` on new files | Note it — propose `openlore generate` post-sprint | | `gap` on existing domain | Run the `openlore-repair` host skill | | `stale` | Fix the reference | | No drift | Done |
  • skills/openlore-orient/SKILL.md: This is the lowest-latency path and gives the model access to whatever tool surface the server was wired with — the lean 10-tool `navigation` preset by default (`openlore install --preset full` wires all 62), not just `orient`.
  • skills/openlore-orient/SKILL.md: bash scripts/orient.sh "<task description>"
  • skills/openlore-orient/SKILL.md: The wrapper tries the direct CLI subcommand first (`npx --yes openlore orient --json --task "<task>"`) and falls back to driving the `openlore mcp` server over stdio JSON-RPC via the sibling `orient-via-mcp.mjs` helper on older openlore versions that predate the CLI subcommand.
  • skills/openlore-orient/SKILL.md: Run `npx openlore analyze` once to seed the graph, then retry.
  • skills/openlore-orient/SKILL.md: Re-run `npx openlore analyze` to rebuild.
  • skills/openlore-plan-refactor/SKILL.md: To apply the plan, use the `openlore-execute-refactor` skill.
  • skills/openlore-plan-refactor/SKILL.md: Each change = one atomic edit → one diff verification → one full test run → ✅ or rollback.
  • skills/openlore-plan-refactor/SKILL.md: If a recent analysis already exists, skip unless the user explicitly requests a fresh run.
  • skills/openlore-plan-refactor/SKILL.md: | Ecosystem | Command | |---|---| | Node.js | `npm test -- --coverage --collectCoverageFrom="<files>"` | | Python | `pytest --cov=<module> --cov-report=term-missing` | | Rust | `cargo tarpaulin --include-files <files>` | | Go | `go test -cover ./...` |
  • skills/openlore-plan-refactor/SKILL.md: **Test gate**: exact test command to run after this change
  • skills/openlore-plan-refactor/SKILL.md: Workflow: /openlore-plan-refactor → /openlore-execute-refactor
  • skills/openlore-plan-refactor/SKILL.md: - `git diff --stat` shows only the expected files
  • skills/openlore-plan-refactor/SKILL.md: Hash: <to be filled by the execute workflow>
  • skills/openlore-repair/SKILL.md: OpenLore does not validate OpenSpec structure, so run the `openspec validate` follow-up the receipt names.
  • skills/openlore-repair/SKILL.md: **If validation cannot run — the CLI is missing, broken, or fails for any reason — say so explicitly and report the spec as NOT validated.** Never present an unvalidated edit as complete;
  • skills/openlore-repair/SKILL.md: Finalize: after validation, run `openlore mapping refresh` when shell access is available so the persisted link index matches the spec you edited.
  • skills/openlore-repair/SKILL.md: If you cannot run it, say so explicitly — correctness is unaffected, because audit and Repair re-derive the index in memory, and only the cache is stale.
  • skills/openlore-write-tests/SKILL.md: **Prerequisite**: openlore analysis must exist (`openlore analyze` has been run).
  • skills/openlore-write-tests/SKILL.md: If `orient` returns `"error": "no cache"` → run `openlore analyze` first, then retry.
  • skills/openlore-write-tests/SKILL.md: | File found | Framework | |---|---| | `vitest.config.*`, `vitest.config.ts` | Vitest — runner: `npx vitest run <file>` | | `jest.config.*` | Jest — runner: `npx jest <file>` | | `pytest.ini`, `pyproject.toml` (with `[tool.pytest]`), `setup.cfg` | pytest — runner: `pytest <file> -v` | …[+147 chars]
  • skills/openlore-write-tests/SKILL.md: Run the test file with `$TEST_RUNNER`.

Package scripts (1)

  • package.json (postinstall): node scripts/postinstall.mjs
  • Low risk: network-instructions (.claude/commands/first-run-hardening.md:21)
  • Low risk: scripts-present (bench/retrieval/run.ts)
  • Low risk: scripts-present (bench/run.ts)
  • Low risk: autonomy-phrase (examples/mistral-vibe/skills/openlore-execute-refactor/SKILL.md:240)
  • Low risk: autonomy-phrase (examples/opencode-skills/openlore-execute-refactor/SKILL.md:240)
  • Low risk: scripts-present (scripts/api-consumer-smoke.mjs)
  • Low risk: scripts-present (scripts/copy-assets.mjs)
  • Low risk: scripts-present (scripts/postinstall.mjs)
  • Low risk: autonomy-phrase (skills/openlore-execute-refactor/SKILL.md:240)
  • Low risk: network-instructions (skills/openlore-orient/SKILL.md:37)
  • Low risk: scripts-present (skills/openlore-orient/scripts/orient.sh)

Attribution and licence

Created by
Clay Good
Declared authors
  • Clay Good (declared in package.json)
Publisher
Clay Good (Community)
Source
clay-good/OpenLore · commit c45c2ca
Licence
MIT License (files served here)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by Clay Good.

Install from source

450 of its files are not served here, so there is no ZIP. clay-good/OpenLore at c45c2ca