Source of truth for the code every brana cockpit mod carries (allowlist, run, state, probe, snapshot). Never installed: the engine refuses imports outside a…

Every Claude Code session, your model re-infers what it already knew: your stack, your naming conventions, your past decisions. This invisible per-session cost is the knowledge retrieval tax — and it compounds.
Brana eliminates it. A plugin for Claude Code that accumulates your engineering conventions, corrections, and decisions — so each session starts from where the last one ended, not from zero.
Each session, brana captures what went wrong and what worked. Next session, those patterns surface automatically before you start. Corrections don't repeat. Patterns proven across 3+ sessions get promoted and recalled with higher confidence than new ones.
Without brana: Claude resets every session — re-infers your stack, re-learns your conventions, repeats the same mistakes. With brana: Claude remembers your corrections, follows your conventions, and gets harder to fool over time.
Skills, rules, hooks, and agents that enforce the loop automatically:
curl -fsSL https://raw.githubusercontent.com/martineserios/thebrana/main/install.sh | bash
Or manually:
git clone https://github.com/martineserios/thebrana.git ~/brana
cd ~/brana && ./bootstrap.sh
Restart Claude Code. The plugin loads automatically.
Custom install directory: BRANA_DIR=~/my-dir bash install.sh
/brana:build "add user authentication" -- auto-detects strategy (feature, bug fix, refactor...)
/brana:backlog -- manage tasks
/brana:close -- end session, capture learnings
/brana:build detects what you're doing -- feature, bug fix, refactor, spike, migration, investigation, or greenfield -- and runs the right workflow. TDD enforced, docs included.
Hooks capture corrections, test writes, and failure cascades. /brana:close extracts patterns. Next session, they're recalled automatically. Confidence-weighted: new learnings start quarantined, proven ones surface first.
/brana:challenge runs an Opus-powered adversarial review with four flavors: pre-mortem, simplicity challenge, assumption buster, adversarial user. Auto-triggers after plan mode.
Rules are active constraints, not suggestions. The PreToolUse hook blocks implementation writes on feat/* branches until a spec or test exists. The worktree gate prevents accidental commits to main. The doc gate blocks commits that change behavior without updating docs. You can't skip the fundamentals because they aren't optional.
Organized by job:
| Job | Skills |
|---|---|
| DECIDE | backlog, brainstorm, sitrep, challenge |
| UNDERSTAND | research, onboard, memory, notebooklm-source |
| BUILD | build, align, docs, reconcile |
| SHIP | ship, review, client-retire |
| CAPTURE | close, retrospective, log, gsheets, export-pdf |
| Tools | acquire-skills, plugin, scheduler, rust-skills, mcp-builder |
All skills are invoked as /brana:<name>. See Skill Reference for full details.
| Agent | Model | Auto-fires when |
|---|---|---|
| memory-curator | Haiku | Starting work, familiar problem, stuck |
| client-scanner | Haiku | New client, project health check |
| venture-scanner | Haiku | New business project |
| challenger | Sonnet | Plan or architecture decision forming |
| debrief-analyst | Opus | End of implementation session |
| scout | Haiku | Research tasks (spawned by skills) |
| archiver | Haiku | Retiring a client |
| daily-ops | Haiku | Session start on venture project |
| metrics-collector | Haiku | Business reviews |
| pipeline-tracker | Haiku | Pipeline tracking, deal events |
| pr-reviewer | Sonnet | PR creation (auto-triggered) |
All agents are read-only. See Agent Reference for full details.
| Section | Contents |
|---|---|
| Reference | Complete specs: skills, hooks, agents, rules, commands, scripts, configuration |
| Guide | Getting started, configuration, workflows (build, research, session, capture, learn, venture), troubleshooting |
| Architecture | Overview, plugin structure, extending (skills, hooks, agents), ADRs |
| Doc Index | Full index of all documentation |
Brana is structured as four layers:
| Layer | What it is | Examples |
|---|---|---|
| Commands | Skills invoked as /brana:* slash commands | build, backlog, research, close |
| Specialists | Agents that auto-fire for specific work types | challenger, pr-reviewer, debrief-analyst |
| Fabric | Hooks enforcing rules automatically | spec gate, worktree guard, learning capture |
| Memory | Cross-session pattern storage and recall | corrections, proven patterns, session state |
Physically, these live in two places:
Plugin (loaded by Claude Code) Identity layer (~/.claude/)
+-- skills/ -> /brana:* commands +-- CLAUDE.md -> who Claude is
+-- hooks/ -> automatic behaviors +-- rules/ -> behavioral rules
+-- agents/ -> specialized sub-agents +-- scripts/ -> helper scripts
+-- commands/ -> agent commands +-- scheduler/ -> scheduled jobs
The plugin is the toolkit — what Claude can do. Loads via Claude Code's plugin system.
The identity layer is the foundation — how Claude thinks. Deploys once via bootstrap.sh.
For contributors:
git clone https://github.com/martineserios/thebrana.git
cd thebrana
claude --plugin-dir ./system # edits take effect on next session
Optional: Node.js v20+ (MCP integrations), claude-flow (cross-client memory search)
v1.0.0 | Changelog
| Version | Milestone |
|---|---|
| v1.0.0 | Marketplace publication, full documentation |
| v0.7.0 | Plugin packaging, namespace migration, bootstrap.sh |
| v0.6.0 | Unified repo (enter + thebrana merged) |
| v0.5.0 | Project alignment, venture management |
| v0.4.0 | Validation, context budget, self-documentation |
| v0.3.0 | Learning loop, knowledge health |
| v0.2.0 | Hook system (session start/end, spec-first gate) |
| v0.1.0 | Skills, rules, deploy scripts |
See CONTRIBUTING.md for setup, branch naming, and PR process. Issues tagged good first issue are a great starting point.
<!-- Add yourself here when your first PR is merged -->
See SECURITY.md.
MIT
hooks/register.ts 29 lines1import type { EngineInterface, Register } from 'claude-code'
2
3import { DEFAULT_TIMEOUT_MS, guard } from './run'
4
5// cockpit-shared is never installed. It exists so `claude plugin test mods/_shared` runs
6// the *.test.ts beside it (the engine skips a folder with no hooks module: exit 0, no tests
7// run — t-3446) and so the canonical adapter line below is exercised through the real
8// engine. The files here are the source of truth, vendored into mods/<mod>/hooks/_shared/.
9//
10// The adapter line, byte-for-byte what every mod carries at its top level (run.ts explains
11// why it cannot live in a shared file; Check 77a allows `$.process.run(` nowhere else):
12const run = ($: EngineInterface, argv: readonly string[]) => guard(argv, () => $.process.run(argv, { timeoutMs: DEFAULT_TIMEOUT_MS }))
13
14const COMMAND = 'cockpit-shared-run'
15
16export const register: Register = on => {
17 on('session.start', async ($, e, next) => {
18 // Dev aid when loaded with --plugin-dir mods/_shared: `/cockpit-shared-run brana backlog get t-1`
19 // prints the guard's verdict and result as JSON. In tests it is the handle that drives run().
20 await $.command.register({ name: COMMAND, description: 'cockpit-shared dev aid: run an argv through the allowlist guard; prints the RunResult as JSON' })
21 return next(e)
22 })
23
24 on('command.run', { command: COMMAND }, async ($, e) => {
25 const argv = (e.args ?? '').split(/\s+/).filter(Boolean)
26 return { text: JSON.stringify(await run($, argv)) }
27 })
28}
29hooks/run.ts 36 lines1import type { ProcessRunResult } from 'claude-code'
2
3import { check } from './allowlist'
4
5// The one gate every host command passes through (ADR-096 Law 3).
6//
7// Why guard(argv, exec) and not run($, argv): the engine's scanner refuses a hooks module
8// that passes `$` to a function it did not declare at its own top level ("$ is passed to
9// "run", which is not a function declared at the top of this file"), so a shared
10// $-taking run() is impossible by construction. Instead every mod's hooks module carries
11// exactly this one adapter line at its top level — validate Check 77a allows
12// `$.process.run(` nowhere else in mods/**:
13//
14// const run = ($: EngineInterface, argv: readonly string[]) => guard(argv, () => $.process.run(argv, { timeoutMs: DEFAULT_TIMEOUT_MS }))
15//
16// `plugin validate` then reports the call as `$.process.run (via run)`, which Check 77b
17// parses against the allowed set.
18export type RunResult =
19 | { denied: true; reason: string }
20 | { denied: false; failed: boolean; exitCode: number; stdout: string; stderr: string }
21
22export const DEFAULT_TIMEOUT_MS = 15000
23
24export async function guard(argv: readonly string[], exec: () => Promise<ProcessRunResult>): Promise<RunResult> {
25 const verdict = check(argv)
26 if (!verdict.allowed) return { denied: true, reason: verdict.reason }
27 try {
28 const r = await exec()
29 return { denied: false, failed: false, exitCode: r.exitCode, stdout: r.stdout, stderr: r.stderr }
30 } catch (err) {
31 // Cannot start (brana not on PATH), timed out, or the host refused: a result, never a
32 // throw — a hook that throws is skipped and the band draws nothing.
33 return { denied: false, failed: true, exitCode: -1, stdout: '', stderr: String(err instanceof Error ? err.message : err) }
34 }
35}
36hooks/allowlist.ts 30 lines1// The only place verbs are named (ADR-096 Law 3; cockpit.md §_shared/allowlist.ts).
2// Prefix match on argv: every token of a listed prefix must equal the argv token at the
3// same position; extra args are allowed only after a listed prefix. No shell is ever
4// involved ($.process.run takes argv), so a metacharacter inside one token is just text.
5//
6// READ changes only by spec: the valves verb joins in t-3021's landing commit, nowhere
7// else. allowlist.test.ts asserts both lists by exact equality.
8export const READ: readonly (readonly string[])[] = [
9 ['brana', 'cockpit', 'snapshot', '--json'],
10 ['brana', 'backlog', 'get'],
11 ['brana', 'backlog', 'query'],
12 ['brana', 'backlog', 'next'],
13 ['brana', 'backlog', 'search'],
14 ['brana', 'backlog', 'blocked'],
15]
16
17// Automatic-and-logged; the only write without a confirm button (cockpit.md).
18export const WRITE: readonly (readonly string[])[] = [['brana', 'cockpit', 'log-event']]
19
20export type Verdict = { allowed: true; kind: 'read' | 'write' } | { allowed: false; reason: string }
21
22const startsWith = (argv: readonly string[], prefix: readonly string[]): boolean =>
23 argv.length >= prefix.length && prefix.every((tok, i) => argv[i] === tok)
24
25export function check(argv: readonly string[]): Verdict {
26 if (READ.some(p => startsWith(argv, p))) return { allowed: true, kind: 'read' }
27 if (WRITE.some(p => startsWith(argv, p))) return { allowed: true, kind: 'write' }
28 return { allowed: false, reason: `argv not on the READ or WRITE allowlist: ${argv.join(' ')}` }
29}
30