A local-first "brain" of your ~/code folder — an interactive, Obsidian-style
graph of how every repo connects to the infrastructure it depends on.
It answers, at a glance:
- Which repos use which API keys / services (Anthropic, Supabase, Google, Stripe, …)
- Which repos are Telegram bots, and which talk to a database
- Which are deployed live vs local-only
- Which repos have live system processes — launchd agents/daemons (KeepAlive) and running MCP / dev servers, with their listening port and instance count
- Where your dependencies are shared — click "Supabase" and every repo that uses it lights up (true backlinks)
- Cross-repo links — which apps consume your shared design systems
Nodes are your repos plus the things they depend on (each service, database, platform, design system). Edges mean "repo uses entity". Node size = how shared a dependency is, so your biggest hubs (Vercel, Supabase, Anthropic…) are obvious.
- Quick start
- How it works
- Configuration
- Privacy
- Bonus: Obsidian vault
- Scripts
- Tech
- Contributing
- Code of Conduct
- License
One-time setup:
cd~/code/codebrain
npm install
npm link # registers the global `codebrain` commandThen, from anywhere, just run:
codebrainThat re-scans ~/code (fresh graph every run), builds the UI once (cached after
the first run), serves it on a free port, and opens it in your browser.
Flags: --no-open (print the URL only), --no-scan (reuse the last graph),
--rebuild (force a fresh UI build), --port <n>.
Prefer npm scripts? npm run dev does the same via the Vite dev server on
http://localhost:5290; npm run scan only refreshes the data.
Two parts:
scan.mjs— a zero-dependency Node scanner that walks your code root and, per repo, extracts: git remote, deploy status (live vs local), env-var names → services/API keys, telegram bots, databases, framework, and cross-repo design-system usage. Writespublic/graph.json(nodes + links + stats). It also snapshots the runtime layer (see below).What counts as a repo: each top-level directory under the code root that looks like a project (has a
package.json,.git,pyproject.toml,go.mod,index.html, source files at its root, …). It does not recurse to find nested repos — a monorepo is a single node. The two collection foldersdesign-systems/andthird-party-repos/are the exception: their immediate children are scanned ascollection/childrepos.A Vite + React force-graph UI that renders
graph.json. Hover to highlight neighbours, click a node to pin it and open a detail panel of everything it connects to, search, and filter by type / live-only / orphans.
On each scan, codebrain also detects the system processes attached to your
repos and adds them as process nodes (orange, lime ring = running) linked to
the repo they run from:
- launchd agents & daemons (
~/Library/LaunchAgents,/Library/LaunchAgents,/Library/LaunchDaemons) whoseWorkingDirectory/ProgramArgumentspoint into a repo — withKeepAlive/RunAtLoadand running-vs-loaded status. - Live processes launched from a repo dir (MCP stdio servers, dev servers). Identical instances (e.g. one MCP server per editor session) collapse into a single node with an instance count.
- The listening TCP port for each, matched by the process's working directory (so a server that forks a worker to hold the socket still resolves).
This is a snapshot at scan time: persistent launchd jobs always appear; on-demand MCP/dev servers appear only while they're running.
The scanner reads env-var keys only. When it parses a .env file it keeps
the text before the first = and discards the value — no secret value is
ever stored, logged, or written to graph.json. The runtime layer is the same
names-only philosophy: it reads ps command lines and plist bodies in
memory to find which repo a process belongs to, then stores only the repo
match, a script basename, the manager, port, status and instance count — never
raw command lines, process args, or a plist's EnvironmentVariables block (any
of which could carry secrets).
All settings live in codebrain.config.json; edit it and re-run npm run scan.
A couple of settings also accept an environment variable, which takes
precedence and is handy for values you don't want to commit.
The code root is resolved in this order:
CODE_ROOTenvironment variable (e.g.CODE_ROOT=~/projects codebrain)codeRootincodebrain.config.json- Default: the parent directory of the codebrain repo — so if codebrain
lives in
~/code/codebrain, it scans~/code.
Built-in defaults already skip non-code dirs (downloads, iterm,
terminal-config, interview-prep folders, …); use include to override them,
and exclude to drop your own.
A repo is marked live if it has a linked Vercel project, or its README links to a
known hosting domain (vercel.app, netlify.app, pages.dev, fly.dev,
github.io, render.com, railway.app, …). To recognise your own custom
domains, add them to liveDomains:
{ "liveDomains": ["example.com"] } // *.example.com and example.com now count as liveOr, to keep a personal domain out of the committed config, set
CODEBRAIN_LIVE_DOMAINS (comma-separated) — it's merged with the defaults and
your config:
CODEBRAIN_LIVE_DOMAINS="example.com,my-other-domain.dev" codebrainCODE_ROOT and CODEBRAIN_LIVE_DOMAINS can also live in a .env file next to
the codebrain repo, so you don't have to edit your shell profile or the tracked
config. Copy the template and edit:
cp .env.example .env.env is git-ignored; variables already set in your shell override it.
codebrain is local-first: nothing is uploaded, and the scanner only ever reads env-var names, never values (see Security). But the generated output describes your own infrastructure — repo names, which services and databases each uses, deploy status, custom domains, and running processes/ports.
For that reason the generated artifacts are git-ignored and never committed:
public/graph.json— the graph dataobsidian-vault/— the per-repo Markdown notesdist/— the built UI (embeds the graph)
Keep them ignored. If you record a demo or share screenshots, run against a sample/anonymized code root or scrub identifiers first.
Every scan also writes obsidian-vault/ — one Markdown note per repo with
[[backlinks]] to each service/database/platform it uses. Open that folder as an
Obsidian vault to explore the same graph in Obsidian's native graph view.
| command | what it does |
|---|---|
codebrain | global launcher: scan + build (cached) + serve + open browser |
npm run dev | scan + Vite dev server on :5290 (auto-opens) |
npm run scan | regenerate public/graph.json and the Obsidian vault |
npm run build | production build to dist/ |
npm test | run the scanner unit tests (vitest) |
Vite · React · react-force-graph-2d (d3-force) · zero-dep Node scanner · vitest.
Contributions are welcome! Please open an issue first to discuss what you'd like to change.
- Fork the repo
- Create a feature branch (
git checkout -b feature/your-feature) - Commit your changes (
git commit -m 'feat: describe change') - Push and open a pull request
Please make sure tests pass (npm test) before submitting a PR. The pure scanner
helpers in lib.mjs are covered by lib.test.mjs — add a case there when you
change classification or parsing logic. See How it works for an
overview of the scanner and UI.
This project follows the Contributor Covenant v2.1. By participating you agree to uphold a welcoming, harassment-free environment.
Distributed under the MIT License. See LICENSE for details.

{ "codeRoot": null, // folder to scan (supports ~). null = parent of this repo"exclude": [ "third-party-repos", // a whole collection"third-party-repos/foo", // one subfolder"some-repo"// a top-level repo by name ], "include": [], // force back in repos excluded by built-in defaults"liveDomains": [] // extra domains that count as a "live" deployment }