Introduction

opencode reads its settings from ~/.config/opencode. That folder holds global instructions, agents, skills, and MCP servers. It works well, but it sits outside Git: no history, no review, and no easy way to set up a second machine.

This article shows a simple fix. Keep the files in a Git repository, and link them into ~/.config/opencode. The repository becomes the only place you edit, and every machine reads the same files.

The complete example is in mustapha-zouari/blog8-opencode-config.

The Configuration Folder

What Lives in the Folder

PathPurpose
opencode.json or opencode.jsoncModels, providers, permissions, MCP servers
AGENTS.mdGlobal instructions for every session
instructions.mdOptional extra instructions referenced by the config
agents/Subagent definitions
skills/<name>/SKILL.mdReusable procedures, loaded on demand
plugins/Optional extensions
secrets.envCredentials used by the configuration

Everything except secrets.env can go to Git. A project-level opencode.json(c) and .opencode/ folder are merged on top of these global settings, so a project can override them.

opencode 2 prefers agents/ and still discovers the older agent/ directory.

The Pattern

A GitHub configuration repository linked into the opencode configuration directory and read by opencode at startup A GitHub configuration repository linked into the opencode configuration directory and read by opencode at startup

Three rules:

  • The repository holds the real files.
  • ~/.config/opencode holds links to them.
  • A new machine needs a clone and one script run.

Repository and Setup

The Repository

The repository is a template, so you can clone it or use it as the starting point for your own.

git clone https://github.com/mustapha-zouari/blog8-opencode-config.git "$HOME/SourceRepo/blog8-opencode-config"
blog8-opencode-config/
├── opencode.jsonc
├── AGENTS.md
├── README.md
├── LICENSE
├── agents/
│   ├── reviewer.md
│   └── issue-reporter.md
├── skills/
│   ├── git-commit/
│   │   └── SKILL.md
│   ├── java-code-style/
│   │   └── SKILL.md
│   └── tech-documentation/
│       └── SKILL.md
├── secrets.env.example
├── setup.sh
└── .gitignore

The paths match the folder opencode reads, so every link is a one-to-one mapping.

Linking the Files

Use directory links for folders that grow. A new skill is a new folder inside skills/, and it appears without a new link. Use file links for single files, because the target folder also holds local files, such as secrets.env, that must stay out of Git.

repo="$HOME/SourceRepo/blog8-opencode-config"
target="$HOME/.config/opencode"

ln -s "$repo/agents" "$target/agents"
ln -s "$repo/skills" "$target/skills"
ln -s "$repo/opencode.jsonc" "$target/opencode.jsonc"
ln -s "$repo/AGENTS.md" "$target/AGENTS.md"

Check the result:

$ ls -l ~/.config/opencode
lrwxrwxrwx  agents         -> /home/me/SourceRepo/blog8-opencode-config/agents
lrwxrwxrwx  AGENTS.md      -> /home/me/SourceRepo/blog8-opencode-config/AGENTS.md
lrwxrwxrwx  opencode.jsonc -> /home/me/SourceRepo/blog8-opencode-config/opencode.jsonc
lrwxrwxrwx  skills         -> /home/me/SourceRepo/blog8-opencode-config/skills
-rw-------  secrets.env

Absolute links are easier to read and to debug. Relative links only survive when both folders move together. For a fixed checkout, use absolute links.

The Setup Script

The script must be safe to run more than once, and it must back up what it replaces.

#!/usr/bin/env bash
set -euo pipefail

repo="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
target="${OPENCODE_CONFIG_DIR:-$HOME/.config/opencode}"
mkdir -p "$target"

link() {
    local src="$repo/$1"
    local dst="$target/$1"

    if [ -L "$dst" ] && [ "$(readlink "$dst")" = "$src" ]; then
        echo "already linked: $1"
        return
    fi

    if [ -L "$dst" ]; then
        rm "$dst"
        echo "replaced stale link: $1"
    elif [ -e "$dst" ]; then
        mv "$dst" "$dst.bak"
        echo "backed up: $1 -> $1.bak"
    fi

    ln -s "$src" "$dst"
    echo "linked: $1"
}

for item in opencode.jsonc AGENTS.md agents skills; do
    link "$item"
done

echo "done: $target"

Run it from the repository:

"$HOME/SourceRepo/blog8-opencode-config/setup.sh"

Add plugins to the list if you use it.

Secrets and MCP

Keeping Secrets Out

Credentials stay in one local file outside Git. Three rules:

  1. Commit secrets.env.example, with names and empty values.
  2. Ignore secrets.env in .gitignore.
  3. Reference variables in opencode.json(c), never values.

Create the real file at ~/.config/opencode/secrets.env from the example, then fill in the values.

{
    "$schema": "https://opencode.ai/config.json",
    "mcp": {
        "servers": {
            "playwright": {
                "type": "local",
                "command": ["npx", "-y", "@playwright/mcp@latest"],
            },
            "github": {
                "type": "remote",
                "url": "https://api.githubcopilot.com/mcp/",
                "oauth": false,
                "headers": {
                    "Authorization": "Bearer {env:GITHUB_PAT_TOKEN}",
                },
            },
        },
    },
}

Playwright runs locally and needs Node.js. Install its browser once if it is missing. GitHub is a remote server. This example uses a PAT, so it sets oauth: false; to use OAuth, remove oauth: false and the headers block, then sign in through your client. Keep the PAT scoped to the repositories and actions the agent needs.

opencode replaces {env:NAME} from the shell environment at startup. If the variable is not set, the value becomes an empty string, so a missing export fails quietly. Load the file from your shell profile:

set -a
[ -f "$HOME/.config/opencode/secrets.env" ] && . "$HOME/.config/opencode/secrets.env"
set +a

Portable MCP Entries

  • Use command names from PATH (npx, uvx), not version-pinned paths. The path breaks on the next upgrade.
  • Keep credentials in {env:...} placeholders.
  • Use disabled: true to switch off a server on one machine instead of deleting it.
  • Treat every URL in a public repository as public. Put internal hosts in a private repository.
  • Do not share a local database server with a hardcoded URL. Read the URL from an environment variable or keep the entry in a private repository.
avoid: /home/me/.nvm/versions/node/v22/bin/npx
use:   npx

Skills and Agents

Skills

A skill is a folder with a SKILL.md. The frontmatter needs name and description; the body holds the instructions.

---
name: git-commit
description: Use when committing, branching, or preparing a pull request. Defines Conventional Commits, branch naming, and the verify-before-commit gate.
---

# Git commits and branches

1. Read `git status` and `git diff`; stage explicit paths.
2. Keep one logical change per commit.
3. Use `<type>(<scope>): <subject>` with `feat`, `fix`, `docs`, `test`, `refactor`, `chore`, `perf`, or `ci`.
4. Branch from the default branch: `feat/<topic>`, `fix/<topic>`, `docs/<topic>`.
5. Run the project's verification command before committing code.
6. Never commit secrets, dependencies, or generated files.

The reference repository also shares two style skills:

SkillWhat it enforces
java-code-stylevar when obvious, final, SOLID, small methods, domain-specific names
tech-documentationConcise Markdown, consistent terminology, Mermaid and clean SVG diagrams

The shared git-commit skill is a generic version of a project-specific skill that referenced docs/CONVENTIONS.md and npm run verify. A skill that names a specific repository’s paths or commands belongs to that project, not the global config.

Agents and Subagents

An agent is a Markdown file with frontmatter and a system prompt. Set mode: subagent so the primary agent can launch it as a child session.

---
description: Reviews changes for correctness, regressions, and missing tests. Read-only.
mode: subagent
permissions:
    - action: edit
      resource: '*'
      effect: deny
---

Review the current diff. Report findings in severity order with file and line references. Do not modify files.

The description matters: the primary agent reads it when choosing which subagent to launch. Keep it specific and start with the action.

The reference repository also includes agents/issue-reporter.md. It uses the Playwright MCP server to reproduce browser bugs and the GitHub MCP server to open one issue with evidence.

Share generic agents globally. Keep agents that name internal paths, commands, or business rules in the project’s .opencode/agents/ folder. A project agent can read the project’s own skills and conventions without exposing them to every repository.

Sharing and Maintenance

Sharing with a Team

  • Use a private repository if the configuration names internal systems. A public one is fine for generic skills.
  • Everyone clones the reference repository or their own fork and runs the setup script. Nobody copies files.
  • Review changes in a pull request. A skill or AGENTS.md change alters agent behaviour.
  • Keep personal values, such as model choice, out of the shared file, or read them from environment variables.

Pitfalls

  • Editors can replace the link — some tools save by writing a new file and renaming it. The rename replaces the link with a regular file, and edits stop reaching the repository. Check with test -L and git status.
  • Moving the repository breaks every link — ls -l shows the old target. Run the setup script again; it replaces stale links.
  • Windows only — symlinks need Developer Mode or administrator rights. Junctions avoid that for folders, but not for the two file links. Linux, macOS, and WSL use normal symlinks.

Checking the Setup

  1. ls -l ~/.config/opencode shows links, not copies.
  2. opencode starts and lists the expected agents, skills, and MCP servers.
  3. opencode mcp list shows the Playwright and GitHub servers and their connection status.
  4. Editing a skill changes git status in the repository.
  5. A second machine, after a clone and the setup script, shows the same links.

Conclusion

Agent configuration is code. Put it in a repository and link it into place.

  • Directory links for folders, file links for single files.
  • A setup script that backs up instead of deleting.
  • Secrets outside Git, referenced as environment variables.
  • Portable MCP entries with commands from PATH.

The reference repository is the starting point: clone it, run the setup script, and replace the example skills and agents with your own.

The result is one source of truth, history and review for free, and a new machine ready after a clone and one script run.