A highly opinionated OpenCode configuration for Python backend development. This is not a general-purpose AI assistant setup — it is a precise, curated environment built around a specific stack, specific patterns, and non-negotiable quality standards.
Most AI coding setups trade precision for generality. This one does the opposite.
Every agent in this configuration operates within a single authoritative ruleset
(AGENTS.md) that defines the exact tools, patterns, and constraints used across all
projects. There is no "let the AI decide" for tooling choices, code style, project
structure, or architectural patterns — those decisions have already been made, documented,
and encoded. Agents enforce them unconditionally.
The result is that AI assistance becomes genuinely useful for the hard parts — domain logic, architecture decisions, debugging — rather than wasting conversation on "which formatter should I use?" or "how should I structure this FastAPI router?"
This configuration is opinionated about:
- Toolchain:
uvfor package management,rufffor linting and formatting (with a custom three-step pipeline),banditfor security,pre-commitfor enforcement — no alternatives, no overrides - Code quality: McCabe complexity ≤ 4, max 4 function arguments, strict typing with
X | NoneoverOptional, absolute imports only, Google docstrings — all enforced at the agent level before any code is written - Architecture: FastAPI with
create_app()factory,Annotated[T, Depends()]everywhere,Controller(Generic[ModelType])repository pattern,model_factory()for schema derivation,selectinloading on all relationships — agents know these patterns and apply them without being asked - Podman: multi-stage Containerfiles, layer ordering as a caching discipline, non-root
execution, uv-managed venv,
python:3.x-slimonly - Performance: async-first with explicit rules against blocking I/O, N+1 awareness, algorithmic complexity consciousness — as a passive quality signal, never a blocker
- Security: bandit strict profile,
allow_skipping = false(no# nosec), token hashing, least-privilege IAM, OWASP Top 10 awareness built into every review
Where agents ask rather than assume:
- Deployment target (FastAPI / Lambda / full-stack)
- Authentication strategy and role model
- Database choice and schema design
- Testing scope and infrastructure
Everything else is handled by the standards, and the goal is to keep it that way.
This configuration is built around a specific primary stack and knows it deeply:
| Layer | Technology |
|---|---|
| Language | Python 3.14 |
| Package manager | uv (workspaces, lockfiles, venv) |
| Web framework | FastAPI + pydantic-settings |
| ORM / DB | SQLModel + asyncpg + PostgreSQL 17 |
| Migrations | Alembic (async, with alembic-postgresql-enum) |
| Auth | JWT-based (provider-agnostic pattern) |
| Testing | pytest + asyncio + xdist + pytest-socket (95% branch coverage) |
| Linting | ruff (46 rule sets, preview mode, unsafe fixes) |
| Security | bandit (strict profile, zero suppressions) |
| Container | Podman multi-stage, gunicorn + UvicornWorker |
| Serverless | AWS SAM + python-uv build method |
| Frontend (occasional) | bun + Vite + Biome + TypeScript strict |
| CI/CD | GitHub Actions + OIDC + composite setup action |
Secondary patterns that are also well-understood: AWS Lambda, SAM templates, OIDC deployments, and bun-first full-stack apps where FastAPI serves the built frontend.
opencode-config/
├── AGENTS.md # Global Python coding standards — loaded into every session
├── opencode.jsonc # Runtime config: LSP, MCP servers, formatters, plugins, watcher
├── tui.jsonc # TUI settings: catppuccin-mocha theme, scroll acceleration
├── agents/ # 13 specialised agents (6 primary, 7 subagents)
├── skills/ # 7 on-demand procedural playbooks
├── tools/ # 3 custom TypeScript tools
├── plugins/
│ └── shell-strategy/ # Non-interactive shell instructions (uv + bun aware)
└── scripts/
└── ruff-format.sh # Three-step Python formatter pipeline
Switch between primary agents with Tab in the TUI.
| Agent | Model | Color | Purpose |
|---|---|---|---|
build |
claude-sonnet-4-6 | Green | Full implementation. Enforces AGENTS.md on every write. Proactively invokes subagents after completing work. |
plan |
claude-sonnet-4-6 | Blue | Read-only analysis. Produces structured plans with file paths and compliance notes. Never writes code. |
refactor |
claude-sonnet-4-6 | Amber | Reduces complexity, improves naming, enforces limits. Never changes behaviour. Show-before-apply workflow. |
git |
claude-haiku-4-5 | Red | Git specialist. Conventional commits, branch management, PR descriptions. All git commands require approval. |
debug |
claude-sonnet-4-6 | Deep orange | Diagnoses bugs and traces failure chains. Full read + bash access for diagnostics. Never edits files. |
frontend |
claude-sonnet-4-6 | Cyan | Bun-first frontend specialist. Asks about framework/stack upfront. Knows the backend/+frontend/ split, Vite proxy, and multi-stage Podman build. |
Invoke subagents via @name in a prompt, or they are triggered automatically by primary agents.
| Agent | Model | Purpose |
|---|---|---|
code-review |
claude-sonnet-4-6 | Reviews against AGENTS.md: Blocker / Suggestion / Nitpick tiers. |
security |
claude-sonnet-4-6 | OWASP Top 10, auth flaws, secrets exposure, bandit findings. Critical→Info severity tiers. |
tests |
claude-sonnet-4-6 | Generates pytest suites. Reads conftest.py before inventing fixtures. 95% coverage target. |
docs |
claude-haiku-4-5 | Google-style docstrings, README sections, CLAUDE.md files. |
db |
claude-sonnet-4-6 | SQLModel models, Alembic migrations, query analysis. Catches N+1, unnamed constraints, unsafe migrations. |
ci |
claude-haiku-4-5 | GitHub Actions, Containerfiles, Podman Compose, SAM templates, Makefiles. |
research |
claude-haiku-4-5 | Fetches and synthesises external docs, RFCs, PEPs, library changelogs. Never writes to project files. |
| Situation | Subagent called |
|---|---|
| After completing a feature or fix | @code-review |
| Touching auth, tokens, or cryptography | @security |
| New module or function without test coverage | @tests |
| Creating or modifying a SQLModel model | @db |
Skills are on-demand playbooks loaded by agents when the situation matches. They are not always in context — agents load them only when needed.
| Skill | Loaded by | Trigger |
|---|---|---|
new-fastapi-project |
build |
"new project", "bootstrap", "scaffold" |
new-lambda-project |
build, ci |
"new Lambda", "new SAM", "serverless" |
alembic-migration |
build, db |
"add migration", "modify model", "schema change" |
pr-checklist |
git, code-review |
"open PR", "ready to merge", "prepare PR" |
docker-build-debug |
debug, ci |
"podman build failing", "container issue", "container not starting" |
new-frontend-feature |
build, frontend |
"add frontend", "add UI", "React/Vue/Svelte component" |
performance-analysis |
build, debug |
"slow", "high memory", "profile", "optimise", "benchmark" |
Three npm plugins are loaded automatically by OpenCode at startup (no manual install needed —
OpenCode resolves them from the plugin array in opencode.jsonc). One instruction-based
plugin ships as a local file.
| Plugin | Source | Purpose |
|---|---|---|
@tarquinen/opencode-dcp |
npm | Prunes stale tool outputs from context; extends session life on long tasks |
opencode-handoff |
npm | Creates focused handoff prompts for continuing work in a new session |
shell-strategy |
local instructions | Teaches agents to use non-interactive shell flags in all contexts. uv-aware (Python), bun-aware (frontend), no bare python or pip. |
One MCP server is configured: the PostgreSQL live analysis server. It is disabled globally and re-enabled only on the db agent.
| Server | Runs via | Scoped to | Purpose |
|---|---|---|---|
postgres |
uvx postgres-mcp (stdio) |
db |
Live DB introspection: schema discovery, SQL execution, EXPLAIN analysis, index recommendations, DB health |
The server is spawned on demand by OpenCode as a local stdio process — no manual
start/stop required. OpenCode launches it automatically when the db agent is active.
Setup: create an opencode.json at the project root (git-ignored, never committed)
with the local connection string:
{
"mcp": {
"postgres": {
"type": "local",
"command": ["uvx", "postgres-mcp", "--access-mode=unrestricted"],
"environment": {
"DATABASE_URI": "postgresql://postgres:postgres@localhost:5433/<dbname>"
}
}
}
}Note:
typeandcommandare required by the OpenCode config schema. A partialenvironment-only entry will fail validation withInvalid input mcp.postgres. The full server definition must be repeated in the project file.
OpenCode merges this on top of the global config. The db agent picks up DATABASE_URI
automatically and can:
- Inspect schemas, tables, columns, and indexes via
postgres_list_schemas/postgres_get_object_details - Execute SQL via
postgres_execute_sql - Run
EXPLAIN (ANALYZE, BUFFERS)viapostgres_explain_query - Find slow queries via
postgres_get_top_queries - Get index recommendations via
postgres_analyze_workload_indexes/postgres_analyze_query_indexes - Run a full DB health check via
postgres_analyze_db_health
When scaffolding a new FastAPI project with a DB, the new-fastapi-project skill
creates the opencode.json stub automatically (Step 11b) and adds it to .gitignore
and .containerignore.
Three TypeScript tools (require Bun) that provide structured output rather than raw terminal text.
| Tool | Invocation | Purpose |
|---|---|---|
uv_run |
{ command: string[] } |
Runs any command through uv run -- in the project venv. Ensures the correct Python is always used. |
ruff_check |
{ paths: string[], fix?: bool } |
Runs ruff check --output-format json. Returns structured violations array rather than colourised terminal output. |
pytest_collect |
{ path?: string, filter?: string } |
Collects test node IDs without running them. Surfaces import errors and missing tests before executing the suite. |
OpenCode formats every file it writes. The formatter config mirrors the Neovim conform.nvim setup exactly so agent-written files and editor-saved files come out identical.
| Filetype | Formatter | Notes |
|---|---|---|
.py, .pyi |
ruff-format.sh |
Three-step pipeline: ruff check --fix → ruff format → ruff check --select I --fix. Uses --config pyproject.toml when present. |
.md, .mdx |
prettier (via npx) |
--prose-wrap preserve — does not reflow manually-wrapped paragraphs. |
.js, .jsx, .ts, .tsx, .css, .html |
prettier (via npx) |
Fallback for frontend files. Biome auto-overrides per-project when biome.json is present. |
.json, .jsonc, .yaml, .yml |
prettier (via npx) |
Single formatter for all config and data files. Biome auto-overrides per-project when biome.json is present. |
.sh, .bash |
shfmt |
Standard shell script formatting. |
| Tool | Required for | Install |
|---|---|---|
| opencode | Everything | brew install sst/tap/opencode |
| uv | Python projects | brew install uv |
| ruff | Python formatter | brew install ruff |
| node | npm plugins, prettier | brew install node |
| bun | Custom TS tools, frontend runtime | brew install bun |
| podman | Container runtime | brew install podman |
| podman-compose | Compose support | brew install podman-compose |
| shfmt | Shell formatter | brew install shfmt |
| trivy | Container image CVE scanning (optional) | brew install trivy |
git clone https://github.com/philipp-bliznuk/opencode-python.git ~/projects/opencode
cd ~/projects/opencode
./install.shThe script will:
- Check for all prerequisites and offer to install missing ones via Homebrew
- Back up any existing
~/.config/opencode/directory - Create a symlink:
~/.config/opencode→ this repo - Make
scripts/ruff-format.shexecutable - Verify the shell-strategy instructions file is present
npm plugins (@tarquinen/opencode-dcp, opencode-handoff) are loaded
automatically by OpenCode at startup — no separate install step is required.
Because the config directory is a symlink to the repo, any git pull takes effect immediately — no re-running the install script.
cd ~/projects/opencode
git pullThat's it. The symlink means there is no copy step.
No API key required. Pass an environment variable when launching:
OPENCODE_ENABLE_EXA=1 opencodeGives agents access to go-to-definition, find-references, and call hierarchy via the LSP:
OPENCODE_EXPERIMENTAL_LSP_TOOL=true opencodeThe /editor command (compose a long prompt in your editor) and /export (export session to Markdown) use the $EDITOR environment variable. Add this to your shell profile:
export EDITOR=nvim # or vim, code --wait, etc.The postgres MCP is already configured in opencode.jsonc and scoped to the db
agent. To activate it for a project, create a git-ignored opencode.json at the
project root with the local connection string. See the
PostgreSQL MCP setup section above for
the exact format.
AGENTS.md is a global instruction — it applies to every project opened in OpenCode. For project-specific overrides, add an opencode.json at the project root. Config files are merged, not replaced, so project-level settings stack on top of the global ones without losing anything.
Example: pin a different model for a specific project:
// ~/your-project/opencode.json
{
"model": "openai/o3"
}Example: add project-specific instructions:
{
"instructions": ["./CLAUDE.md"]
}Example: set the PostgreSQL connection string for the db agent (git-ignored):
{
"mcp": {
"postgres": {
"environment": {
"DATABASE_URI": "postgresql://postgres:postgres@localhost:5433/myproject"
}
}
}
}Add opencode.json to .gitignore and .containerignore — it contains credentials.