OS coverage: Steps marked
[Win][Linux][macOS]are OS-specific. Steps with no label apply to all three.What this guide covers: How to configure OpenCode for maximum efficiency on a real project — config files, MCP servers, custom agents, skills, rules, and model selection.
- What is OpenCode?
- Installation
- Connecting to a Provider
- Understanding Config Locations
- Project Initialization (/init)
- Writing a Good AGENTS.md
- Setting Up MCP Servers
- Custom Agents
- Agent Skills (SKILL.md)
- Custom Commands
- Permissions Model
- Global vs Project Config
- Putting It All Together
- Harness Structure
- What to Do Next
OpenCode is an open-source AI coding agent. It runs in your terminal, IDE, or desktop — and is also available as a standalone desktop app.
It supports:
- 75+ providers (Anthropic, OpenAI, OpenRouter, open-weight models, local models, and more) via Models.dev
- MCP (Model Context Protocol) servers — plug in any tool: GitHub, Supabase, Cloudflare, etc.
- Custom agents, skills, rules, and commands
- GitHub Copilot and ChatGPT Plus/Pro via direct login (
/connect)
curl -fsSL https://opencode.ai/install | bash
This works on macOS and Linux. For Windows, see below.
All platforms — npm
npm install -g opencode-ai
# also works with bun, pnpm, or yarn
bun add -g opencode-ai
macOS / Linux — Homebrew
brew install anomalyco/tap/opencode
Use the
anomalyco/taptap, notbrew install opencode— the official Homebrew formula is updated less frequently.
Linux — Arch
sudo pacman -S opencode # Stable
paru -S opencode-bin # Latest from AUR
All platforms — Mise
mise use -g github:anomalyco/opencode
All platforms — Docker
docker run -it --rm ghcr.io/anomalyco/opencode
Windows — Chocolatey
choco install opencode
Windows — Scoop
scoop install opencode
Windows — npm (what we used)
npm install -g opencode-ai
Windows note: OpenCode officially recommends WSL for the best experience. However, the npm install works fine in PowerShell/cmd, once you fix the PATH issue documented in the troubleshooting section below.
Desktop app — available for macOS, Windows, and Linux: opencode.ai/download
Binary release — grab the latest binary directly: github.com/anomalyco/opencode/releases
opencode --version
Open OpenCode in any project directory:
opencode
Inside the TUI, run:
/connect
Select your provider. Options include:
- opencode — OpenCode Zen (curated models, managed by the OpenCode team, simplest to start with)
- anthropic, openai, openrouter, google, groq, and many more
For OpenCode Zen: it opens opencode.ai/auth in your browser. Sign in, add billing, copy the API key, paste it back in the TUI.
For Anthropic directly:
/connect → anthropic → paste ANTHROPIC_API_KEY
Alternatively, set environment variables — OpenCode reads them automatically:
OpenCode reads env vars from the OS environment, not from .env files. Set them once at the user level so they're available to opencode and every terminal you open — no reloading needed.
Windows — set permanently for your user account (run once):
# One-liner to load everything from your .env file into permanent user env vars
Get-Content .env | Where-Object { $_ -match '^\s*[^#]\w+=.+' } | ForEach-Object {
$k,$v = $_ -split '=',2
[System.Environment]::SetEnvironmentVariable($k.Trim(), $v.Trim(), 'User')
}
Or set individual vars:
[System.Environment]::SetEnvironmentVariable("GITHUB_TOKEN", "ghp_...", "User")
Open a new terminal after running — existing terminals inherit the old environment.
Linux/macOS — add to ~/.zshrc or ~/.bashrc:
export GITHUB_TOKEN=ghp_...
export SUPABASE_ACCESS_TOKEN=sbp_...
# etc.
Then: source ~/.zshrc
Never put real secrets in
.env.example— that file is committed to git. Keep real values in.env(gitignored) and promote them to user-level env vars as above.
- OpenCode does NOT auto-load
.envfiles. The{env:VAR}syntax inopencode.jsoncreads from the OS environment, so set vars at the user level — not just the session. - Windows user-level vars survive across terminals and reboots — set once with
SetEnvironmentVariable(..., 'User'), done. - Linux/macOS —
~/.zshrc/~/.bashrcexports are the equivalent; they load on every new shell.
OpenCode merges config from multiple locations. Later sources override earlier ones for conflicting keys — non-conflicting keys from all sources are preserved.
| Source | Path | Scope |
|---|---|---|
| Remote (org defaults) | .well-known/opencode |
Organization |
| Global | ~/.config/opencode/opencode.json |
User (all projects) |
| Custom (env var) | $OPENCODE_CONFIG |
Session override |
| Project | opencode.json in project root |
|
.opencode/ directories |
agents, commands, plugins, skills |
Key insight: they are merged together, not replaced. If your global config sets autoupdate: true and your project config sets model: "anthropic/claude-sonnet-4-5", both apply.
| Config | Path |
|---|---|
| Global config | %USERPROFILE%\.config\opencode\opencode.json |
| Global AGENTS.md | %USERPROFILE%\.config\opencode\AGENTS.md |
| Global agents | %USERPROFILE%\.config\opencode\agents\ |
| Global skills | %USERPROFILE%\.config\opencode\skills\ |
| Managed (IT) | %ProgramData%\opencode\opencode.json |
| Config | Path |
|---|---|
| Global config | ~/.config/opencode/opencode.json |
| Global AGENTS.md | ~/.config/opencode/AGENTS.md |
| Global agents | ~/.config/opencode/agents/ |
| Global skills | ~/.config/opencode/skills/ |
| Managed (Linux) | /etc/opencode/opencode.json |
| Managed (macOS) | /Library/Application Support/opencode/ |
- Global config — provider keys, personal preferences (theme, shell, autoupdate)
- Project
opencode.json— MCP servers, project-specific agents, permissions, instructions .opencode/directory — agents, skills, commands (committed to git, shared with team)AGENTS.md— project rules loaded into every session automatically
Navigate to your project:
cd /path/to/domain-harness # Windows: cd C:\Users\...\domain-harness
opencode
Inside the TUI:
/init
OpenCode scans your project, may ask a few questions, and creates AGENTS.md — a rules file automatically loaded into every session.
What /init produces:
- Build, lint, and test commands
- Repo structure notes that aren't obvious from filenames
- Coding conventions and project-specific gotchas
- References to existing instruction files (Cursor rules, Copilot instructions, etc.)
If you already have an AGENTS.md, /init improves it in place instead of replacing it.
Tip: Commit your
AGENTS.mdto git. It's shared with your team and ensures consistent agent behavior across machines.
- For a new/empty project,
/initcreates a minimal file. The real value comes when there's existing code for it to analyze. - You can also write
AGENTS.mdby hand from the start — especially useful when you have prior design documents or architecture decisions to encode.
AGENTS.md is the most important file in your opencode harness. It contains instructions that will be included in the LLM’s context to customize its behavior for your specific project. It's loaded into every session and tells the agent:
- What this project is and isn't
- Architecture principles it must never violate
- Tech stack and key dependencies
- Which custom agents to use for which tasks
- What NOT to do
- Project rules:
AGENTS.mdin project root — shared with team via git - Global rules:
~/.config/opencode/AGENTS.md— personal, not committed - Claude Code compatibility: For users migrating from Claude Code, OpenCode supports Claude Code’s file conventions as fallbacks.
CLAUDE.mdis used as fallback if noAGENTS.mdexists:- Project rules:
CLAUDE.mdin your project directory (used if no AGENTS.md exists) - Global rules:
~/.claude/CLAUDE.md(used if no ~/.config/opencode/AGENTS.md exists) - Skills:
~/.claude/skills/
- Project rules:
To disable Claude Code compatibility, set one of these environment variables:
export OPENCODE_DISABLE_CLAUDE_CODE=1 # Disable all .claude support
export OPENCODE_DISABLE_CLAUDE_CODE_PROMPT=1 # Disable only ~/.claude/CLAUDE.md
export OPENCODE_DISABLE_CLAUDE_CODE_SKILLS=1 # Disable only .claude/skills
When opencode starts, it looks for rule files in this order:
- Local files: Traverses up from the current directory looking for
AGENTS.md, thenCLAUDE.md. - Global file:
~/.config/opencode/AGENTS.md - Claude Code global file:
~/.claude/CLAUDE.md(unless disabled)
The first matching file wins in each category. For example, if you have both AGENTS.md and CLAUDE.md, only AGENTS.md is used. Similarly, ~/.config/opencode/AGENTS.md takes precedence over ~/.claude/CLAUDE.md.
- Architecture constraints that must never be violated — put these first, state them clearly
- Stack reference — specific versions, specific tools
- Key design decisions with the why — "no image storage because privacy" is better than just "no image storage"
- Agent invocation guide — which custom agents to use when
- Anti-patterns — "do NOT do X" is often more useful than "do Y"
- Detailed API docs (link to them instead, or put them in a skill)
- Long code examples (put those in skills)
- Generic coding advice (the model already knows this)
| Section | What to put in it |
|---|---|
| Project Overview | One paragraph — what it is and what it isn't |
| Architecture Principles | Hard constraints that must never be violated (e.g., no raw data storage, modality-agnostic core) |
| Stack Reference | Specific tools and versions — not generic ("Supabase Postgres" not "a database") |
| Key Design Decisions | Decision + why + links to reference docs. "No image storage because privacy" beats "no image storage" |
| MCP Tool Usage | Which MCP server to use for which task, so the agent picks the right tool |
| What NOT to Do | Explicit prohibitions — often more useful than instructions |
| Agent Invocation Notes | Which custom agent to invoke for which class of task |
You can point opencode.json at additional files that merge with AGENTS.md:
{
"instructions": [
"docs/api-standards.md",
".cursor/rules/*.md"
]
}
Gotcha: Do NOT add
"AGENTS.md"toinstructions— opencode already auto-discovers and loads it. Listing it here would load it twice, wasting tokens every session.
Remote URLs also work (fetched with a 5-second timeout):
{
"instructions": [
"https://raw.githubusercontent.com/my-org/shared-rules/main/style.md"
]
}
While the instructions array in opencode.json pre-loads files into every session, you might have dense reference files (like API standards or React patterns) that you don't want burning tokens unless strictly necessary.
While OpenCode doesn't automatically parse @ file references, you can write explicit instructions in AGENTS.md teaching the agent to fetch them itself using its read tool:
## External File Loading
CRITICAL: When you encounter a file reference (e.g., @docs/general.md), use your Read tool to load it on a need-to-know basis.
- Do NOT preemptively load all references - use lazy loading based on actual need.
## Development Guidelines
- For React component architecture: @docs/react-patterns.md
- For REST API design: @docs/api-standards.md
MCP (Model Context Protocol) servers give OpenCode access to external tools — GitHub, Supabase, Cloudflare, docs search, and more. They show up as tools the LLM can call, just like the built-in read, edit, and bash tools.
Every enabled MCP server adds its tool descriptions to the context. Be selective. The GitHub MCP server in particular can add a lot of tokens. Disable servers you're not actively using.
In opencode.json:
{
"mcp": {
"server-name": {
"type": "local", // "local" or "remote"
"command": ["npx", "-y", "@some/mcp-package"],
"environment": {
"MY_VAR": "{env:MY_ENV_VAR}" // env var substitution
},
"enabled": true
}
}
}
Get the latest docs into Claude, Codex, Cursor, and other agents. No API key needed for the free tier. Add use context7 to any prompt.
"context7": {
"type": "remote",
"url": "https://mcp.context7.com/mcp",
"enabled": true
}
Usage in prompts:
How do I set up RLS in Supabase? use context7
Search GitHub code, files, and paths across a million Githup repos for implementation examples. Free, no auth.
"gh_grep": {
"type": "remote",
"url": "https://mcp.grep.app",
"enabled": true
}
Usage in prompts:
Show me how other projects implement JWT refresh token rotation. use gh_grep
Requires Node.js (npx). Enables the LLM to call git commands via MCP.
"git": {
"type": "local",
"command": ["npx", "-y", "@modelcontextprotocol/server-git", "--repository", "."],
"enabled": true
}
Note: OpenCode can already run git commands via
bash. This MCP server provides a more structured interface. Evaluate whether you need it.
Requires a GitHub Personal Access Token with repo and read:org scopes.
Create token at: https://github.com/settings/tokens
"github": {
"type": "local",
"command": ["npx", "-y", "@modelcontextprotocol/server-github"],
"environment": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "{env:GITHUB_TOKEN}"
},
"enabled": false
}
Set env var:
# Linux/macOS
export GITHUB_TOKEN=ghp_...
# Windows PowerShell
$env:GITHUB_TOKEN = "ghp_..."
Then enable in opencode.json and run:
opencode mcp list # verify it appears
Warning: Certain MCP servers such as GitHub MCP server adds a lot of tokens. Enable it per-agent rather than globally if you have a large number of tools.
Requires a Supabase Management API token (not the anon key or service role key — this is a separate token for the management API).
Get token at: https://supabase.com/dashboard/account/tokens
"supabase": {
"type": "local",
"command": [
"npx", "-y",
"@supabase/mcp-server-supabase@latest",
"--access-token",
"{env:SUPABASE_ACCESS_TOKEN}"
],
"enabled": false
}
Also OAuth-based.
"sentry": {
"type": "remote",
"url": "https://mcp.sentry.dev/mcp",
"oauth": {},
"enabled": false
}
Authenticate:
opencode mcp auth sentry
List all servers and their status:
opencode mcp list
Debug a specific server:
opencode mcp debug supabase
If a server adds too many tokens to every session, disable it globally and enable it only for the agents that need it:
{
"mcp": {
"supabase": { "type": "local", "command": ["..."], "enabled": true }
},
"tools": {
"supabase_*": false // disabled globally
},
"agent": {
"build": {
"tools": {
"supabase_*": true // re-enabled for the build agent only
}
}
}
}
OpenCode ships with five agents. You don't configure them — they're just there:
| Agent | Type | What it does | When to use |
|---|---|---|---|
| build | primary | All tools enabled. Makes file changes, runs commands. The default. | Everything, all the time |
| plan | primary | Same as build but edit/bash set to ask — describes rather than does |
Before any large change — press Tab to switch |
| general | subagent | Full tool access, runs multi-step tasks in parallel | Delegating parallel work |
| explore | subagent | Read-only, no edits, fast codebase Q&A | "Where is X implemented?" |
| scout | subagent | Read-only, inspects external repos and library source | Cross-referencing a dependency |
The intended workflow for non-trivial tasks:
- Switch to
plan(Tabkey) → let it lay out the approach - Review and iterate on the plan
- Switch back to
build(Tabkey) → execute
For many projects, the five built-ins plus a good AGENTS.md are sufficient. Don't create custom agents unless you have a specific need:
- Generic web app with standard CRUD → built-ins + AGENTS.md is enough
- Solo project with no domain-specific rules → built-ins are fine
Create a custom subagent when you need:
- Domain-specific hard rules that the default agent might forget
- Read-only analysis with a separate model — let a cheaper/faster model do the review so your main model isn't tied up
- Scoped permissions — a writer that can only edit
.mdfiles, a security reviewer that can only grep - Consistent persona across sessions — specialized prompt baked in, not re-explained every time
Examples by project type:
| Project | Agents you might create |
|---|---|
| Any project | @security-auditor (read-only, OWASP review), @docs-writer (edits only .md) |
| Fintech app | @compliance-reviewer (regulation rules, read-only), @transaction-analyst (fraud pattern review) |
| Health/wellness app | @domain-analyst (medical safety rules, no-diagnosis constraint), @privacy-auditor (data handling review) |
| Platform / SaaS | @api-designer (contract-first design, no implementation), @migration-planner (schema changes only) |
| Learning platform | @curriculum-reviewer (pedagogy rules), @accessibility-auditor (a11y read-only review) |
| Type | How invoked | Can make changes? |
|---|---|---|
| Primary | Tab key to cycle, default is Build | Yes (by default) |
| Subagent | @name in prompt, or auto-invoked by primary |
Configurable |
Create a .md file in .opencode/agents/ (project) or ~/.config/opencode/agents/ (global):
---
description: "Short description — the primary agent reads this to decide when to invoke"
mode: subagent # primary | subagent | all
model: opencode-go/kimi-k3
temperature: 0.1 # lower = more deterministic / rule-following
steps: 20 # max iterations before forced text response
permission:
edit: deny
bash: deny
webfetch: allow
---
Your system prompt goes here in plain markdown.
The filename (without .md) becomes the agent name. review.md → @review.
Not all tasks need the same model. Assign based on what the task actually requires:
| Task type | What matters | Good fit |
|---|---|---|
| Code generation / implementation | Coding benchmark, context window | Kimi K3, GLM-5.2 |
| Planning / reasoning | Chain-of-thought, instruction following | Kimi K3, DeepSeek V4 Pro |
| Documentation writing | Language quality, instruction following | Grok 4.5 |
| Security / domain review | Careful analysis, rule-following | MiniMax M3, DeepSeek V4 Pro |
| Session titles / lightweight | Speed, cost | DeepSeek V4 Flash, MiMo-V2.5 |
On the OpenCode Go plan, model IDs use the format
opencode-go/<model-id>. Connect once via/connect→ select OpenCode Go → paste your API key.
This is the configuration from our reference consumer project. Adapt model names and steps to your project and provider:
| Agent | Mode | Model (Go plan) | Steps | Rationale |
|---|---|---|---|---|
build (built-in) |
primary | opencode-go/kimi-k3 |
— | Strongest open coding model on Go plan |
plan (built-in) |
primary | opencode-go/kimi-k3 |
— | Same model — strong reasoning without edits |
@domain-expert |
subagent | opencode-go/kimi-k3 |
15 | Domain safety rules need strong instruction following |
@security-auditor |
subagent | opencode-go/minimax-m3 |
25 | Cost-effective deep analysis; extra steps for thorough grep |
@docs-writer |
subagent | opencode-go/grok-4.5 |
20 | Best language quality on Go plan |
small_model (config) |
— | opencode-go/deepseek-v4-pro |
— | Background tasks: session titles, summaries |
opencode agent create
Walks you through: name, description, scope (global/project), permissions — generates the markdown file.
---
description: "..." # Required — primary agent reads this to decide when to invoke
mode: subagent # primary | subagent | all
model: opencode-go/kimi-k3
temperature: 0.1 # 0.0 = deterministic, 1.0 = creative
steps: 20 # cap on agentic iterations — prevents runaway agents
color: "#ff6b6b" # optional visual indicator in TUI
permission:
edit: deny
bash:
"git diff*": allow
"*": deny
webfetch: allow
---
- Built-in agents cover most projects — only add custom subagents when you have domain-specific rules or need scoped permissions.
- Always set
steps— without it, a stuck subagent can loop indefinitely. 15–25 is a good range for analysis agents. - Match model to task — documentation doesn't need a coding model; use the cheapest model that does the job well.
edit: denyon analysis agents — they can read and respond freely with zero risk of unintended changes.- Low temperature (0.1) for rule-following agents — domain-specific and security agents must follow rules consistently, not creatively.
Skills are reusable knowledge chunks stored in SKILL.md files. Unlike AGENTS.md (always loaded), skills are loaded on demand — the LLM sees a list of available skills and calls skill({ name: "..." }) to load the full content when needed.
Create one folder per skill name and put a SKILL.md inside it. OpenCode searches these locations:
- Project config:
.opencode/skills/<name>/SKILL.md - Global config:
~/.config/opencode/skills/<name>/SKILL.md - Project Claude-compatible:
.claude/skills/<name>/SKILL.md - Global Claude-compatible:
~/.claude/skills/<name>/SKILL.md - Project agent-compatible:
.agents/skills/<name>/SKILL.md - Global agent-compatible:
~/.agents/skills/<name>/SKILL.md
.opencode/
└── skills/
└── your-skill-name/
└── SKILL.md ← must be uppercase SKILL.md
Or globally:
~/.config/opencode/
└── skills/
└── your-skill-name/
└── SKILL.md
---
name: your-skill-name # must match the directory name
description: >
What this skill contains and when to use it.
The agent reads this to decide whether to load the skill.
license: MIT # optional
compatibility: opencode # optional
metadata: # optional key-value pairs
domain: skincare
---
Naming rules:
- Lowercase alphanumeric + single hyphens
- No leading/trailing hyphens, no consecutive
-- - 1–64 characters
- Must match directory name exactly
Think of skills as your project's reference library — data, tables, contracts, and specs that agents need to look up, not rules they need to follow at all times.
| Type of content | Good skill candidate? | Why |
|---|---|---|
| Database schema + RLS policies | ✅ Yes | Implementation detail, large, needed only when writing migrations |
| API contract / TypeScript interfaces | ✅ Yes | Precise reference, only needed when implementing that module |
| Domain rubrics / grading scales | ✅ Yes | Data tables that need to be exact — not paraphrased in a prompt |
| Ingredient/safety lists | ✅ Yes | Long lists that would bloat every session if always loaded |
| Hard rules the agent must follow | ❌ No | Put these in AGENTS.md or the agent's system prompt — always loaded |
| Generic coding advice | ❌ No | The model already knows this |
Examples by project type:
| Project | Skills you might create |
|---|---|
| Fintech app | regulatory-rules, plaid-schema, transaction-categories |
| Learning platform | curriculum-schema, progress-diff, assessment-rubric |
| Health/wellness app | domain-safety-rules, user-schema, onboarding-flow |
| E-commerce | product-schema, pricing-rules, inventory-schema |
Three ways a skill gets loaded:
Auto — agent decides based on description match
The agent sees the skill's
descriptionin its available list and decides to load it when the task matches. Works well for unambiguous tasks.Write the database migration for the users table.If your
descriptionsays "use when writing migrations or reviewing the schema", the agent pulls it in.Explicit — you name it in the prompt (guaranteed)
Using the db-schema skill, write the migration for the users table.No ambiguity. Name the skill, it loads immediately every time.
Instructed in the agent's system prompt (always-on for that agent)
For skills that a specific agent needs on every single task, add one line to the agent's system prompt:
Load the `your-domain-skill` skill at the start of every session.This is the right pattern when the skill contains reference tables or data the agent needs for every review — not just sometimes.
In general, you'll have two types of connections:
Domain agents → domain knowledge skills (always-on) A specialist agent that enforces rules should always load the skill containing the reference data those rules depend on. Don't rely on auto-detection when the data is needed every time.
Primary build agent → implementation skills (auto or explicit)
The main build agent loads implementation skills (schema, API contracts, module interfaces) when it detects the task requires them. For critical tasks, name the skill explicitly in the prompt.
Your project will have different skills — a fintech app might have regulatory-rules, plaid-schema, transaction-categories. A learning app might have curriculum-schema, progress-diff. The pattern is the same.
Rule of thumb: Name skills explicitly in your prompt when the task is specific and you want guaranteed loading. Let auto-loading handle exploratory or research tasks.
Place it in the opencode.json file at the root of your project repository. This applies the rule to all agents working in this specific codebase.
{
"permission": {
"skill": {
"*": "allow", // allow all skills
"internal-*": "deny" // except internal ones
}
}
}
- Skills are the right place for detailed domain knowledge — data tables, schemas, rubrics, reference lists. Don't embed this in AGENTS.md (always loaded, wastes tokens) or inline in agent prompts.
- The
descriptionfield drives auto-loading. Make it specific and action-oriented — the agent reads it to decide whether to pull the skill in. - For reference data a specialist agent always needs, instruct it to always load the skill — one line in the system prompt beats relying on auto-detection.
- For guaranteed loading on a specific task, name the skill in your prompt —
"Using the X skill, do Y"is always reliable. SKILL.mdmust be uppercase. Lowercaseskill.mdis silently ignored.
Many tools you use in your stack publish official skills — SKILL.md files maintained by the vendors themselves, kept current as their APIs change. These are identical in format to your custom skills and land in the same .opencode/skills/ directory.
Tools relevant to this stack that publish official skills:
| Tool | What the skill covers | Source |
|---|---|---|
| Langfuse | Tracing setup, prompt management, SDK usage, API queries | github.com/langfuse/skills |
| Mem0 | Python + TypeScript SDK, framework integrations, platform migration | github.com/mem0ai/mem0/tree/main/skills |
| Supabase | Auth, RLS, Edge Functions, migrations, Postgres best practices | github.com/supabase/agent-skills |
How to install — Manual method (recommended for OpenCode):
Manual download gives you full control over where the file lands. Target .opencode/skills/ so it's committed to git with your project.
# [Win] — run from your project root
New-Item -ItemType Directory -Force ".opencode\skills\langfuse"
Invoke-WebRequest -Uri "https://raw.githubusercontent.com/langfuse/skills/main/skills/langfuse/SKILL.md" -OutFile ".opencode\skills\langfuse\SKILL.md"
New-Item -ItemType Directory -Force ".opencode\skills\mem0"
Invoke-WebRequest -Uri "https://raw.githubusercontent.com/mem0ai/mem0/main/skills/mem0/SKILL.md" -OutFile ".opencode\skills\mem0\SKILL.md"
New-Item -ItemType Directory -Force ".opencode\skills\supabase"
Invoke-WebRequest -Uri "https://raw.githubusercontent.com/supabase/agent-skills/main/skills/supabase/SKILL.md" -OutFile ".opencode\skills\supabase\SKILL.md"
# [Linux/macOS]
mkdir -p .opencode/skills/{langfuse,mem0,supabase}
curl -o .opencode/skills/langfuse/SKILL.md \
https://raw.githubusercontent.com/langfuse/skills/main/skills/langfuse/SKILL.md
curl -o .opencode/skills/mem0/SKILL.md \
https://raw.githubusercontent.com/mem0ai/mem0/main/skills/mem0/SKILL.md
curl -o .opencode/skills/supabase/SKILL.md \
https://raw.githubusercontent.com/supabase/agent-skills/main/skills/supabase/SKILL.md
How to install — via the skills CLI:
npx skills add langfuse/skills --skill langfuse
npx skills add https://github.com/mem0ai/mem0 --skill mem0
npx skills add supabase/agent-skills --skill supabase
The CLI will prompt you through an interactive flow:
Ok to proceed? (y)— typeyto install theskillspackage- "Which agents do you want to install to?" — a multi-select list appears showing all supported agents: Amp, Cursor, OpenCode, Warp, Zed, etc. Use arrow keys + Space to select, Enter to confirm. Select OpenCode at minimum.
- "Installation scope" —
Global(available across all projects) orProject(current repo only). Global is fine for tools you use everywhere (Langfuse, Mem0, Supabase). - "Proceed with installation?" — review the summary (shows install path) then confirm.
Where it installs: The CLI installs to ~\.agents\skills\<name> (global) — NOT .opencode/skills/. OpenCode discovers this path automatically, so it works. The skill is then available in every project you open.
After install, verify:
# [Win]
Get-ChildItem "$env:USERPROFILE\.agents\skills" | Select-Object Name
# [Linux/macOS]
ls ~/.agents/skills/
CLI vs Manual — key difference: The CLI installs globally to
~/.agents/skills/(available in all projects). The manual method installs to.opencode/skills/in the current project (committed to git, shared with team). Use CLI for personal tools, manual for project-shared skills.
Commands are prompt shortcuts for repetitive tasks. You can define them in opencode.json or as markdown files in .opencode/commands/.
{
"command": {
"audit": {
"description": "Run security audit on current changes",
"template": "Use the @security-auditor agent to review all files changed in the last git commit. Focus on data handling, auth, and input validation.",
"agent": "build"
},
"diff-design": {
"description": "Review the longitudinal diff module design",
"template": "Use @harness-architect to review the longitudinal diff module. Check: is it truly modality-agnostic? Does it handle the null-prior case? Are threshold configs exposed?",
"agent": "build"
}
}
}
Run in the TUI with /audit or /diff-design.
Examples by project type:
| Project | Commands you might create |
|---|---|
| Any project | /audit (security review), /plan (feature planning template) |
| Fintech app | /compliance-check, /schema-diff, /api-review |
| Health/wellness app | /safety-audit, /domain-review, /data-audit |
| Platform / SaaS | /breaking-change, /migration-plan, /api-contract |
.opencode/commands/audit.md:
---
description: Run security audit on current changes
agent: build
---
Use @security-auditor to review all files changed since the last git commit.
Focus on: data-at-rest risks, auth flows, input validation, image handling.
Write findings with severity and recommendations.
Use $ARGUMENTS to pass text from the command invocation:
---
description: Create a new feature plan
---
Create a detailed implementation plan for: $ARGUMENTS
Break it into:
1. What to build (exact scope)
2. Files to create/modify
3. Dependencies needed
4. Test approach
5. Open questions before starting
OpenCode has a layered permission system. You can set global defaults and override per-agent.
| Value | Behavior |
|---|---|
"allow" |
No prompt, runs immediately |
"ask" |
Prompts for approval before running |
"deny" |
Tool is disabled |
Most permissions default to "allow". But three things are protected without any config needed:
| Protection | Default | What it does |
|---|---|---|
doom_loop |
"ask" |
Fires if the agent calls the exact same tool 3 times in a row with identical input — catches stuck agents |
external_directory |
"ask" |
Fires if any tool tries to read/write a path outside your project folder — protects the rest of your machine |
.env / .env.* files |
"deny" for read |
opencode blocks reading secrets files by default, even without config |
You only need to configure these if you want to change the default (e.g., allow a specific external path, or hard-deny instead of ask).
| Key | Covers |
|---|---|
read |
File reads (matches file path) |
edit |
Write, edit, patch |
bash |
Shell commands (matches the parsed command) |
glob |
Directory glob |
grep |
Search |
list |
Directory listing |
task |
Subagent invocation |
skill |
Skill loading |
webfetch |
HTTP requests |
websearch |
Web search |
lsp |
Language server |
external_directory |
Files outside project worktree |
doom_loop |
Repeated identical tool calls |
Redundant config adds noise. "edit": "allow" and "skill": { "*": "allow" } are both defaults — don't add them. Our project config only sets "bash": "ask" because that's the one thing we want stricter than the default.
// Minimal — only what changes
"permission": {
"bash": "ask" // everything else stays at default
}
You can allow specific commands while asking for others:
{
"agent": {
"build": {
"permission": {
"bash": {
"*": "ask", // ask for everything...
"git status*": "allow", // ...except these (note the * for arguments)
"git log*": "allow",
"git diff*": "allow",
"npm run*": "allow"
}
}
}
}
}
Rule: last matching pattern wins. Put "*" first, specific rules after.
Gotcha:
"git status": "allow"only matches baregit statuswith no arguments. If the agent runsgit status --shortorgit status .it would still trigger an ask prompt. Always use"git status*"(with the wildcard) to cover commands with arguments.
opencode --auto
Automatically approves all "ask" permissions. Explicit "deny" rules are still enforced. Useful for CI or when you fully trust the agent on a task. Toggle it in the TUI via the command palette.
MCP tools follow the pattern servername_toolname. Use wildcards to manage a whole server:
{
"tools": {
"supabase_*": false // disable all Supabase MCP tools globally
},
"agent": {
"build": {
"tools": {
"supabase_*": true // re-enable for build agent
}
}
}
}
Rule of thumb:
- Global (
~/.config/opencode/opencode.json) — your personal preferences, provider keys, shell choice, theme - Project (
opencode.jsonin project root) — MCP servers, project-specific agents and commands, permissions for this codebase
What we put in global config (~/.config/opencode/opencode.jsonc):
{
"$schema": "https://opencode.ai/config.json",
// Windows-specific. Linux/macOS users: omit this line entirely.
"shell": "pwsh",
"autoupdate": "notify"
// Provider keys: if you used /connect inside opencode, keys are already
// stored securely — you don't need them here.
// Only add if managing keys via env vars:
// "provider": {
// "anthropic": { "options": { "apiKey": "{env:ANTHROPIC_API_KEY}" } }
// }
}
What we put in project config (opencode.jsonc):
- MCP servers (context7, github, supabase, cloudflare, sentry)
- Project-level permissions
- Watcher ignore patterns
- Build agent permission overrides
Create the global config directory and file:
# Linux/macOS
mkdir -p ~/.config/opencode
# then create ~/.config/opencode/opencode.jsonc
# Windows PowerShell
New-Item -ItemType Directory -Force "$env:USERPROFILE\.config\opencode"
# then create %USERPROFILE%\.config\opencode\opencode.jsonc
Use
.jsoncnot.jsonfor both global and project configs if you want comments. VS Code will show errors on//comments in.jsonfiles.
A well-structured opencode.jsonc is minimal, commented, and separates machine-specific settings from shared project settings.
The key principle: Only configure what differs from the defaults. Restating defaults ("edit": "allow", "autoupdate": true) adds noise without value.
What belongs in the project config (committed to git):
- MCP servers with
{env:VAR}for all secrets - Project-level model choice
- Permission overrides (typically just
"bash": "ask") - Per-agent permission refinements
- Watcher ignore patterns
What belongs in the global config only (never committed):
"shell"— machine-specific; would break other OS users who clone the repo- Provider API keys (or use
/connectto store them in opencode's credential store) - Personal preferences (theme, autoupdate)
If your project opencode.jsonc was written on Windows, omit the "shell" key when sharing it. OpenCode auto-detects /bin/zsh or /bin/bash on Linux/macOS. Everything else in a well-structured project config is OS-agnostic.
This is the recommended layout for any project using OpenCode. Adapt names to your domain.
your-project/
│
├── opencode.jsonc # Project config — model, MCP, permissions
├── AGENTS.md # Project rules — loaded into every session automatically
├── .env.example # All env vars needed (copy to .env, never commit .env)
│
├── docs/
│ ├── design/ # Prior design artifacts, ADRs, specs (reference only)
│ ├── knowledge/ # Domain reference data (safety lists, rubrics, etc.)
│ └── reference-impl/ # Prior implementation code used as reference
│
└── .opencode/
├── agents/
│ ├── domain-expert.md # Domain safety rules, read-only, always loads domain skill
│ ├── security-auditor.md # OWASP review, read-only, can grep
│ └── docs-writer.md # Technical writing, edits *.md only
│
└── skills/
├── db-schema/
│ └── SKILL.md # Full SQL schema, RLS policies, indexes
├── domain-knowledge/
│ └── SKILL.md # Domain rubrics, safety rules, reference tables
├── onboarding-flow/
│ └── SKILL.md # User onboarding sequence, field names, routing rules
└── module-contracts/
└── SKILL.md # TypeScript interfaces, API contracts, diff logic
Why this layout:
docs/— reference material for humans AND agents to read; not executable code.opencode/agents/— committed to git, shared with team, each enforces one domain of concern.opencode/skills/— lazy-loaded reference library; keeps session context lean- No skills in global config unless the rule applies across ALL your projects
- Run
/connectin the TUI and authenticate with your provider - Run
/initto generate your initialAGENTS.md - Enable Context7 and gh_grep MCP servers — zero setup, immediate value
- Write your
AGENTS.mdarchitecture constraints before writing any code
- Create your domain-expert subagent with the hard rules your domain requires
- Create your
db-schemaskill with your actual SQL schema and RLS policies - Create your security-auditor subagent with
edit: denyand grep allowed - Assign models per agent based on task type (coding, writing, analysis)
- Enable project-specific MCP servers (GitHub, database, etc.) once you have tokens
- Use
planmode (Tabkey) before any significant change — review the plan, then switch tobuild - Reference skills explicitly in prompts for critical tasks:
"Using the db-schema skill, write the migration" - Run
@security-auditorbefore any code that touches user data goes to production - Keep
"bash": "ask"globally — always know what shell commands the agent is running