--- name: bruhs description: End-to-end development lifecycle for AI coding agents — scaffold projects, plan and build features, create Linear tickets, open and review PRs, clean up after merge, audit codebases, and improve UI polish/motion quality. Works with any agent that reads CLAUDE.md or AGENTS.md. Use when starting a project, implementing a feature, shipping code, addressing PR reviews, merging, auditing code quality, designing or reviewing UI polish, or reviewing animations. --- # bruhs ## Index |commands|spawn,claim,cook,verify,yeet,walk,land,peep,dip,slop,doodle,deepen,drill,recap |practices|engineering-principles,type-driven-design,architecture-deepening,testing-infrastructure,source-ground-truth,_common,writing,design-docs,pr-review,typescript-react,typescript-hono,python,python-fastapi,effect-ts,effect-*,rust,rust-*,ui-design,design-engineering |config|CLAUDE.md + AGENTS.md (`bruhs:state` and `bruhs:rules` blocks) ## Commands Quick Reference |spawn|Create project or add to monorepo|commands/spawn.md |claim|Initialize config for existing project|commands/claim.md |cook|Plan + Build feature end-to-end|commands/cook.md |verify|Prove a claim with falsifiable evidence (VERIFIED/NOT VERIFIED/INCONCLUSIVE)|commands/verify.md |yeet|Ship: Linear ticket → Branch → Commit → PR|commands/yeet.md |walk|Generate reviewer walkthrough (markdown or interactive HTML canvas)|commands/walk.md |land|Watch PR checks and iterate on CI failures until green|commands/land.md |peep|Address PR review comments, or self-review with linked context, evidence gates and a ship verdict, then merge|commands/peep.md |dip|Clean up after merge, switch to base branch|commands/dip.md |slop|Deep codebase analysis, AI slop cleanup (--quick for branch-diff only)|commands/slop.md |doodle|Visualize architecture as tldraw diagrams (PRs, modules, deps, compare, map, freeform)|commands/doodle.md |deepen|Find shallow modules, propose architectural deepenings (explore → candidates → grill)|commands/deepen.md |drill|Find missing/weak test-infra layers, propose adoptions (explore → candidates → grill)|commands/drill.md |recap|Status update from git log over a time window (bugfix/tech-debt/net-new)|commands/recap.md ## Lifecycle Map Commands compose into an end-to-end loop. Typical flow: ``` spawn / claim → project setup ↓ cook → plan + build ↓ verify → prove the behavior changed (recommended for fixes) ↓ slop --quick → clean up branch-diff slop before shipping ↓ yeet → Linear ticket + branch + commit + PR ↓ walk --post → reviewer walkthrough comment (large PRs) ↓ land → watch CI to green ↓ peep → address review comments (compose with land via --land) ↓ dip → post-merge cleanup ``` Out-of-band, any time: - `slop` — deep codebase audit - `doodle` / `deepen` / `drill` — structural / test-infra investigation - `recap` — status update for standup or retro ## Invocation - `/bruhs` → Interactive menu (AskUserQuestion) - `/bruhs:` → Direct to command - `/bruhs:cook ` or `/bruhs:cook TICKET-123` → With argument - `/bruhs:verify [claim | PR#] [--keep-artifacts]` → Falsifiable verification - `/bruhs:walk [PR#] [--canvas|--post|--commit-body]` → Reviewer walkthrough - `/bruhs:land [PR#] [--no-fix|--max-iterations N]` → Watch CI to green - `/bruhs:peep [PR# | TICKET] [--land|--resolve-conflicts]` → Address review comments - `/bruhs:slop [path] [--quick|--fix|--report] [--severity ...]` → Codebase analysis - `/bruhs:doodle [args] [--out|--gist|--commit|--pr-comment|--format|--depth]` → Render diagram (modes: pr, module, deps, dependents, compare, map, freeform) - `/bruhs:deepen [path | module-name] [--no-explore]` → Find shallow modules, propose deepenings - `/bruhs:drill [path | layer-name] [--no-explore]` → Find missing/weak test-infra layers, propose adoptions - `/bruhs:recap [window] [--all-authors|--linear|--branch ]` → Status update from git log --- ## Engineering Doctrine Every command uses this shared vocabulary: |Term|Meaning| |---|---| |**Landmine**|A harmless-looking decision that creates a delayed failure| |**Receipt**|Inspectable evidence or reasoning behind a decision| |**Tripwire**|An executable guardrail that fails actionably when an assumption breaks| |**Simple**|Logic that decomposes naturally and removes complexity| |**Obvious**|Code whose purpose, state, errors, and next change are visible in one pass| The governing rules: - **Think from first principles.** Preserve invariants and user outcomes, not accidental complexity. - **No receipt, no operational number.** Timeouts, retries, memory budgets, concurrency limits, queue bounds, cache sizes, and similar values must be derived from measurements, quotas, capacity math, research, incidents, contracts, or an explicit user requirement. - **Fail loudly and actionably.** State what failed, why, which assumption or limit was violated, and what fixes it. Degraded behavior must be explicit and observable. - **Turn assumptions into tripwires.** Comments describe; validation, constraints, exhaustive handling, assertions, and CI enforce. - **Understand before optimizing.** Remove unnecessary work and pathological shapes first; measure when an optimization introduces a real trade-off. - **Prefer obvious over clever.** Use direct control flow, narrow interfaces, explicit state, and domain language. - **Minimize hidden state.** Make mutation, caching, retries, fallbacks, I/O, and lifecycle ownership visible. - **Design for maintenance.** Delete obsolete code. Add abstractions only when they centralize an invariant, remove duplication, or hide real complexity. Read `practices/engineering-principles.md` before planning, implementing, reviewing, or verifying. It wins when another practice conflicts with it. --- ## Type-Driven Design > A function's type signature should tell you everything about what it does. ### Priority Hierarchy (fix in this order) 1. **Missing/wrong type signatures** - Types ARE documentation 2. **Hidden side effects** - Signature lies about behavior 3. **any** types - Type system disabled 4. **!** or **as** on external data - Compiler trust violated 5. **Fast-path violations** - `await` in loops, N+1 queries, sync-in-async, per-request client construction (see Performance below) 6. **Other implementation issues** - Secondary to type correctness ### Checklist **Signatures:** - [ ] Explicit return types on public functions - [ ] No **any** - use unknown + validation - [ ] No **!** - handle null explicitly - [ ] No **as** for external data - validate instead - [ ] Errors in return type, not thrown silently - [ ] **readonly** parameters signal no mutation - [ ] Discriminated unions for state (not multiple booleans) **Errors:** - [ ] Errors visible in return type (Result or union) - [ ] Typed errors, not strings - [ ] Handle at call site, not deferred - [ ] No empty catch blocks **Immutability:** - [ ] Prefer **const** over **let** - [ ] Don't mutate parameters - [ ] Return new objects instead of mutating ### Patterns ```typescript // ❌ Signature hides truth function getUser(id: string): User // might throw, might be null // ✅ Signature tells full story function getUser(id: string): Promise> // ❌ Multiple booleans = impossible states type State = { isLoading: boolean; isError: boolean; data: User | null } // ✅ Discriminated union = only valid states type State = | { status: 'idle' } | { status: 'loading' } | { status: 'success'; data: User } | { status: 'error'; error: Error } ``` For full patterns → `practices/type-driven-design.md` --- ## Architectural Deepening > A deep module hides a lot of behaviour behind a small interface. A shallow module's interface is nearly as complex as its implementation. Use the `/bruhs:deepen` command to find shallow modules and propose deepenings. Distinct from `/bruhs:slop` (line-level audit) — this is **structural**. ### Vocabulary (use these terms exactly) |Term|Meaning| |---|---| |**Module**|Anything with an interface and an implementation (function, class, package, slice)| |**Interface**|Everything a caller must know — types, invariants, ordering, errors, configuration| |**Depth**|Behaviour-per-unit-of-interface. Deep = lots of behaviour behind a small interface| |**Seam**|A place where you can alter behaviour without editing in that place (Feathers)| |**Adapter**|A concrete thing that satisfies an interface at a seam| |**Leverage**|Caller-side benefit of depth — capability per unit of interface learned| |**Locality**|Maintainer-side benefit — change/bugs/tests concentrate in one place| ### Core principles - **Depth is a property of the interface, not the implementation.** Internal seams are fine; don't expose them. - **The deletion test.** Imagine deleting the module. If complexity vanishes → pass-through, merge it. If it scatters across N callers → the module earns its keep. - **The interface is the test surface.** If you want to test *past* it, the module is the wrong shape. - **One adapter means a hypothetical seam. Two adapters means a real one.** Don't add a port for a single implementation. ### When to invoke `/bruhs:deepen` - A codebase feels like it bounces between many small files for one operation - Tests feel like they're testing the wrong thing (mocks everywhere, brittle to refactors) - Callers must learn implementation details (transport, encoding, storage) to use a "domain" module - Same invariant repeats across N call sites - During `/bruhs:slop` you keep flagging "architecture" — promote to `/bruhs:deepen` for structural treatment For full glossary, dependency categories, seam discipline, design-it-twice mechanics → `practices/architecture-deepening.md` --- ## Testing Infrastructure > A test suite is a layered safety net. Each layer catches a different bug class. A missing layer isn't covered by the others — it's just where bugs ship from. Use the `/bruhs:drill` command to find missing or weak layers and propose adoption plans. Companion to `/bruhs:deepen`: deepen modules, drill tests. ### The 8 layers |#|Layer|Catches| |---|---|---| |1|**Acceptance specs**|Intent drift — no executable definition of "done"| |2|**Unit tests**|Regressions in module behaviour at the interface| |3|**Coverage gate**|Untested code landing (measured on **changed lines per PR**, not total)| |4|**Mutation runner**|Tests that exist but assert nothing meaningful — the **kill ratio** is the only number that proves coverage is doing work| |5|**Complexity composite**|High-complexity / low-coverage hotspots (the worst-of-both — Savoia's "CRAP")| |6|**Architecture checker**|Layer / dependency rule violations — encodes ADRs so they're enforced, not just documented| |7|**Test-code linter**|Rot in test files themselves; AI-generated test code becoming a second untyped codebase| |8|**CI orchestrator**|Discipline collapse — runs every layer as hard gates. The 8th is load-bearing| ### Core principles - **Hard gate, advisory, or ratchet — pick one, defend it.** Indefinite advisory is decoration. Ratchet ("no worse than baseline") is honest about legacy debt and locks in improvement. - **The orchestrator is load-bearing.** Layers 1–7 without a real CI gate are theatre. `continue-on-error: true` on a gated layer is advisory regardless of what the config claims. - **Replace, don't layer.** Old advisory checks become waste once a hard gate exists. Old shallow-module tests become waste once the deeper module's interface is tested. Delete; don't preserve out of guilt. - **The "what would slip through" test.** Don't propose adopting a layer unless you can name a concrete bug class it catches in *this* codebase. ### When to invoke `/bruhs:drill` - Tests pass but bugs still ship — the suite isn't catching what it claims to catch - Coverage is high but feels meaningless (no mutation kill ratio measured) - ADRs exist but get violated; "clean architecture" decisions erode unenforced - AI-generated test files are accumulating without review - CI is green; the build still feels unreliable — likely an advisory orchestrator - During `/bruhs:slop` you keep flagging "no test for X" — promote to `/bruhs:drill` for safety-net treatment For full layer specs, diagnostics, gating posture, rejected framings → `practices/testing-infrastructure.md` --- ## Common Rules ### Naming |Type|Convention|Example| |---|---|---| |Components|PascalCase|`UserCard.tsx`| |Hooks|camelCase + `use`|`useAuth.ts`| |Utilities|camelCase|`formatDate.ts`| |Constants|SCREAMING_SNAKE|`MAX_RETRIES`| |Booleans|is/has/can/should|`isLoading`, `hasPermission`| |Functions|verb + noun|`getUser`, `createOrder`| |Handlers|handle/on prefix|`handleSubmit`, `onUserClick`| ### Code Organization - Functions do ONE thing - If you need comments to separate sections → extract functions - Treat length as a signal, not a rule. Split only when doing so makes responsibilities and state more obvious. - Do not introduce line-count limits without a repository-specific receipt. ### Error Handling - Specific messages: `User "${id}" not found` not `Something went wrong` - Validate at boundaries, trust internal code - Never swallow errors (empty catch blocks) ### Git ``` : Fixes TICKET-123 Assisted-by: Claude:claude-opus-5-5 ``` Types: `feat|fix|refactor|chore|docs|test` Every agent-written commit (including land/peep/doodle fixups) carries `Assisted-by: :` with the running model, never `Co-Authored-By`. Details → `practices/_common.md` → *AI Attribution*. Branch: `/-` Stacked PRs → `commands/yeet.md` → *Stacked PRs*. ### Comments - ✅ WHY (business logic, workarounds, non-obvious decisions) - ❌ WHAT (code already says what it does) - ❌ Commented-out code - ❌ TODO without ticket reference - ❌ Plan IDs, people, line numbers (provenance goes in the commit or PR body) ### Writing Anything a person reads (PR bodies, tickets, review replies, commits, recaps): fewest words that keep the meaning, point first, lowercase in chat and sentence case in documents, no AI text tells. Scan `practices/writing.md` before sending. Design docs: 1–3 pages, the problem explained cold with evidence, one labelled diagram, only hard-to-reverse decisions. Shape and gates → `practices/design-docs.md`. ### External Searches Always include current year in WebSearch queries for fresh results. For full guidelines → `practices/_common.md` --- ## Performance (Fast-Path-By-Default) > Pick the fast path by default. You don't need a benchmark to avoid an anti-pattern. ### Philosophy - **Correctness first, but equivalent-correctness patterns are not equivalent.** When two patterns solve the same problem, pick the faster one — even without a measurement. - **Obvious perf wins don't require benchmarks.** N+1 queries, `await` in a loop, sync I/O on the event loop, per-request client construction — these are anti-patterns, not "premature optimization." - **Measure when the tradeoff is real** (readability cost, complexity cost, risk of regression). Don't measure to justify avoiding an anti-pattern. - **Measure at boundaries, not inside** — p50/p95/p99 at ingress/egress; internal timers lie about contention. - **Operational bounds need receipts.** Never copy a concurrency, timeout, retry, or cache value from these examples into production without deriving it from the real system. ### Universal Defaults - **Batch at boundaries.** One round-trip of N beats N round-trips (DB, HTTP, IPC, GPU, syscalls). - **Stream, don't buffer.** Start bytes moving before you have them all — SSR, JSON, file I/O, LLM tokens. - **Don't block the hot loop.** Event loop, render thread, request handler — push CPU off, I/O async, never sync-in-async. - **Colocate data with consumer.** Query next to its route, fetch next to its RSC, cache next to its reader. - **Built-ins beat rewrites.** `Array.prototype`, `itertools`, `std::collections`, platform `fetch`. - **Bound concurrency on user input.** `p-limit`, `asyncio.Semaphore`, `buffer_unordered(n)` — unbounded `Promise.all` over user data is a DoS. - **Singleton clients.** Never `new Client()` per request — it defeats connection pooling. ### LLM-Common Traps (flag these in review) 1. `await` in a `for` loop over independent items → `Promise.all` / `gather` / `join!` 2. `.map().filter().reduce()` chains over large arrays → one pass or lazy iterator 3. N+1 ORM access in "clean" code → eager/join loading 4. `useMemo`/`useCallback` sprinkled without a measured re-render problem 5. `JSON.parse(JSON.stringify(x))` for deep clone → `structuredClone` or targeted copy 6. Unbounded `Promise.all` / recursion over user input → clamp concurrency 7. Sync hashing/crypto in request path → worker / threadpool 8. Per-request client construction (`new PrismaClient`, `httpx.AsyncClient()`, `reqwest::Client::new()`) → singleton 9. Full-body logging in hot paths → structured log fields 10. Parsing config / reading env per request → load once at boot Per-stack performance patterns → `practices/.md` (Performance section in each) --- ## Source as Ground Truth > Code is the best ground truth over docs. Whenever the work touches a third-party package, repo, or dependency, resolve its real behavior from the **installed source**, not from training memory or docs that may have drifted. Docs orient; the source decides. When they disagree, the version in the lockfile wins. Use [`opensrc`](https://github.com/vercel-labs/opensrc) to pull a package's source on demand: ```bash npm install -g opensrc rg "parse" $(opensrc path zod) # search the real implementation cat $(opensrc path zod)/src/types.ts # read a specific file find $(opensrc path pypi:requests) -name "*.py" # non-npm via pypi: prefix ``` Reach for it before citing an API you're unsure of, when docs are thin or contradict behavior, when an error points into a dependency, or when a review finding hinges on what a library actually does. Reading source is not running it — execute untrusted / AI-generated code in a sandbox, never your host (see Config → stack `sandboxing`). For full guidance (version-matching, context7-vs-source, fallbacks) → `practices/source-ground-truth.md` --- ## Command Details Each command's full workflow lives in its own file. Read the specific file when executing that command: | Command | File | |---|---| | cook | `commands/cook.md` | | verify | `commands/verify.md` | | yeet | `commands/yeet.md` | | walk | `commands/walk.md` | | land | `commands/land.md` | | peep | `commands/peep.md` | | dip | `commands/dip.md` | | spawn | `commands/spawn.md` | | claim | `commands/claim.md` | | slop | `commands/slop.md` | | doodle | `commands/doodle.md` | | deepen | `commands/deepen.md` | | drill | `commands/drill.md` | | recap | `commands/recap.md` | --- ## Interactive Menu When `/bruhs` is invoked without arguments, present the command list via `AskUserQuestion` and route to the selected command's file. Options: spawn, claim, cook, yeet, peep, doodle, dip, deepen, drill. `slop` is intentionally excluded from the menu — invoke directly with `/bruhs:slop`. --- ## Config Reference Project state lives in two marker-bounded blocks inside `CLAUDE.md` **and** `AGENTS.md` (mirrored). The model reads them every session — no separate config file to keep in sync. Hand-written rules live outside the blocks and are never touched. ### Block 1 — `bruhs:state` (auto-maintained JSON) ```markdown ### Project State (managed by /bruhs) ```json { "integrations": { "linear": { "mcpServer": "linear-", "team": "", "teamName": "Team Name", "project": "", "projectName": "Project Name", "labels": { "feat": "Feature", "fix": "Bug", "chore": "Chore", "refactor": "Improvement" } } }, "tooling": { "mcps": ["linear", "notion", "context7"], "skills": ["superpowers", "shadcn", "vercel-react-best-practices"] }, "stack": { "structure": "single|monorepo", "framework": "nextjs|tanstack-start|astro|hono|...", "styling": ["tailwind", "shadcn"], "database": ["drizzle-postgres", "convex", "..."], "auth": "better-auth|clerk|...", "gpu": ["modal", "runpod", "lambda"] } } ``` ``` ### Block 2 — `bruhs:rules` (stack-derived behavioral rules) ```markdown ### Stack-Specific Rules (managed by /bruhs) #### Convex - Convex actions that import the `ai` SDK MUST live in a file whose first line is `'use node'`. - ... ``` Rules are derived from the detected stack via `scripts/derive_stack_rules.py` — short, high-signal, and **only** stack-specific. Universal behavioral rules (the kind that map to *your* failure modes) should be hand-written by you, outside the bruhs blocks. ### Scripts | Script | Purpose | |---|---| | `scripts/sync_bruhs_block.py` | Atomically write a `bruhs:state` or `bruhs:rules` block into CLAUDE.md + AGENTS.md. | | `scripts/read_bruhs_block.py` | Read the JSON state (or rules markdown) back out. Falls back to legacy `.claude/bruhs.json` for `--kind state` during the transition. | | `scripts/derive_stack_rules.py` | Given state JSON on stdin, emit the markdown rules body to pipe into `sync_bruhs_block.py --kind rules`. | ### Migration from `.claude/bruhs.json` Legacy `.claude/bruhs.json` is still read by `read_bruhs_block.py` as a fallback. `/bruhs:claim` detects the legacy file and offers to port it into the marker blocks, then prompts before deleting the old file. --- ## Linear MCP Tool format: `mcp____linear_` Common tools: |Tool|Purpose| |---|---| |`linear_get_teams`|List teams (includes labels)| |`linear_get_user`|Current user (for assigneeId)| |`linear_create_issue`|Create ticket| |`linear_edit_issue`|Update status| |`linear_get_issue`|Fetch ticket by ID| Multi-workspace: Each project's `bruhs:state` block points to its Linear workspace via `mcpServer`. Example: ```javascript // Load the tool ToolSearch(`select:mcp__${config.integrations.linear.mcpServer}__linear_create_issue`) // Create issue const issue = call(`mcp__linear-sonner__linear_create_issue`, { title: "Add leaderboard", teamId: config.integrations.linear.team, projectId: config.integrations.linear.project, assigneeId: user.viewer.id, labelIds: [labelId] }) // Use Linear's generated branch name const branchName = issue.gitBranchName // "sonner-140-add-leaderboard" ```