No description
  • Shell 80%
  • JavaScript 20%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Lynx 646941e448 chore: close the gitleaks red; add it to the gate
gitleaks reported 2 leaks on this repo and nothing was looking, because
`gitleaks detect` was never in CLAUDE.md `## Run` — even though
rules/quality-gate.md names it as the any-stack half of the security check.
A mandated check that no one runs is the "skip that reads as passed" the same
rule file warns about.

Both findings are false positives, reviewed rather than waved through: the
SonarQube web API takes its token as the HTTP basic-auth *username* with an
empty password, so /sonar documents `curl -sf -u "$TOKEN:"`. gitleaks'
curl-auth-user rule matches the flag shape; the value is a shell variable read
from the environment. Verified verbatim at 1ef45e21:commands/sonar.md:61,66 —
both are historical, since gitleaks scans commit diffs and the skills/ rename
never re-added those lines.

Suppressed by fingerprint in .gitleaksignore, not by a regex allowlist, so the
two occurrences are frozen and nothing new can hide behind them: re-adding the
pattern in a future commit yields a NEW fingerprint and gets flagged, which is
the behaviour we want. Proved it still bites — cloned the repo with the ignore
file in place, planted an AWS key and a GitHub token, and gitleaks exited 1.

CLAUDE.md `## Run` now lists five gate checks, not four. 90 lines, up 2 and
still over the under-80 rule; a working security check is worth more than the
line count, and the overage stays flagged rather than fudged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Planned-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 21:08:51 +02:00
agents feat(spec): add /spec command for cold-executor specs + plan-reviewer Lens 3 2026-07-24 18:51:40 +02:00
docs docs(plan): 0001 phase-4 done (dfa9893) — plan complete 2026-08-15 20:22:11 +02:00
plugins fix(enforce-research): make block a real gate; fix three heuristic bugs 2026-08-14 21:50:26 +02:00
rules docs(workflow): add context-hygiene section (SCRUB levers, disposable subagents) 2026-08-09 12:15:52 +02:00
scripts fix(opencode): yolo wraps documented --auto flag; becomes tracked repo file 2026-08-04 15:10:30 +02:00
skills feat(skills): move plan/spec/scaffold/handoff to skills; deny all five in OpenCode 2026-08-15 20:10:14 +02:00
templates feat(statusline): show unpushed commit count; session start reports it 2026-08-04 20:03:14 +02:00
tests chore: delete CI; the harnesses are the gate now 2026-08-15 20:26:53 +02:00
.env.example chore: handoff — drain notes, update status 2026-08-14 22:17:23 +02:00
.gitignore chore: gitignore per-project Claude Code local settings 2026-08-14 21:13:57 +02:00
.gitleaksignore chore: close the gitleaks red; add it to the gate 2026-08-15 21:08:51 +02:00
bootstrap-opencode.sh refactor: retire commands/ — skills are the only authoring format 2026-08-15 20:17:42 +02:00
bootstrap.sh refactor: retire commands/ — skills are the only authoring format 2026-08-15 20:17:42 +02:00
CLAUDE.md chore: close the gitleaks red; add it to the gate 2026-08-15 21:08:51 +02:00
LICENSE chore: MIT LICENSE, drop pacman node/pnpm duplication, document agent: plan frontmatter 2026-08-04 16:23:12 +02:00
NOTES.md chore: handoff — drain notes, update status 2026-08-15 21:07:13 +02:00
README.md refactor: retire commands/ — skills are the only authoring format 2026-08-15 20:17:42 +02:00
sync.sh refactor: retire commands/ — skills are the only authoring format 2026-08-15 20:17:42 +02:00

lean-code

A minimalist Claude Code config that does three things: install, configure, get out of the way.

Heads up — this is not a copy-paste magic script. It's calibrated to my setup, my projects, my mood. There's a Qdrant on my homelab, a llama-cpp endpoint, Sonar, opinions about coffee, and a handful of rules that exist because of incidents (...slops!) you weren't around for. Plan to read every file and swap out whatever doesn't match your reality before you trust any of it. You'll inherit my bugs otherwise!

Why this exists

The big agent-config frameworks — pick whichever one your feed is pushing this month — ship skills, agents, hooks, MCP servers, a project memory system, slash commands for every imaginable workflow, and an opinion about which way the toilet paper goes. It's a lot. Most of it is good, but it's a BEAST. I wanted to understand what's actually happening when Claude reads my repo and decides what to do next, so... so I did the only thing I could do, I asked Claude to tell me about itself.

This is the rebuild from first principles and some other blogs and materials. Strip everything to the bone, then add back only the pieces I can explain to the sharpest knife in the drawer after they've had three beers. If I can't justify a file, the file dies. The result is small enough to read end-to-end in one sitting, opinionated enough to actually matter, and dumb enough that I can debug it with cat and grep while petting the dog.

If you want a fully-featured agentic workshop, install one of the big frameworks — you already know which one. If you want to understand how the moving parts fit together and you like tears and suffer, you can build your own. (You will thank yourself later!)

The three-layer model

The whole thing is three layers.

System layer — bash tools, Claude Code itself, the MCPs I actually use. Installed once per machine by bootstrap.sh. After this runs I have a working AI coding setup. This layer doesn't know anything about my projects.

Global config layer — the rules, skills, and subagents that live in ~/.claude/. Loaded by Claude Code every time it starts, in every project. This is where opinions live: "use uv, not pip", "read before you edit", "tests have tiers, pick one." Three artifact types: a rule is an always-on belief; a skill is a procedure, and one frontmatter line decides who may start it — disable-model-invocation: true makes it a tool I pick up (/plan, /spec, /handoff, /sonar, /scaffold), and without it the agent reaches for it on its own (diagnose, when a bug resists the first fix); an agent is a delegable context (codebase-explorer, plan-reviewer, ui-verifier). Also installed by bootstrap.sh.

Commands used to be a fourth type here. Claude Code merged them into skills — .claude/commands/x.md and .claude/skills/x/SKILL.md both create /x — so this repo did too, and one flag now carries the distinction that used to need its own directory. OpenCode hasn't merged them, which is why sync.sh still derives command files for it. See docs/adr/0001-commands-are-skills.md.

Project layer — per-project state. Three files: CLAUDE.md (durable status the agent reads at session start), NOTES.md (in-session scratch that gets drained by /handoff at session end), and CONTEXT.md (the domain glossary — one word per concept, so naming stays consistent and the agent burns fewer tokens decoding jargon). Plus docs/plans/ — every approved plan, numbered (0001-…/, 0002-…/) and committed to git, so the plan log versions with the code. /plan generates the skeleton for a lib/app (and scaffolds it on accept); /scaffold does it directly for a throwaway script — either way sized to the project's tier: a one-off script gets git + a linter, a full app gets the works.

System assumes nothing (EXCEPT! BTW... I use Arch. And the scripts too!). Global assumes system. Project assumes global. Layers don't reach across.

What's in the box

lean-code/
├── bootstrap.sh                  # one-shot install: tools + Claude Code + global config
├── bootstrap-opencode.sh         # agent-layer add-on: OpenCode CLI + ~/.config/opencode config
├── sync.sh                       # the release button: deploy config edits to this machine (--check reports drift)
│
├── rules/                        # auto-loaded by Claude Code from ~/.claude/rules/
│   ├── core.md                   # discipline (verify/read/search/ask) + YAGNI, standards, security
│   ├── toolchain.md              # mise/uv/podman/pnpm — the modern stack lock-in
│   ├── research.md               # Context7 for docs, SocratiCode for code — never stale memory
│   ├── testing.md                # tiered testing (script / library / app), anti-patterns
│   ├── workflow.md               # session-start ritual, conventional commits, handoff cues
│   ├── quality-gate.md           # native floor (types/lint/security/tests) the agent keeps green
│   └── scoped/                   # path-scoped rules (paths: frontmatter) — Claude Code only;
│       └── expo.md               #   Expo gotchas (version-free by doctrine). OpenCode's glob never descends here
│
├── skills/                       # one type, two invocation models (see the flag on each)
│   │                             # -- user-invoked: disable-model-invocation: true --
│   ├── scaffold/                 # /scaffold — new project sized to its tier (script/lib/app); infers, or consumes a /plan decision
│   ├── plan/                     # /plan — grill, draft, WAIT; persist numbered to docs/plans/, scaffold on accept
│   ├── spec/                     # /spec — deepen ONE plan phase into a cold-executor spec (Context7-pinned) a weak/local model can run; additive, never touches /plan
│   ├── sonar/                    # /sonar — deep SonarQube scan + dashboard, you decide (opt-in)
│   ├── handoff/                  # /handoff — security checkpoint, drain NOTES, update CLAUDE.md, commit
│   │                             # -- model-invoked: the agent reaches for these on its own --
│   ├── diagnose/SKILL.md         # disciplined debugging loop for hard bugs (auto-fires when stuck)
│   ├── mobile-screenshot-harness/ # Android dev-client + headless screenshots — the agent SEES mobile UI
│   ├── stop-slop/                # vendored — strip AI tells from prose (fires when writing/editing)
│   ├── security-checkpoint/      # vendored Anthropic security review — /handoff Phase 2's engine
│   ├── codebase-exploration/     # vendored SocratiCode — search/graph/read a codebase
│   └── codebase-management/      # vendored SocratiCode — index, watch, context artifacts
│
├── agents/                       # subagents the model can delegate to
│   ├── codebase-explorer.md      # vendored SocratiCode — deep multi-file exploration
│   ├── plan-reviewer.md          # independent gap + contradiction review of a drafted plan
│   └── ui-verifier.md            # Playwright browser testing in its own context window
│
├── plugins/                      # OpenCode plugins — JS hooks where prompts aren't enough
│   └── enforce-research.js       # tripwire: new third-party import without a Context7 lookup
│
├── templates/                    # deployed verbatim into ~/.claude
│   ├── CLAUDE.md                 # global non-negotiables salience block (the 8 rules)
│   └── statusline.sh             # context/cost/git/rate-limit statusline
│
└── scripts/
    ├── setup-socraticode.sh      # wire a project into the Socraticode MCP (per-project .mcp.json)
    ├── reset-socraticode.sh      # drop Qdrant collections for a clean SocratiCode re-index
    └── opencode-yolo.sh          # opt-in `opencode --auto` launcher (DANGER — VM/container only)

Install

Two scripts, run in order. Step 1 is required for everyone. Step 2 is optional — add it only if you want OpenCode too.

Configure your endpoints first (.env)

The scripts bake in no host defaults — they stay portable by reading every service URL from the environment, so set these up before you run either script. Copy the example, point it at wherever your services actually live, and source it into the shell you'll run the scripts from:

cp .env.example .env
$EDITOR .env
set -a; . ./.env; set +a   # export everything into this shell
Var Used by Notes
QDRANT_URL, OLLAMA_URL SocratiCode indexing required, no default
EMBEDDING_MODEL SocratiCode optional (defaults to nomic-embed-text)
LLAMACPP_URL, LLAMACPP_MODEL OpenCode local model URL required; model optional
SONARQUBE_URL, SONARQUBE_TOKEN SonarQube MCP server USER token
SONAR_HOST_URL, SONAR_TOKEN /sonar scanner Global Analysis token
CONTEXT7_API_KEY Context7 docs MCP optional (raises rate limits; runs keyless) — the one hosted service here, see Design rules

.env is gitignored — secrets never get committed. Anything you'd rather keep permanent goes in your shell rc instead. Tip: prefer 127.0.0.1 over localhost — rootless podman can resolve localhost to IPv6 and miss an IPv4-only service.

Step 1 — bootstrap.sh

On a fresh, minimal Arch box (no GUI needed):

git clone <this-repo> ~/lean-code
cd ~/lean-code
./bootstrap.sh

This installs the system layer (git, mise, uv, podman, pnpm, Claude Code, the MCPs) and the global config layer — it drops rules/, skills/, agents/, and the templates into ~/.claude/, and copies setup-socraticode + reset-socraticode into ~/.local/bin (add that to your PATH). Run it once per machine.

Think of this script as the single source of truth. Everything downstream — including OpenCode — reuses what it produces, above all the rule files in ~/.claude/rules/.

Step 2 — bootstrap-opencode.sh (optional, adds OpenCode)

Want OpenCode, the open-source agent — alongside Claude Code, or instead of it? Run:

./bootstrap-opencode.sh

⚠️ Run Step 1 first — even if you never plan to use Claude Code. bootstrap-opencode.sh ships no rules of its own. It just points OpenCode at the same ~/.claude/rules/ files Step 1 created. That's the entire point: one set of rules, two agents, maintained in one place — never the same rule copied into two configs. Skip Step 1 and this script preflight-checks, finds no rules, and stops.

The two scripts complement each other: Step 1 owns the system and the rules; Step 2 is a thin adapter that teaches OpenCode to read them (plus an OpenCode-shaped MCP + provider config). After both, claude and opencode behave the same way in every project — same rules, same conventions, same blind spots ignored.

What Step 2 wires up

  • No symlink, no AGENTS.md. OpenCode reads ~/.claude/CLAUDE.md as a native global fallback when no AGENTS.md exists, so both agents share the same inline ruleset for free.
  • ~/.config/opencode/opencode.json with "instructions": ["…/.claude/rules/*.md"] — a single glob OpenCode loads additively on top of the CLAUDE.md fallback. Path-scoped rules (paths: frontmatter, which OpenCode can't honor) live in rules/scoped/ — a glob's * never crosses /, so they're excluded by construction, not by script logic. The config is merged, never overwritten on re-runs: sync owns the machinery keys (instructions, tools, MCP wiring, canonical permission entries), you keep provider, model, mcp.*.enabled toggles, and anything custom.
  • MCP: context7 (docs) + playwright (browser) + sonarqube (code quality, reads SONARQUBE_URL/SONARQUBE_TOKEN), the same trio Claude Code gets. Context7 uses CONTEXT7_API_KEY if set, runs keyless otherwise.
  • Provider preset: a llama-cpp entry (@ai-sdk/openai-compatible) pointing at a LAN host, set as the default model. Edit provider.llama-cpp.options.baseURL + the model id in ~/.config/opencode/opencode.json, override at install time with LLAMACPP_URL=… LLAMACPP_MODEL=… ./bootstrap-opencode.sh, or swap the block for Anthropic / OpenRouter and run opencode auth login.
  • Permissions are three-class in both agents: routine dev commands allowed (git * reads + commits, mise, uv, …), consequential ones ask (git push, git rebase, git reset, curl, podman, …), catastrophic ones denyed (git push --force, rm -rf ~, dd of=/dev/…). Deny rules survive even opencode-yolo--auto honors denies. Claude Code reads them from ~/.claude/settings.json (evaluated deny → ask → allow, first match wins); OpenCode from the permission.bash map (ordered catch-all → specific → denies last; last match wins). Both are written by sync.sh — canonical rule lists win, your additions are preserved, and you can't weaken a deny. OpenCode gets a second map, permission.skill, on the same last-match-wins rules: it denies the five user-invoked skills, because OpenCode ignores disable-model-invocation and would otherwise let its model fire /handoff or /sonar on its own.
  • opencode-yolo (~/.local/bin) — an opt-in launcher wrapping OpenCode's documented --auto flag: every prompt not explicitly denyed is auto-approved, so deny rules still apply (unlike the undocumented env-var hack it replaced). It's a separate command, not an alias — plain opencode keeps the scoped allow-list, and the dangerous choice stays salient at typing time. ⚠️ Edits + bash run unconfirmed — use only in a throwaway VM / container / git worktree.
  • Commands, skills, and the codebase agent. Commands are derived, not copied: sync.sh reads each skills/<name>/SKILL.md and writes ~/.config/opencode/commands/<name>.md with OpenCode's frontmatter and the same body — one source, two agents, the same trick used for agents below. Claude-side keys (model, disable-model-invocation, name) are stripped on the way. Skills need no copy — OpenCode discovers SKILL.md from ~/.claude/skills (a Claude-compatible search path), so diagnose + the SocratiCode skills are visible to its skill tool automatically; the five user-invoked ones are denied there via permission.skill. The codebase-explorer agent is derived into ~/.config/opencode/agents/ in OpenCode's frontmatter format (mode: subagent + a least-privilege permission: block — read/grep/glob/list/skill allowed, edit/bash/webfetch denied) from the same agent body Claude Code uses.
  • SocratiCode + Understand-Anything work with both agents — see Codebase analysis below.

Releasing config edits — sync.sh

bootstrap.sh builds the factory; sync.sh ships the release. Both bootstraps call it, so first install and day-to-day deploys can never diverge. After editing anything in rules/, skills/, agents/, templates/, or plugins/:

./sync.sh           # deploy to this machine (~/.claude + ~/.config/opencode)
./sync.sh --check   # report drift (stale/missing/orphan), change nothing, exit 1 on any

Nothing is removed silently: a deployed file with no repo counterpart is reported as an orphan and deleted only after a y/N. Each deploy stamps ~/.claude/.lean-code-version with the repo sha, so "which release is live here?" is a cat away.

Use it

Which model runs which command. rules/workflow.md's Model Roles section settles it: /plan and /spec are planner-only — architecture and ADR-worthy calls need Opus's reasoning depth. /scaffold, /sonar, and /handoff are executor-safe — bounded, mechanical work Sonnet handles fine. Start a planning session on Opus; run the rest wherever you already are.

Each skill now also carries a model: line saying the same thing — but read it as a hint, not a guardrail. Claude Code applies the override for the invoking turn and then the session model resumes, so a multi-turn /plan only gets Opus for the turn that does the grilling and drafting. It doesn't cross to OpenCode at all (that wants a full provider/model id, which would be exactly the kind of stale pin this repo refuses), so derive_command strips it. rules/workflow.md remains the authority; the frontmatter just saves you a /model on the turn that matters most.

Starting a new project — the order follows the tier:

For anything you'd plan (a lib or an app), lead with /plan — tier and stack are design decisions, so they belong to planning, not to a guess from a one-liner:

mkdir my-thing && cd my-thing
claude
> /plan a python lib for rate limiting

/plan grills you (tier and stack first, then the design), and when you accept it self-bootstraps the repo and scaffolds the skeletongit init, the numbered plan under docs/plans/, CLAUDE.md/CONTEXT.md, deps resolved live, a green quality gate, one initial commit — then stops before Phase 1. One command, end to end. Building the first tracer-bullet slice stays behind an explicit "start Phase 1" (that's the planning gate doing its job).

For a throwaway script where planning is overkill, skip straight to scaffold:

> /scaffold a python script that renders my docs to HTML

/scaffold infers the tier and stack from what you ask for and builds only what that tier needs. Versions are resolved live (uv add / pnpm add pin the current release to the lockfile; config shapes come from Context7), so the skeleton never ships stale pins. Either order works — run /scaffold first and it infers; run /plan first and scaffold consumes its decision instead of re-guessing.

Day-to-day:

/plan <what you want> — the agent reads the rules, grills(thx Matt) you one question at a time (proposing its own answer each time, exploring the code instead of asking when it can), drafts a plan, and waits for you to say go. On accept, the plan is saved numbered under docs/plans/NNNN-slug/ (overview + one file per phase) and committed — so it lives in git, and a second plan never clobbers the first. Domain terms settled during the grilling land in CONTEXT.md as they're resolved. Good for anything bigger than "fix this typo."

/spec <phase> — optional, and additive: it never touches /plan. Run it right before you build a phase to expand that one phase into a cold-executor spec — a docs/plans/NNNN-slug/phase-M.spec.md self-contained enough that a model with zero conversation context (a local 27B, or a fresh session, or you-in-a-crisis) can execute it: exact file paths and signatures, every external call Context7-pinned from the lockfile, each task sized to one context window, no decisions left open. It synthesizes the already-approved plan — it does not re-interview (borrowed from Matt Pocock's to-spec). Before you see it, plan-reviewer attacks it through a third "cold-executor readiness" lens — the smart model's spec gets stress-tested before the dumb one runs it. The spec is committed so it reaches a fresh clone, then deleted when the phase lands (the code is now the truth). Skip /spec entirely for phases you'll build yourself with full context; its whole point is the handoff to an executor that lacks it. Move the intelligence to plan-time, and a weaker model can do the typing.

/handoff at the end of a session — runs a security checkpoint over the session's whole diff (skippable when it's docs-only, adversarial when it touched auth/input/SQL/secrets; findings land in NOTES.md, criticals get fixed on the spot — the engine is skills/security-checkpoint, Anthropic's open-source security-review prompt adapted to session diffs, so it works identically in OpenCode), drains NOTES.md (every entry gets classified: promote to a task, promote to a comment, promote to a decision doc, or delete), updates CLAUDE.md's Status block, and commits the bookkeeping as a clean chore commit. Future-you opens the next session and the state is right there.

In between, you (I mean, the AI...) just code. The rules quietly steer the agent away from the usual failure modes. If something surprising comes up mid-task — a TODO surfaced by the code, a workaround you applied, an open question — append a timestamped entry to NOTES.md and keep going. /handoff will deal with it.

That's the whole model. Read the files, follow the conventions, commit the diffs.

Codebase analysis: one for me, one for the "dude" who does the heavy lifting

Two tools live in the box doing similar-sounding work but pointed at different audiences.

SocratiCode MCP is the agent's eyes. The agent queries it on demand during a session — semantic search, blast-radius impact, symbol-level call graphs. It reaches for it the way a human reaches for grep, except it understands meaning. It is not installed as a global Claude Code plugin — the plugin's bundled MCP server is static (Docker-managed Qdrant/Ollama, auto-generated project ID), and my Qdrant/Ollama addresses differ per project and machine. Instead: scripts/setup-socraticode.sh writes a per-project .mcp.json that runs npx -y socraticode against my own Qdrant and Ollama with no pinned project ID — so each source root gets its own per-path collection and the agent indexes polyglot repos one source root at a time (reset-socraticode wipes the collections for a clean re-index). The plugin's workflow skills (codebase-exploration, codebase-management) + the codebase-explorer agent are vendored into this repo (skills/, agents/) and deployed with the rest of the rules pack. Used silently in every project. Cheap to have around because nothing fires unless the agent asks.

Understand-Anything is the human's eyes. Optional, prompt-gated in both bootstraps: bootstrap.sh installs it as a Claude Code plugin, bootstrap-opencode.sh installs it via the upstream installer's first-class opencode backend (install.sh opencode — clones to ~/.understand-anything/repo, symlinks the skills into ~/.agents/skills). Either way its restraint rule rides along: the Understand-Anything restraint lines live in rules/research.md, one of the files OpenCode's instructions glob pulls in. It builds a knowledge graph and ships a Vite dashboard that visualises layers, dependencies, and a guided tour — perfect for onboarding a teammate, prepping a code review, or amusing the moneybag who needs to see something fancy before signing the next invoice. The agent will not invoke it on its own — rules/research.md makes that explicit.

Roughly: SocratiCode answers questions for the agent; Understand-Anything answers questions for the rest of us.

Both work with either agent, with full parity. SocratiCode ships only as a project-scoped MCP server (no global plugin) — setup-socraticode autodetects which agents you have installed and writes the right config for each (.mcp.json for Claude Code, the mcp block in opencode.json for OpenCode). The skills are shared verbatim: both read the same SKILL.md files from ~/.claude/skills (OpenCode lists it as a Claude-compatible search path), so bootstrap.sh deploys them once and OpenCode picks them up for free. The agent is the one artifact whose format genuinely differs — Claude reads ~/.claude/agents/codebase-explorer.md (vendored as-is), while OpenCode reads ~/.config/opencode/agents/ with its own mode: subagent + permission: frontmatter, so bootstrap-opencode.sh derives that copy from the same agent body (one source, two frontmatters — the same trick setup-socraticode uses for the MCP block). Understand-Anything is a first-class OpenCode backend of its own installer, so bootstrap-opencode.sh offers it as a prompt-gated step (install.sh opencode), the same way bootstrap.sh prompts for the Claude Code plugin.

Run claude or opencode from any project — both read the same rules, follow the same conventions, ignore the same noise.

Code quality: the floor and the deep scan

Two layers, both opinionated.

The native floor runs every loop, for free: rules/quality-gate.md makes the agent clear types, lint, security, and tests before it says "done." No server, no tokens — just the tools already on the box. Green here means "not obviously broken," nothing more.

The deep scan is /sonar — a lever I pull, never auto-fired. It runs the SonarQube scanner against a SonarQube server I host, then reads the findings back inline. bootstrap.sh pre-pulls both Sonar images so the first scan doesn't stall on a download:

  • sonar-scanner-cli — the scanner /sonar shells out to.
  • mcp/sonarqube — the MCP server both Claude Code and OpenCode use to read issues back as me.

Two tools, two token types, one server. The split trips people up:

  • The MCP server reads issues as meUSER token (SONARQUBE_URL + SONARQUBE_TOKEN).
  • The /sonar scanner pushes analysis results → Global Analysis token (SONAR_HOST_URL + SONAR_TOKEN).

Both URLs point at the same running SonarQube. Generate both tokens from the web UI (avatar → My Account → Security → Generate Tokens, pick the type per above); the per-project analysis key lives in sonar-project.properties. You bring your own SonarQube — lean-code wires the plumbing, not the server.

Design rules

  • YAGNI, ruthlessly. Every file justifies itself or it dies. Three near-duplicate rule sets become one. Templates with placeholder values get the values filled in deterministically by the script, not left for a human to fix.
  • Opinionated by default. Modern tools, not legacy ones. uv over pip. podman over docker. pnpm over npm. Expo Router over React Navigation. The rules don't say "consider", they say "always" — and where there's a real reason to break a rule, the rule says so.
  • Layers don't reach across. A rule doesn't depend on a specific project structure. A project doesn't depend on a specific rule being present. Each layer works alone.
  • Plain text, no clever encoding. Markdown for everything humans read. Bash for everything machines run. If you can't fix it with vim and git, it doesn't belong here.
  • No metrics. Coverage thresholds, lint scores, complexity numbers — all gameable by an LLM, all noise. Quality is enforced by criteria (happy path / error path / boundary), not by a number to chase.
  • One cloud dependency, and it's named: Context7. Qdrant, Ollama, llama.cpp, SonarQube all run on hardware I control, which makes it easy to read this repo as fully self-hosted. It isn't. rules/research.md puts Context7 on the critical path of every task that touches a third-party library, and it's a hosted service — the library name and the lookup question leave the machine on every call, so keep proprietary code and secrets out of the query text. That's the deliberate trade: current docs beat a model's stale memory, and there's no local mirror of the world's documentation to point at instead. With Context7 unreachable the rule degrades to web search (also cloud), and past that to memory — which is the exact failure the rule exists to prevent. plugins/enforce-research.js is the tripwire for that last case, but it only fires in OpenCode.

Not for

  • Teams who need uniform tooling enforced via CI hooks
  • Polyglot monorepos (the rules assume Python or TypeScript or both)
  • Anyone who thinks "1% test coverage gain" is a meaningful sentence

Credits

The skeleton started as a study of the maximalist frameworks — this repo is what's left after asking "but do I actually need that?" eight hundred times. Go install one of those if you want the deluxe version; come back here when you want to know which parts you need. (It's an opinion, not the only right way. But at least, I understand it better now!)

What survived is mostly mine; what isn't is vendored with gratitude and attribution — none of this is my work, all of it is inspiration:

  • stop-slop by Hardik Pandya (MIT) — strips the AI tells from prose before they reach you.
  • SocratiCode by Giancarlo Erra — the agent's eyes; its exploration/management skills and the codebase-explorer agent are vendored here.
  • Understand-Anything by Lum1104 — the human's eyes; knowledge-graph dashboards for the rest of us.
  • claude-code-security-review by Anthropic (MIT) — the security prompt behind /handoff's checkpoint, adapted to session diffs.
  • skills by Matt Pocock — nothing vendored, everything influenced. His videos and repo are where the diagnose skill, the CONTEXT.md glossary, requirement-grilling, and vertical-slice TDD came from — without them this would be the ten-thousandth copy of the same mistakes.
  • A Philosophy of Software Design by John Ousterhout — the deep-modules and different-layer-different-abstraction lenses in the standards, and /plan's design-it-twice step, are his ideas, deliberately kept in his words.
  • The Pragmatic Programmer by David Thomas & Andrew Hunt — tracer bullets: /plan's Phase 1 is the thinnest working end-to-end slice, straight from their book.