cairn
Provides tools for querying an organization's GitHub repositories without cloning, including finding similar implementations, reading org repo files, and profiling the org's stack and shared packages.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@cairnwhere is the trips table defined across my repos?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
cairn
A map of all your repos for coding agents. cairn scans every git repo in a folder once, works out how they connect, and gives your agent:
a tiny always-loaded index;
a short card per repo.
The agent knows where things live without re-exploring.
Cairns are the stacked-stone markers that guide hikers along a trail. cairn leaves small, cheap markers that guide coding agents to the right repo, and then to the right file.
Works with Claude Code, Codex, Gemini CLI, and Cursor, on Windows, macOS, and Linux. Using Claude Code? Install cairn as a plugin, or paste that section into Claude Code and it installs everything for you.
Why
Coding agents work inside one repo. Real work spans many: a web app, an API, a shared types package, a payments service, a reporting job. Say "add a tip amount to trips end to end" and the agent has to:
discover that sibling repos exist;
grep through all of them;
read a pile of files;
guess which service owns the
tripstable.
That costs tokens and turns, and weaker models guess wrong.
Hand-maintained "related repos" docs help until they go stale. cairn builds that map from the code itself and keeps it fresh:
~/code/ ~/code/.cairn/
├── rider-web/ cairn ├── INDEX.md ← ~25 tokens per repo, always loaded
├── trips-svc/ ─────▶ ├── cards/trips-svc.md ← read on demand (≤ 800 tokens)
├── payments-svc/ scan ├── workspace.json ← the full graph
├── analytics-etl/ └── authored/ ← your summaries and decisions
└── …Related MCP server: koragraph
What your agent sees
The index is a few lines, loaded into every session through CLAUDE.md or an equivalent:
# Workspace repos (cairn)
- admin-console (@fleetline/admin-console): Next.js back-office for ops staff; queries trips… · nextjs
- analytics-etl: Nightly SQL reporting jobs over trips, payments, drivers… · python
- payments-svc (payments): Go service that charges completed trips and records weekly payouts · go
- trips-svc: FastAPI service owning the trip lifecycle and the trips/trip_events tables · fastapi · used by admin-console
- ui-kit (@fleetline/ui-kit): Shared React components (Button, Card) · react · used by admin-consoleused by lists the repos that depend on, call, or reference that one, so a change's blast
radius is visible before any card is opened.
A card is read only when the agent needs that repo:
# trips-svc
> FastAPI service owning the trip lifecycle and the trips/trip_events tables.
`./trips-svc` · python, fastapi · HEAD 1fee22d
## Relates
← infra: references path trips-svc (extracted · infra/docker-compose.dev.yml:3)
→ admin-console: shares tables trips (inferred · admin-console/lib/db.ts:4)
→ analytics-etl: shares tables trips (inferred · analytics-etl/jobs/daily_revenue.sql:2)
→ payments-svc: shares tables trips (inferred · payments-svc/internal/charge/charge.go:3)
## Run
test `pytest`
## Layout
app/ → routes/pages
migrations/ → DB migrationsEvery relationship comes with evidence (file and line) and a trust level:
extracted: found directly, such as a package dependency or a path reference;inferred: strong signals, such as a table that only one repo creates;ambiguous: hidden until you confirm it.
Install
You need Python 3.11+ and git.
Tool | Command |
uv (recommended) |
|
pipx |
|
run once, no install |
|
pip |
|
The package is called cairnmap; the command is cairn.
As a Claude Code plugin
If you use Claude Code, the plugin is the whole setup: it builds the map when you ask, loads it into every session, and starts cairn's MCP tools. It runs cairn from PyPI through uv.
Type it yourself in Claude Code, then restart Claude Code:
/plugin marketplace add Moe1177/cairn
/plugin install cairn@cairnOr ask Claude Code to do it. Paste this section, or the link to it, into Claude Code and say "install this". The steps below are written so Claude Code can follow them.
Check that uv is installed:
uvx --version. If it isn't found:uv may be installed but not on PATH (
pip install --user uvputs it in%APPDATA%\Python\Python3*\Scriptson Windows,~/Library/Python/3.*/binon macOS). If it's there, tell the user to add that folder to PATH instead of installing uv again.Otherwise, ask the user before installing uv with its official installer:
macOS and Linux:
curl -LsSf https://astral.sh/uv/install.sh | shWindows:
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
Add the marketplace and install the plugin:
claude plugin marketplace add Moe1177/cairn claude plugin install cairn@cairnCheck it:
claude plugin listshowscairn@cairnas enabled.If
~/.claude/skills/cairn/SKILL.mdexists, cairn was also installed withcairn install claude. Runcairn uninstall claude, or every cairn tool appears twice.Tell the user to restart Claude Code, then open it inside any repo of their folder of repos. If that folder has no map yet, Claude offers to build one (or say "build the cairn map").
After that, every session starts with the map, and the cairn MCP tools are ready. The plugin never
edits CLAUDE.md. If uv goes missing later, the first session says so and Claude offers to install
it, only if you agree.
To | Run |
Update |
|
Remove |
|
Codex, Gemini CLI and Cursor use cairn install instead (see Quick start).
Quick start
cd ~/code # the folder that CONTAINS your repos
cairn init # scan, then add the index to ./CLAUDE.md (asks first)
cairn install all # optional: Codex, Gemini CLI, Cursor, the /cairn skill, the MCP serverOpen your agent in any repo under that folder. It now knows about every sibling repo.
To give each repo a good one-line summary, run /cairn inside your agent. It writes the summaries
with cairn set-summary and settles uncertain links. Until then, the index shows cairn's draft
from what it detected, such as draft: web app (104 routes, owns 46 tables) or draft: library (used as a package by 3 repos), and "no summary yet" where it found nothing to go on. A draft
is built from counts only: README text is deliberately kept out of always-loaded context.
How to use it
See what a repo links to
Inside any repo, run cairn links, or ask your agent "which repos does this repo link to?". Both
give the same answer:
trips-svc is linked to 3 repos:
admin-console ↔ both ways shares 2 tables, mentions each other in docs
payments-svc ← one way calls this repo (2 routes)
rider-web ← one way calls this repo (3 routes)→ one way means this repo uses the other one, ← one way that the other one uses this repo,
and ↔ both ways that each uses the other or they share something, like a database. Add
--evidence for the file and line behind each link. In Claude Code, the plugin's /cairn:links
does the same.
Day to day
cairn status # repos, relationships, unconfirmed links, missing/stale summaries
cairn refresh # re-read only repos that changed (fast)
cairn hooks install # optional: refresh automatically after each commit/mergeSettle uncertain links once
cairn annotate-edge "web->api:shares_db" --confirm
cairn annotate-edge "shop->blog:shares_db" --reject --why "different databases"
cairn set-summary payments-svc "Charges completed trips and pays drivers weekly." --alias paymentsThese decisions live in .cairn/authored/ and .cairn/relations.yaml. They survive every re-scan,
and you can commit them so your team shares them.
From your agent (MCP)
cairn install <harness> registers cairn's MCP server. Agents can call these tools:
Tool | What it answers |
| "Which repo is 'the payments service'?" |
| The card for a repo (re-scanned first if its HEAD moved) |
| Which repos a repo is linked to, one line each with its direction (→ one way, ← one way, ↔ both ways); empty means the repo the session is in |
| Everything connected to a repo, with evidence |
| Which repos expose or use a table, package, or path |
| Where inside a repo: |
| Where the org already implemented a feature (its repos on GitHub, nothing cloned); needs |
| A file from an org repo, without cloning it |
| The org's stack: the datastores, frameworks, auth, test and deploy tools and libraries its active repos use, and its own shared packages |
| The org's own libraries its repos share, where each is defined and who uses it; with a name, one package's users and code that calls it |
| Update the map now |
What cairn detects
Signal | Examples |
HTTP calls | Next.js routes, Express/Fastify/Hono, FastAPI/Flask, Go (net/http, chi, gin, echo) and OpenAPI routes, matched with |
gRPC | Go, Python, TypeScript and Java servers matched with their client stubs |
Service names | Calls addressed to a sibling's service name, as Docker and Kubernetes DNS do: |
Pub/sub topics | Kafka, NATS, Redis and RabbitMQ (Spring AMQP) publishers matched with subscribers ( |
docker-compose |
|
Deploy repos | compose, Kubernetes and Helm files that run your repos' images ( |
Render services |
|
Package dependencies | npm ( |
Packages inside monorepos | npm/yarn/pnpm, Cargo, |
Shared database tables | SQL migrations and queries, Prisma, Drizzle, Supabase, MongoDB (Mongoose models, |
Copies of one app | Repos that share their first commit (an app cloned per event or per client): "change one, check the other". The same package name only suggests it |
Path references |
|
Documentation | READMEs and docs that mention a sibling repo |
AWS resources | Which repo creates each SQS queue, SNS topic, DynamoDB table, S3 bucket, EventBridge bus, Kinesis stream, Lambda function, SSM parameter and CloudFormation export (Terraform, CloudFormation/SAM, Serverless Framework, CDK), and which repos use it: ARNs, queue URLs, |
Shared env vars | Specific names read by both sides. These only back up another link; they never make one on their own |
Look-alikes are deliberately ignored:
health-check routes;
calls to other companies' APIs;
vague topic names;
.protofiles with no implementer;generic env vars such as
PORT;localhost, public domains, and URLs in comments;public images (
mongo:3.4) and look-alike names (catalogue-dbis notcatalogue);a queue that a repo declares but never consumes;
the same schema in two copies of one app (copies often use a database each, so no link);
Firestore's
db.collection(...), which looks like MongoDB's;the Terraform state bucket every repo's backend names, IAM wildcards (
arn:aws:sqs:*:*:*), AWS's own event sources (aws.ec2), and generated deploy output (cdk.out,.serverless).
Evaluation workspaces in the test suite keep every one of these at precision 1.0.
Why not read AWS resource tags?
Tags record who owns a resource in an AWS account. cairn answers a different question, which code depends on which code, and it gets that from the repos alone:
Tags only describe the creator. Half of every link is who uses a resource, and the repos that read a queue or a table don't tag it. Those links come from ARNs, SSM paths, exports and SDK calls in the users' code.
cairn reads code, not the account. It makes no network calls and needs no credentials. The only tags it could see are in the IaC block that creates the resource, and that block already tells cairn which repo creates it. A tag only adds something when a central infrastructure repo creates resources for app repos; there, each user already links to that infra repo.
No tag key means "owning repo".
team,app,serviceandNamemean different things in different places, and tags drift from the code without anything noticing. A link from code evidence says which file and line it came from.
cairn bench-links scores links against real workspaces (One Login's AWS repos among them), so a
new signal like tags can be measured before it's trusted.
Finding the place in a repo
The map tells an agent which repo to open; the query MCP tool tells it where inside. Ask
"where is login handled?" and it answers with src/auth.py:12 login() — grep: login (definition).
How it searches:
It runs grep first, through cairn's hardened
git grep. It looks for the question's identifiers, routes and quoted messages, and failing those, its words.For "who calls X", it lists the callers before the definition.
It also searches repos the question names, and the repos the map relates to the one asked about, unless an identifier, route or message already answered.
It never searches files cairn never opens (
.env, keys,secrets.yaml…).Every answer says which search answered and why. It needs nothing installed.
Deep indexes are optional, and built per repo with graphify. A code graph names the symbol on grep's hits and answers when grep finds nothing. cairn asks grep first because, on its offline locate benchmark, grep matched or beat the code graph on every kind of question (results).
cairn deep enable # installs graphify if needed (asks first), indexes every repo
cairn deep status # size, build sha, fresh or stale
cairn refresh # also rebuilds stale deep indexes once any exist (--no-deep skips)
cairn deep build trips-svc # one repo by hand; cairn deep clear trips-svc deletes itcairn deep enable installs graphify as its own tool (uv tool install graphifyy, else pipx, else
pip), so cairn's own environment is left alone; cairn doctor shows how many repos are indexed.
You can also install cairn with the extra (pip install 'cairnmap[graphify]'); cairn refresh --deep
forces a deep rebuild check even before any index exists.
graphify always runs
--code-only: no LLM, no network, and an allowlisted environment, so no API key reaches it. It writes only to.cairn/deep/<repo>/, never into the repo.cairn answers queries itself from the saved graph, offline, so serving needs no graphify.
The first build is something you ask for (
cairn deep enableorcairn deep build): a large repo can take minutes. After that,cairn refresh(and the git hooks' background refresh) keeps indexes current, one build per repo at a time. When an index still falls behind,queryandcairn deep statussay so; the card's Deeper section flags new commits.
Prior art across your org
Before an agent builds a feature, it should know whether the org already built it somewhere: the team's rate limiter, its way of verifying webhooks, the retry helper everyone uses. In an org of thousands of repos nobody has them all cloned, so cairn asks the code host instead. Nothing is cloned or downloaded beyond the files an agent chooses to read.
Name the org in .cairn/config.yaml:
orgs:
- org: acme # github.com; for GitHub Enterprise add `host: git.acme.com`cairn uses your GitHub login (gh auth login, or GH_TOKEN), so it sees exactly the repos you
can. Then an agent calls find_similar with the feature and a few words its code would contain,
and read_org_file to read what it finds:
find_similar(task="stop one client from flooding our API",
terms=["RateLimiter", "X-RateLimit-Remaining", "express-rate-limit"])
Prior art in acme for `RateLimiter`, `X-RateLimit-Remaining`, `express-rate-limit`: 10 files (3 searches, 0.6s).
- gateway/src/middleware/rate-limit.ts (matched RateLimiter, X-RateLimit-Remaining)
- ...From a terminal: cairn similar "stop one client from flooding our API" --terms RateLimiter,X-RateLimit-Remaining.
The words matter. A request rarely shares words with the code that answers it ("flooding" never
appears in a rate limiter), so the agent, which understands the request, supplies the code's
words. Each term is one code search; they run at once. Hits are merged by rank, so a file several
searches find rises, and what isn't prior art sinks or goes: vendored copies of libraries
(node_modules, site-packages, Lambda layers), docs, notebooks, configs, tests. On ~9,300
repos (aws-samples), a file that already implements the task was in the top five for 17 tasks
in 18 on the tasks cairn was tuned on and 10 in 12 on tasks written after it, against 5 in 18 and
2 in 12 for one plain search of three of the task's words, in about 0.6 s a task
(results).
Code search allows 10 requests a minute, so find_similar makes at most four (one per term);
past the limit it waits, and its answer says how long.
The org's stack
New code should be built with what the org builds with. org_profile (or cairn org-profile)
says what that is, read from its 200 most recently pushed repos: their languages, datastores,
queues, web frameworks, auth, test and deploy tools, their most used libraries per ecosystem, and
the org's own packages that other repos depend on, each with the share of repos that use it:
The stack of aws-samples (github.com): what its 200 most recently pushed repos use (share of those repos), read 2026-10-17.
Languages: Python 40%, TypeScript 26%, JavaScript 8%, ...
Datastores: S3 42%, DynamoDB 27%, OpenSearch/Elasticsearch 10%, PostgreSQL 8%, MySQL 7%, Redis 6%, ...
Auth: Cognito 26%, JWT 14%
Deploy and CI: Docker 54%, AWS CDK 50%, GitHub Actions 43%, Terraform 19%, ...
Python libraries: boto3 42%, strands-agents 24%, pytest 18%, requests 18%, ...It reads one listing page per 100 repos, each repo's file tree and up to three of its manifests (about 15 s, some 800 API requests), then answers from a cache for a week.
It does not make find_similar find more. Terms written after reading the profile found prior
art for 10 of 12 held-out tasks against 11 of 12 for terms written from the task alone
(results), so cairn doesn't tell agents to search
with it.
The org's own packages
An agent adding logging, retries or a service client should reach for the org's own library,
not a public one, and call it the way sibling repos do. org_packages (or cairn org-packages)
lists the libraries the org's repos use from one another, most shared first, with where each
is defined:
The own packages of hashicorp (github.com): the 226 libraries its 604 active repos use from one another, most used first (by how many repos), read 2026-10-17. ...
- github.com/hashicorp/go-hclog (Go) · 89 repos · in hashicorp/go-hclog — A common logging package for HashiCorp tools
- github.com/hashicorp/vault/api (Go) · 43 repos · in hashicorp/vault/api
- github.com/hashicorp/go-retryablehttp (Go) · 16 repos · in hashicorp/go-retryablehttp — Retryable HTTP client in Goquery narrows the list ("retry http"); a name shows one package: its manifest, every repo
that uses it (and which of a monorepo's projects), and up to five files that import it, found
with one code search. A package counts as the org's when one active repo defines it and another
uses it, or its name is under the org (@acme/..., github.com/acme/...); a fork is its home
only under the org's name, and a name several repos define is a template's, not a library.
It reads every active repo's file tree and manifests (hashicorp's 604 repos: 35 s, about 1,300 API requests), then answers from a cache. After a day it reads again, but only the repos pushed since (2.3 s when none were). When the hour's API budget runs low, it stops with 500 requests to spare and the next call reads the rest, most recently pushed first.
Told what to build, an agent that read the list named the right library for every hashicorp task, and for all 14 held-out grafana tasks. With the org's name masked (a private org's packages, which no model has read about), the agent alone named none (results).
Benchmarks
cairn ships a benchmark harness (cairn bench). It runs real tasks through headless Claude Code
under five conditions, each in a fresh, isolated copy of a multi-repo workspace:
Condition | |
A | No map (the agent explores) |
B | A hand-written "related repos" doc |
C | cairn's index only |
D | Index + repo cards |
E | D + cairn's MCP server |
Tasks cover:
orientation: "who owns the trips table?";
localization: "which files change to add a tip amount end to end?";
cross-repo impact: "what breaks if
drivers.license_nois renamed?";control: questions answerable within one repo.
Answers are graded deterministically on the files and facts they must name.
Results (2026-10-06): 840 runs over four workspaces, two of them real open-source systems: Sock Shop (9 microservice repos; cairn's newest link types were developed on it) and the Supabase JS client family (6 repos, held out: never used to tune cairn). 28 tasks, 3 runs per cell, Haiku 4.5 and Sonnet 5.5. A result is called significant only after Holm adjustment.
Per task, cairn INDEX (C) | Haiku 4.5 | Sonnet 5.5 |
Fresh tokens vs no map | -19% (significant) | -10% (n.s.) |
Cost vs no map | -25% (n.s. after adjustment) | -12% (n.s.) |
Cost vs a hand-written doc | -6% (n.s.) | -12% (significant) |
Cost vs no map, Sock Shop only | -27% (n.s.) | -26% (borderline, adjusted p = 0.055) |
What this shows:
cairn tends to make cross-repo work cheaper: fewer fresh tokens for Haiku, and cheaper than a hand-written related-repos doc for Sonnet. The largest raw savings were on Sock Shop.
It doesn't measurably raise success. Sonnet answers 99-100% of these tasks in every condition; Haiku rises from 86% to 93% with the MCP server, which isn't significant.
It isn't a win everywhere. On fleetline, Sonnet cost 7-10% more with cairn than without (n.s.); the held-out Supabase effects are small and not significant.
Correction: our first, smaller run (2026-10-05) reported Haiku reaching 100% with the INDEX. With more runs that doesn't replicate (89-92%, the same as no map).
A bigger workspace isn't enough to make no map hard. A pilot on OpenFaaS (38 real repos, 2026-10-10, Sonnet 5.5, no map, one run per task) answered all ten of its tasks, for $0.07 a task with two or three searches each: every answer holds a literal string one workspace-wide search finds.
Neither are hard tasks (2026-10-11 pilot, one run per cell). There are 11 of them, on OpenFaaS and One Login. No search for a task's own words finds its answer, and the prompt doesn't name the repo the answer is in. The agent has to read an identifier in code and follow it to a repo it works out itself.
Sonnet answered 11/11 in every condition.
Haiku answered 10/11 in every condition.
With a map, Sonnet's cost fell 17-19% on OpenFaaS and rose 19-29% on One Login.
The one failure a map clearly prevented: Haiku with no map never looked outside the repo it started in.
bench/published/2026-10-11-hard-tasks.md has the tasks, the definition and every failure.
Methods, every table with 95% intervals, paired Wilcoxon tests (raw and Holm-adjusted), the regressions, and the held-out results: bench/published/2026-10-06-real-world.md. The first run is kept at 2026-10-05-shopverse-fleetline.md.
Run the benchmarks yourself from a source checkout. They use your Claude usage.
cairn bench bench/suites/sockshop --runs 3 --model haiku # fetches the pinned repos once
cairn bench bench/suites/shopverse --conditions A,C --tasks gift-message
cairn bench bench/suites/sockshop --resume bench/results/<stamp>.jsonl # after a usage limit
cairn bench bench/suites/sockshop --harness codex --runs 3 # Codex instead of Claude Code
cairn bench-regrade bench/suites/sockshop bench/results/<stamp>.jsonl # grade a log againA task marked hard: true in a suite must be one that no search for the prompt's own words
answers, and whose prompt doesn't name the repo the answer is in. cairn bench-reach checks
this offline, and fails on a hard task that breaks either rule:
cairn bench-reach bench/suites/openfaas bench/suites/oneloginWith --harness codex, each run is codex exec in a read-only sandbox that ignores your Codex
config and rules and turns off every tool beyond reading the workspace (connected apps, plugins,
web search, sub-agents). The condition's context reaches it as developer instructions. A global
AGENTS.md in your Codex home would load into every run, so the benchmark won't start while
one is there.
The locate benchmark is offline and free: no agent runs. Each locator (grep, the code graph, and
the hybrid of both) answers every question in a suite's locate.yaml, and is scored on where the
answer file lands, how much text it returns and how long it takes.
cairn bench-locate bench/suites/sockshop bench/suites/networkx # builds code graphs with graphify
cairn bench-locate bench/suites/shopverse --no-build --locators grepThe prior-art benchmark asks the question an agent has when it starts a feature in a large org:
does an implementation of this already exist, and where? Each suite's prior_art.yaml names a
GitHub org and lists feature tasks with files in it that already implement them, found and read
by hand. Every locator answers every task over GitHub's API, with nothing cloned, and is scored
on where the first answer file lands, how much text it returns and how long it takes. An org
holds more implementations than a key can list, so the report also says what share of each
locator's top five the key has judged; an unjudged hit is unknown, not wrong. It needs a GitHub
login (gh auth login, or GH_TOKEN), and code search allows 10 requests a minute, so a run
waits out the limit as it goes (about ten minutes for both suites). The cairn locator searches
with the terms an agent would pass, kept beside each key in agent_terms.yaml: a model wrote
them from the task alone (--make-terms), and nobody edits them.
cairn bench-prior-art bench/suites/aws-samples bench/suites/aws-samples-heldThe packages benchmark asks whether an agent reaches for the org's own library. Each suite's
packages.yaml lists tasks with the org's packages that serve them. A model names the
libraries it would use, best first, from the task alone and after reading the list
org_packages returned (kept beside the key as org_packages.md, made with --make-list). It
does each twice, once with the org renamed, since a public org's libraries are in its training
data and a private org's aren't. The answers are kept in agent_picks.yaml (--make-picks),
so a run is repeatable and free.
cairn bench-packages bench/suites/hashicorp bench/suites/grafanaThe link benchmark is offline and free too. Each suite's links.yaml lists the pairs of repos
that really link, written from the code and never from cairn's output; cairn bench-links scans
the suite and scores the links cairn shows (and, separately, its unconfirmed suggestions) as
pairs of repos: how many real links it finds, how many it finds that aren't real, and whether
it points them the right way.
cairn bench-links bench/suites/sockshop bench/suites/supabase-jsLinks cairn shows (2026-10-10) | real links | found | not real | precision | recall |
Sock Shop (real, 9 repos) | 17 | 15 | 0 | 1.00 | 0.88 |
Supabase JS (real, held out) | 5 | 5 | 0 | 1.00 | 1.00 |
fleetline + shopverse (synthetic) | 26 | 26 | 0 | 1.00 | 1.00 |
GOV.UK One Login (real AWS, 10 repos, held out) | 24 | 12 | 0 | 1.00 | 0.50 |
OpenFaaS (real, 38 repos) | 88 | 48 | 4 | 0.92 | 0.55 |
What the misses and wrong links are:
Sock Shop: orders reads users and carts through URLs the front-end puts in each order; no hostname for them appears in orders' code.
One Login: cairn finds 11 of its 12 AWS links (SQS queues, DynamoDB tables and SSM parameters behind stack parameters, shared Lambdas, KMS keys, exports). It misses the HTTP links (core calling each credential issuer, known from Pact contracts; stubs standing in for services) and one export whose stack name only a deploy script sets.
OpenFaaS: it was held out until its first run showed 19 wrong links, all READMEs telling readers to run
faas-cli, and missed Go modules outside a repo's root (gateway/go.mod). Both are fixed (it scored 0.70 precision and 0.51 recall before), so it is now tuned on, not held out. Most misses left are images used as Dockerfile base images, HTTP clients of the gateway, and the function store's JSON catalogue.
Unconfirmed suggestions add one or two wrong links in most suites (a table name two repos share, look-alike repos).
Performance
cairn walks each repo once, reads each file once, and scans repos in parallel. A refresh re-reads only repos whose HEAD or working tree changed, and asks git one question per unchanged repo. On a synthetic workspace of 200 repos and 50,000 files (Windows 11, 12 cores):
0.3 | 0.4 | |
First scan | 156 s | 53 s |
Refresh, nothing changed | 15 s | 5.4 s |
Most of a refresh on Windows is git process start-up; Linux and macOS start processes faster.
Reproduce with uv run python bench/perf_scan.py --out <dir>.
Safety and privacy
cairn treats every scanned repo as untrusted input. It:
never modifies your repos, apart from the opt-in hooks and Cursor's
--per-reporule (both marked blocks, removed cleanly);never opens
.envfiles, private keys, or credential files, and never reads through a symlink or junction inside a repo;never runs code from a repo: its git calls turn off fsmonitor, hooks, and the repo's own filters.
It also:
redacts common secret formats from every stored snippet, and stores git remotes without credentials;
keeps README text out of always-loaded context. Repo names that do appear are flattened and capped, so they can't inject instructions or break cairn's blocks.
cairn collects no telemetry, and mapping your folder makes no network calls. It talks to a server only when you ask it to:
with
orgs:in the config,find_similar,read_org_file,org_profile,org_packages,cairn similar,cairn org-profileandcairn org-packagescall your code host's API (GitHub, with your own login:GH_TOKEN, orGH_ENTERPRISE_TOKENfor an Enterprise host, elsegh auth token). They send the search terms (the task's own words when no terms are given) and the org's name, and read back matching files; nothing from your local repos is sent. They only ever reach the orgs in your config: a call naming another org, or another owner's repo, is refused, and a search hit from outside the org is dropped;cairn bench-prior-artcalls the same API, and with--make-termsruns theclaudeCLI (with--make-profile, it also reads the org's profile);cairn bench-packagesreads the org's packages with--make-listand runs theclaudeCLI with--make-picks;cairn deep enableruns your package installer (uv, pipx or pip) to add graphify;cairn bench,cairn bench-locateandcairn bench-linksfetch their pinned benchmark repos with git, andcairn benchruns theclaudeCLI.
See SECURITY.md for the threat model and how to report a vulnerability.
Reference
Commands
Command | What it does |
| Scan, then offer to add the index to Claude Code |
| Map every repo under the folder into |
| Re-read only repos whose HEAD or working tree changed, and rebuild stale deep indexes |
| Build graphify code indexes so |
| List deep indexes (fresh or stale) / delete them |
| What cairn knows, unconfirmed links, missing or stale summaries |
| Settle a relationship |
| Save a summary ( |
| Load cairn into an agent harness |
| Remove it again |
| Opt-in git hooks that refresh after commits and merges |
| The MCP server (harnesses start it for you) |
| Which repos a repo is linked to, one line each with its direction; the repo you're in by default ( |
| What a Claude Code session should know about its folder: the index, or an offer to build a map (the plugin's session hook runs it) |
| Where your org already implemented a feature, over GitHub's API |
| Your org's stack, read from its recently pushed repos over GitHub's API (cached for a week) |
| Your org's own packages: where each is defined and which repos use it (cached for a day) |
| Run the benchmark harness |
| Score grep, graph and hybrid locate offline |
| Score cairn's links against each suite's real ones, offline |
| Score finding an org's existing implementation of a feature, over the host's API (no clone) |
| Score whether an agent names the org's own library for a task, from memory and from |
| Grade a finished run's log again with today's grading |
| Which tasks a search for the prompt's own words answers, offline; fails on a hard task that one does |
| Check git, Python, the map, write access, harnesses and graphify; exits 1 on a failure |
| Tab completion for your shell (bash, zsh, fish, PowerShell) |
| Versions of cairn, Python, the platform, and mcp |
Exit codes: 0 means success, 1 an error (one line on stderr), and 2 a usage error.
What cairn writes, and where
Path | What it is |
| One line per repo: name, aliases, summary, stack |
| One card per repo |
| The full graph |
| Scan cache, last scan log, lock file |
| Yours: aliases, manual links, summaries, decisions |
| A marked block holding the index ( |
|
|
| Codex pointer, MCP server, skill |
| Gemini CLI pointer, MCP server, command |
| Cursor MCP server and command |
| Only with |
| Mapped workspaces; one private backup of each config file cairn first edited |
cairn only edits its own key or marked block in other tools' files, and refuses to touch a file it can't parse.
Uninstall
cairn uninstall all # harness entries, skills, commands, Cursor rules
cairn hooks uninstall # if you installed hooks
uv tool uninstall cairnmap # or: pipx uninstall cairnmapThen delete <folder>/.cairn/ and ~/.cairn/.
Troubleshooting
Symptom | Fix |
Something seems off | Run |
"No git repos found under …" | Run cairn from the folder that contains your repos |
"… is inside the git repository …" | Same: run from the parent folder, not inside a repo |
"git not found on PATH" warning | Install git; without it, remotes, HEAD and caching are off |
"another cairn process is still updating this workspace" | A scan or hook refresh is running; retry in a moment |
An agent says it can't find the workspace | Run |
| That file isn't valid TOML/JSON; fix it and re-run (cairn never guesses) |
Hooks did nothing |
|
Roadmap
✅ Core map, precision pass, MCP server, harness integrations, freshness, benchmarks, release hardening (0.1).
✅ HTTP, gRPC, pub/sub, compose and env-var relationships; packages inside monorepos (0.2).
✅ Deep per-repo queries via graphify (0.3).
✅ Efficiency: 3x faster scans, faster and more reliable CI,
cairn doctor, shell completion, and "used by" on INDEX lines (0.4).✅ Real-world reach (service DNS, deploy repos, RabbitMQ) and benchmarks on real open-source workspaces with significance testing (0.5).
✅ Deploy config: which repo creates each AWS resource and which use it (Terraform, CloudFormation/SAM, Serverless Framework, CDK, Lambda, SSM), and Railway, Fly.io and Render services (0.8).
✅ Codex in the benchmark, Maven and Gradle, real-world link benchmarks, draft summaries.
✅ Harder benchmark tasks: 11 tasks that no search for their own words answers, checked by
cairn bench-reach. Without a map, Sonnet still answered all of them.✅ Prior art across an org too big to clone:
find_similarandread_org_fileover GitHub's API, measured bycairn bench-prior-arton ~9,300 repos.✅ The org's stack:
org_profilelists what the org's active repos build with, in about 15 s and from a cache after. Writing search terms from it didn't find more prior art, and the benchmark says so.✅ The org's own packages across repos nobody cloned:
org_packagessays where each is defined, which repos use it and which files call it, measured bycairn bench-packages.Benchmark sessions that start with only their own repo in view, the way a session opened in one repo does.
Contributing
Issues and pull requests are welcome. See CONTRIBUTING.md for setup and checks, and the changelog for what's new. By participating you agree to the code of conduct.
License
cairn is licensed under the Apache License 2.0: use it, modify it, and ship it, commercially too. Keep the license and the NOTICE file with any redistribution. Full text: LICENSE.
Runtime dependencies and their licenses:
This server cannot be deployed
Maintenance
Related MCP Connectors
Search indexed code, trace dependencies, assess change impact, and recall repository memory.
Ask who calls a route, what a change breaks or who reads a field, across a system's repos.
Give your AI agent a persistent map of your project's structure, dependencies, and bugs.
Ask a codebase what calls what: search, blast radius, paths between symbols, and diffs.
Related MCP Servers
- AlicenseAqualityAmaintenanceEnables AI coding agents to query a semantic cross-repository code graph for symbols, references, callers, dependencies, and change impact across registered repositories.1223Apache 2.0
- AlicenseNot gradedqualityBmaintenanceEnables coding agents to query a local, multi-repo code graph for symbol exploration, blast radius analysis, co-change mining, and durable code-anchored memory via MCP tools.69 npm14Business Source 1.1
- AlicenseAqualityAmaintenanceEnables coding agents to get a compact, token-budgeted map of any repository in one call, including file tree, language stats, entry points, and key symbols, so they read only what they need.41MIT
- AlicenseNot gradedqualityAmaintenanceProvides local-first repository architecture analysis with file-level proof, enabling agents to map codebases, locate implementations, trace call paths, and assess change impact with deterministic evidence.4 npm7MIT