Imported from xlr8-1/dotfiles (
dot_config/opencode/AGENTS.md). Install upstream withnpx skills add xlr8-1/dotfiles --skill opencode. Copyright stays with the author.
Global Development Standards
Global instructions for all projects. Project-specific AGENTS.md files override these defaults.
- Use skills proactively when they match the task — suggest relevant ones, don't block on them
Philosophy
- No speculative features - Don't add features, flags, or configuration unless users actively need them
- No premature abstraction - Don't create utilities until you've written the same code three times
- Clarity over cleverness - Prefer explicit, readable code over dense one-liners
- Justify new dependencies - Each dependency is attack surface and maintenance burden
- No phantom features - Don't document or validate features that aren't implemented
- Replace, don't deprecate - When a new implementation replaces an old one, remove the old one entirely. No backward-compatible shims, dual config formats, or migration paths. Proactively flag dead code — it adds maintenance burden and misleads both developers and LLMs.
- Verify at every level - Set up automated guardrails (linters, type checkers, pre-commit hooks, tests) as the first step, not an afterthought. Prefer structure-aware tools (ast-grep, LSPs, compilers) over text pattern matching. Review your own output critically. Every layer catches what the others miss.
- Bias toward action - Decide and move for anything easily reversed; state your assumption so the reasoning is visible. Ask before committing to interfaces, data models, architecture, or destructive/write operations on external services.
- Finish the job - Don't stop at the minimum that technically satisfies the request. Handle the edge cases you can see. Clean up what you touched. If something is broken adjacent to your change, flag it. But don't invent new scope — there's a difference between thoroughness and gold-plating.
- Agent-native by default - Design so agents can achieve any outcome users can. Tools are atomic primitives; features are outcomes described in prompts. Prefer file-based state for transparency and portability. When adding UI capability, ask: can an agent achieve this outcome too?
Code Quality
Hard limits
- ≤100 lines/function, cyclomatic complexity ≤8
- ≤5 positional params
- 100-char line length
- Absolute imports only — no relative (
..) paths - Google-style docstrings on non-trivial public APIs
Zero warnings policy
Fix every warning from every tool — linters, type checkers, compilers, tests. If a warning truly can't be fixed, add an inline ignore with a justification comment. Never leave warnings unaddressed; a clean output is the baseline, not the goal.
Comments
Code should be self-documenting. No commented-out code—delete it. If you need a comment to explain WHAT the code does, refactor the code instead.
Error handling
- Fail fast with clear, actionable messages
- Never swallow exceptions silently
- Include context (what operation, what input, suggested fix)
Reviewing code
Evaluate in order: architecture → code quality → tests → performance. Before reviewing, sync to latest remote (git fetch origin).
For each issue: describe concretely with file:line references, present options with tradeoffs when the fix isn't obvious, recommend one, and ask before proceeding.
Testing
Test behavior, not implementation. Tests should verify what code does, not how. If a refactor breaks your tests but not your code, the tests were wrong.
Test edges and errors, not just the happy path. Empty inputs, boundaries, malformed data, missing files, network failures — bugs live in edges. Every error path the code handles should have a test that triggers it.
Mock boundaries, not logic. Only mock things that are slow (network, filesystem), non-deterministic (time, randomness), or external services you don't control.
Verify tests catch failures. Break the code, confirm the test fails, then fix. Use mutation testing (cargo-mutants, mutmut) to verify systematically. Use property-based testing (proptest, hypothesis) for parsers, serialization, and algorithms.
Development
When adding dependencies, CI actions, or tool versions, always look up the current stable version — never assume from memory unless the user provides one.
CLI tools
| tool | replaces | usage |
|---|---|---|
rg (ripgrep) |
grep | rg "pattern" - 10x faster regex search |
fd |
find | fd "*.py" - fast file finder |
ast-grep |
- | ast-grep --pattern '$FUNC($$$)' --lang py - AST-based code search |
shellcheck |
- | shellcheck script.sh - shell script linter |
shfmt |
- | shfmt -i 2 -w script.sh - shell formatter |
actionlint |
- | actionlint .github/workflows/ - GitHub Actions linter |
zizmor |
- | zizmor .github/workflows/ - Actions security audit |
prek |
pre-commit | prek run - fast git hooks (Rust, no Python) |
wt |
git worktree | wt switch branch - manage parallel worktrees |
trash |
rm | trash file - moves to macOS Trash (recoverable). Never use rm -rf |
Prefer ast-grep over ripgrep when searching for code structure (function calls, class definitions, imports, pattern matching across arguments). Use ripgrep for literal strings and log messages.
Python
Runtime: 3.13 with uv venv
| purpose | tool |
|---|---|
| deps & venv | uv |
| lint & format | ruff check · ruff format |
| static types | ty check |
| tests | pytest -q |
Always use uv, ruff, and ty over pip/poetry, black/pylint/flake8, and mypy/pyright — they're faster and stricter. Configure ty strictness via [tool.ty.rules] in pyproject.toml. Use uv_build for pure Python, hatchling for extensions.
Tests in tests/ directory mirroring package structure. Supply chain: pip-audit before deploying, pin exact versions (== not >=), verify hashes with uv pip install --require-hashes.
Node/TypeScript
Runtime: Node 22 LTS, ESM only ("type": "module")
| purpose | tool |
|---|---|
| lint | oxlint |
| format | oxfmt |
| test | vitest |
| types | tsc --noEmit |
Always use oxlint and oxfmt over eslint/prettier — they're faster and stricter. Enable typescript, import, unicorn plugins.
tsconfig.json strictness — enable all of these:
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"verbatimModuleSyntax": true,
"isolatedModules": true
Colocated *.test.ts files. Supply chain: pnpm audit --audit-level=moderate before installing, pin exact versions (no ^ or ~), enforce 24-hour publish delay (pnpm config set minimumReleaseAge 1440), block postinstall scripts (pnpm config set ignore-scripts true).
Rust
Runtime: Latest stable via rustup
| purpose | tool |
|---|---|
| build & deps | cargo |
| lint | cargo clippy --all-targets --all-features -- -D warnings |
| format | cargo fmt |
| test | cargo test |
| supply chain | cargo deny check (advisories, licenses, bans) |
| safety check | cargo careful test (stdlib debug assertions + UB checks) |
Style:
- Prefer
forloops with mutable accumulators over iterator chains - Shadow variables through transformations (no
raw_x/parsed_xprefixes) - No wildcard matches; avoid
matches!macro—explicit destructuring catches field changes - Use
let...elsefor early returns; keep happy path unindented
Type design:
- Newtypes over primitives (
UserId(u64)notu64) - Enums for state machines, not boolean flags
thiserrorfor libraries,anyhowfor applicationstracingfor logging (error!/warn!/info!/debug!), not println
Optimization:
- Write efficient code by default — correct algorithm, appropriate data structures, no unnecessary allocations
- Profile before micro-optimizing; measure after
Cargo.toml lints:
[lints.clippy]
pedantic = { level = "warn", priority = -1 }
# Panic prevention
unwrap_used = "deny"
expect_used = "warn"
panic = "deny"
panic_in_result_fn = "deny"
unimplemented = "deny"
# No cheating
allow_attributes = "deny"
# Code hygiene
dbg_macro = "deny"
todo = "deny"
print_stdout = "deny"
print_stderr = "deny"
# Safety
await_holding_lock = "deny"
large_futures = "deny"
exit = "deny"
mem_forget = "deny"
# Pedantic relaxations (too noisy)
module_name_repetitions = "allow"
similar_names = "allow"
Bash
All scripts must start with set -euo pipefail. Lint: shellcheck script.sh && shfmt -d script.sh
GitHub Actions
Pin actions to SHA hashes with version comments: actions/checkout@<full-sha> # vX.Y.Z (use persist-credentials: false). Scan workflows with zizmor before committing. Configure Dependabot with 7-day cooldowns and grouped updates.
Workflow
Before committing:
- Re-read your changes for unnecessary complexity, redundant code, and unclear naming
- Run relevant tests — not the full suite
- Run linters and type checker — fix everything before committing
Commits:
- Imperative mood, ≤72 char subject line, one logical change per commit
- Never amend/rebase commits already pushed to shared branches
- Never push directly to main — use feature branches and PRs
- Never commit secrets, API keys, or credentials — use
.envfiles (gitignored) and environment variables
Hooks and worktrees:
- Install prek in every repo (
prek install). Runprek runbefore committing. Configure auto-updates:prek auto-update --cooldown-days 7 - Parallel subagents require worktrees. Each subagent MUST work in its own worktree (
wt switch <branch>), not the main repo. Never share working directories.
Pull requests: Describe what the code does now — not discarded approaches, prior iterations, or alternatives. Only describe what's in the diff.
Use plain, factual language. A bug fix is a bug fix, not a "critical stability improvement." Avoid: critical, crucial, essential, significant, comprehensive, robust, elegant.
CodeGraph
This project has a CodeGraph MCP server (codegraph_* tools) configured. CodeGraph is a tree-sitter-parsed knowledge graph of every symbol, edge, and file. Reads are sub-millisecond and return structural information grep cannot.
When to prefer codegraph over native search
Use codegraph for structural questions — what calls what, what would break, where is X defined, what is X's signature. Use native grep/read only for literal text queries (string contents, comments, log messages) or after you already have a specific file open.
| Question | Tool |
|---|---|
| "Where is X defined?" / "Find symbol named X" | codegraph_search |
| "What calls function Y?" | codegraph_callers |
| "What does Y call?" | codegraph_callees |
| "What would break if I changed Z?" | codegraph_impact |
| "Show me Y's signature / source / docstring" | codegraph_node |
| "Give me focused context for a task/area" | codegraph_context |
| "See several related symbols' source at once" | codegraph_explore |
| "What files exist under path/" | codegraph_files |
| "Is the index healthy?" | codegraph_status |
Rules of thumb
- Answer directly — don't delegate exploration. For "how does X work" / architecture / trace questions, answer with 2-3 codegraph calls:
codegraph_contextfirst, then ONEcodegraph_explorefor the source of the symbols it surfaces. Codegraph IS the pre-built index, so spawning a separate file-reading sub-task/agent — or running a grep + read loop — repeats work codegraph already did and costs more for the same answer. - Trust codegraph results. They come from a full AST parse. Do NOT re-verify them with grep — that's slower, less accurate, and wastes context.
- Don't grep first when looking up a symbol by name.
codegraph_searchis faster and returns kind + location + signature in one call. - Don't chain
codegraph_search+codegraph_nodewhen you just want context —codegraph_contextis one call. - Don't loop
codegraph_nodeover many symbols — onecodegraph_explorecall returns several symbols' source grouped in a single capped call, while each separate node/Read call re-reads the whole context and costs far more. - Index lag: the file watcher debounces ~500ms behind writes; don't re-query immediately after editing a file in the same turn.
If .codegraph/ doesn't exist
The MCP server returns "not initialized." Ask the user: "I notice this project doesn't have CodeGraph initialized. Want me to run codegraph init -i to build the index?"