@stack256org/brainer
v0.6.3
Published
Your docs and your landing page are nodes in your code graph. Change a function, know which paragraph of marketing copy just became a lie.
Maintainers
Readme
Brainer
Your docs and your landing page are nodes in your code graph.
Change a function → know which paragraph of marketing copy just became a lie. Start a task → get the files that matter, without the search loop.
no API key · no network · no telemetry · 24MB · works offline
Two problems, one graph
1. Prose rots silently
Every code-intelligence tool can tell you what calls what. None can tell you
that yoursite.com/product/ still advertises the feature you renamed last
Tuesday.
$ brainer surfaces getSession
getSession() src/lib/auth/helpers.ts
3 surfaces describe this:
docs/architecture.md › Server vs client 98% declared
docs/auth.md › Authentication › File Layout 98% declared
docs/security.md › Cookie hardening 90% semantic2. Agents search blind, and you pay for it
An agent starting a task greps, globs, reads, discards, greps again. Every round costs tokens and latency, and it still misses things.
$ brainer context "add rate limiting to the login flow"
src/lib/auth/server.ts 91% semantic + imported by login
docs/auth.md › Auth Flow 88% prose match
src/app/auth-form.tsx 84% imports auth/server
src/lib/auth/actions.ts 81% semantic
...
Nothing matched: "throttle" — search for this directly.Measured against a keyword-grep baseline on four real tasks:
| | grep | Brainer | |---|---|---| | recall of the files that mattered | 48% | 94% | | tokens spent | 21.1k | 12.8k |
Twice the recall, ~60% of the cost, one call instead of a loop.
Quick start
npm install -g @stack256org/brainer
brainer install # detect your agent, wire everything up
brainer init # build the indexInstalled from GitHub, not npm — the name
braineron npm belongs to an unrelated package. The@stack256orgscope is registered;@stack256org/brainerpublishes there when it is ready.
After that it is on by default — nobody has to remember Brainer exists or ask for it each session.
brainer install --global # wire up every session, not just this project
brainer serve # the whole graph in a browserinit downloads a 24MB model once. After that Brainer never touches the network.
On by default, including cold sessions
| Host | How it becomes default |
|---|---|
| Claude Code | SessionStart + UserPromptSubmit + PostToolUse hooks |
| Codex, Copilot CLI, Gemini CLI | skill in ~/.agents/skills/ + AGENTS.md |
| Cursor, Windsurf, Continue, Aider | AGENTS.md / .cursorrules + MCP instructions |
| Any other MCP client | MCP instructions sent on connect |
A cloud session starts with no index — a fresh clone, an ephemeral
container. SessionStart detects that, builds the index in the background, and
says so. The query hooks stay quiet until it is ready, so a cold session
degrades to no Brainer, never to Brainer but wrong.
Always-on costs 36 tokens per turn, measured across a realistic mix of task openers, follow-ups and off-topic prompts. One grep round-trip in an agent loop is 800–2000.
Works with your agent
Built in tiers, so it degrades instead of breaking.
| Tier | Surface | Works with | Automatic | |---|---|---|---| | 1 | CLI | anything, including CI | no | | 2 | MCP | every MCP client | on request | | 3 | Skills | Claude Code, Codex, Copilot CLI, Gemini CLI | model decides | | 4 | Hooks | Claude Code | every time |
The CLI is the substrate — MCP, skills and hooks are thin wrappers over it, so a host Brainer has never heard of still works at tier 1.
MCP config is identical everywhere; only the key differs (mcpServers,
servers for VS Code, context_servers for Zed). brainer install writes the
right one. Full matrix, plus an AGENTS.md snippet for hosts without skills:
integrations/README.md.
Three hooks, three jobs
| Event | When | Job | Tuned for |
|---|---|---|---|
| SessionStart | session opens | build/refresh index, warm the model | availability |
| UserPromptSubmit | before work | seed context with relevant files | recall |
| PostToolUse | after an edit | report prose describing the change | precision |
The recall/precision split is deliberate. A missed file makes an agent do incomplete work confidently — worse than searching slowly. A false surface cries wolf, and a noisy hook gets muted within a week. Same graph, opposite tuning.
Both fail silent: no index, tool missing, timeout or stale data prints nothing and leaves your session untouched.
How it works
PROSE CODE
┌──────────────────────┐ ┌──────────────────────┐
│ landing page copy │───── describes ─│ runMigrations() │
│ docs/architecture.md │═════ declared ══│ getSession() │
│ README › Themes │───── semantic ──│ ThemeProvider │
└──────────────────────┘ └──────────────────────┘
═══ corroborated ─── guess, awaiting confirmationProse is a node type. A heading and its body, a JSX text run, a doc comment — indexed, linked to the symbols it describes, traversed like any other edge.
Three properties keep it honest:
Provenance is never blended. An edge from a resolved import and an edge guessed from similarity are different kinds of thing. Structural and semantic results come back in separate buckets; a guess never renders as a fact.
Confidence is calibrated, not raw. Embedding similarity is compressed — a cosine of 0.50 means the link is right ~98% of the time. Reporting the raw number would say "coin flip" about a near-certainty. Brainer stores the score and reports the measured probability.
Guesses become facts. brainer confirm writes a marker into the file, so a
link you approved once is never guessed again. Precision climbs with use instead
of being frozen at whatever the model gives you.
Commands
brainer context "<task>" # files a task touches — recall-tuned seed set
brainer surfaces <symbol> # what prose describes this?
brainer impact <file> # blast radius, bucketed by provenance
brainer trace <from> <to> # how does A reach B?
brainer review # walk unconfirmed links, y/n/skip
brainer doctor # stale index, orphaned links, hubs
brainer serve # the graph, in a browserEvery command takes --json.
Measured, not asserted
Verified against a 103-file TypeScript repo and a separate marketing site, with a hand-labelled golden set.
| | |
|---|---|
| Link recall@30 | 0.885 |
| Link precision@3 | 0.677 (hand-adjudicated) |
| Agent context recall | 94% shipped (100% in the spike) vs grep's 48% |
| Import resolution | 100% (162/162, incl. tsconfig aliases) |
| impact() query | 0.05ms worst case |
| Incremental reindex | 15× faster than full |
| Always-on overhead | 36 tokens/turn |
Two probes failed before they passed, and a cross-encoder reranker was cut for making precision worse (0.391 → 0.198). Full methodology, including what we got wrong: docs/VERIFICATION.md.
Honest limitations
- Renames break confirmed links.
doctorreports them; automatic rename detection is deferred. - 59% of real links have no corroborating identifier and stay labelled guesses until reviewed. The review flow is load-bearing, not a nicety.
- Some prose has no code referent. ~1/6 of a landing page describes the project as a whole. Returning nothing is correct, and Brainer returns nothing rather than inventing a link.
- Cross-file call resolution is heuristic without a type checker, and labelled as such.
- The context seed set is not exhaustive. It is a starting point.
Language support
TypeScript, TSX, JavaScript, JSX. Markdown, MDX and HTML for prose. Grammars sit behind a registry, so more languages are a contribution, not a fork.
Brainer deliberately does not chase a 60-language count. Breadth is the competition's differentiator. Prose is ours.
Releasing
Publishing runs on npm trusted publishing
(OIDC), so there is no NPM_TOKEN secret to leak or rotate. GitHub Actions
requests a short-lived identity token, npm checks it against the trusted-publisher
rule for this repository, and publishes if it matches.
npm version patch # or minor / major
git pushThat is the whole release. release.yml sees a version with no matching tag on
main, runs the tests, tags it, cuts a GitHub release, and publishes.
Release and publish are one workflow, publish.yml, because npm trusted
publishing validates the entry-point workflow filename. Splitting them cannot
work: a release cut with GITHUB_TOKEN raises no event for another workflow to
catch, and a reusable workflow presents the caller's filename in the OIDC
claim. One file, named what npm trusts, is the only arrangement where a merge
publishes by itself.
A merge whose version is unchanged releases nothing, on purpose — a typo fix should not burn a version number, and npm versions cannot meaningfully be unpublished.
First publish is manual. npm cannot create a new package over OIDC: a trusted-publisher rule can only attach to a package that already exists. So the first version goes up by hand, and everything after it is automatic.
With a passkey (WebAuthn) as your second factor there is no six-digit code to
pass to --otp. Use a current npm, which falls back to a browser flow:
npx npm@latest publish --access publicIt prints a npmjs.com/auth/cli/… URL, waits while you authenticate with the
passkey, and then completes. This needs a real terminal — npm redacts that URL
when stdout is not a TTY, so it cannot be driven from a script. npm 10 does not
offer the fallback at all and only accepts --otp.
Then, on npmjs.com: the package → Settings → Trusted Publisher → repository
stack256org/brainer, workflow publish.yml. After that, releases publish
themselves and no token is ever stored.
workflow_dispatch runs the same job with --dry-run by default, so the whole
path can be exercised without shipping anything.
Status
v0.1.0 — working. Indexes, links, queries, serves and installs. Verified against a real 127-file corpus: 281 symbols, 209 prose blocks, 2684 edges in 7.7s.
The confirmation ratchet is live: review, confirm and reject are built
and tested, so a guess you approve is written into the file as a marker and
read back as ground truth — precision now climbs with use rather than sitting
wherever the model left it. trace and doctor are built too.
Still outstanding: true per-file incremental reindex. reindex rebuilds.
Usage · Design · Verification · Integrations
Part of Stack256 — 256 open-source applications you can host yourself.
