Run multiple OpenCode agent stacks without letting them conflict.
Agents, skills, plugins, MCP servers, and instructions often work best as stacks. But as those stacks grow, they can interfere with each other: different prompts, tools, plugins, permissions, and environment variables do not always belong in one shared setup.
OpenCode already has the right foundation for this: a profile config can layer on top of the global config instead of replacing it. That makes OpenCode especially good for multi-agent workflows compared with tools that only support fully separate config directories.
opencode-profile-kit turns that foundation into isolated, switchable profiles for each stack, while preserving the shared base that OpenCode itself provides: sessions, common plugins, common skills, and global configuration.
The core model is:
OpenCode global config + OpenCode profile config = shared base + isolated stack
Use it to:
- keep conflicting agent/skill stacks isolated
- switch between different OpenCode runtimes quickly
- share common sessions, plugins, skills, and base config
- keep profile-specific plugins, skills, MCP servers, and env vars separate
- experiment with new stacks without breaking your daily setup
Everything stays local, transparent, and scriptable.
- Install
- Layout
- Configuration
- Commands
- Basic Usage
- Installing Into a Profile
- Upgrade Recipes
- Migration Archives
- Rewriting Hardcoded Paths
- Clone
- Shared Config Links
- Profile Launcher Commands
- Registry
- Completion
- Optional Shell Helpers
- Doctor
curl -fsSL https://raw.githubusercontent.com/flyinghail/opencode-profile-kit/main/install.sh | bashocp is the default CLI command name, but installation can use a custom command name:
curl -fsSL https://raw.githubusercontent.com/flyinghail/opencode-profile-kit/main/install.sh | CLI_NAME=ocpk bashManual local install from checkout:
mkdir -p ~/.local/bin
ln -sfn "$PWD/bin/ocp" ~/.local/bin/ocpDefaults follow XDG-style separation:
~/.config/opencode-profile-kit/config.env # user-editable config
~/.local/share/opencode-profile-kit/state/ # registry/state
~/.local/share/opencode-profile-kit/bin/ocp # installed source by install.sh
~/.opencode-profiles/<profile>/ # profile directories
Default profile directory:
~/.opencode-profiles
OpenCode global config directory used by link:
~/.config/opencode
Create ~/.config/opencode-profile-kit/config.env to override defaults:
OC_PROFILES_DIR="$HOME/.opencode-profiles"
OC_BIN_DIR="$HOME/.local/bin"OC_PROFILES_DIR must be under $HOME.
ocp new <profile> [--from <template>]
ocp clone <src> <dst> [--full]
ocp rename <old> <new>
ocp remove <profile> [--delete-dir]
ocp run <profile> [opencode args...]
ocp shell <profile>
ocp exec <profile> -- <command...>
ocp exec <profile> --stdin
ocp env <profile> [bash|zsh]
ocp env list <profile>
ocp env set <profile> KEY=value
ocp env remove <profile> KEY
ocp clear [bash|zsh]
ocp which
ocp link <profile> <path> [--force]
ocp link-all <path> [--force]
ocp bin create <profile> <command-name>
ocp bin list [profile]
ocp bin remove <command-name>
ocp bin repair <command-name|--all>
ocp upgrade <profile>
ocp upgrade -g
ocp upgrade init [-f|--force] [--rewrite-paths] [--no-isolate-skills] <profile>
ocp upgrade init [-f|--force] -g
ocp upgrade edit <profile>
ocp upgrade edit -g
ocp upgrade show <profile>
ocp upgrade show -g
ocp external add <profile> <path>
ocp external add -g <path>
ocp external list <profile>
ocp external list -g
ocp external remove <profile> <path>
ocp external remove -g <path>
ocp export [-f|--force] <profile> [file.tar.gz]
ocp export [-f|--force] -a [file.tar.gz]
ocp export [-f|--force] -g [file.tar.gz]
ocp export [-f|--force] -a -g [file.tar.gz]
ocp import [-f|--force|--skip-existing|-y|--yes] [file.tar.gz]
ocp rewrite-paths <profile> [path-suffix]
ocp list
ocp path <profile>
ocp refresh
ocp doctor [profile]
ocp completion [bash|zsh] [--install]
ocp config
Create a profile:
ocp new my-profileRun OpenCode with that profile:
ocp run my-profilePass arguments to OpenCode:
ocp run my-profile --port 0Run any command inside the profile environment:
ocp exec my-profile -- env | grep OPENCODE_CONFIG_DIR
ocp exec my-profile -- zshocp shell <profile> starts ${SHELL:-/bin/sh} in the profile directory with OPENCODE_CONFIG_DIR set.
Store profile-specific environment variables in .ocp-env:
ocp env set my-profile ANTHROPIC_SMALL_FAST_MODEL=claude-haiku
ocp env list my-profile
ocp env remove my-profile ANTHROPIC_SMALL_FAST_MODELPresets are applied by ocp run, ocp shell, ocp exec, and ocp env <profile>. OPENCODE_CONFIG_DIR is always set by ocp and cannot be overridden by .ocp-env.
Use --stdin when an install command needs multiple shell lines:
ocp exec my-profile --stdin <<'SCRIPT'
echo "$OPENCODE_CONFIG_DIR"
npx some-package install
SCRIPTFor installers that write to ~/.config/opencode, prefer an explicit ocp upgrade recipe.
ocp rename <old> <new> renames the profile directory and updates registry, manifest, and registered launcher commands. It warns if markdown files still contain the old profile path.
Show the current profile from OPENCODE_CONFIG_DIR:
ocp whichMost OpenCode ecosystem tools install configuration, plugins, MCP servers, agents, or runtime files into the current OPENCODE_CONFIG_DIR.
To install something into a specific profile:
ocp exec my-profile -- npx <package> installYou can also enter the profile environment first:
ocp shell my-profileThen run the installer normally:
npx <package> installEverything should now install into:
~/.opencode-profiles/my-profile
instead of the global OpenCode config directory.
ocp upgrade <profile> runs $OC_PROFILES_DIR/<profile>/.ocp-recipes with cwd set to the profile directory.
Create a guided recipe:
ocp upgrade init my-profileEdit or view it:
ocp upgrade edit my-profile
ocp upgrade show my-profileGlobal OpenCode config uses -g:
ocp upgrade init -g
ocp upgrade -gRecipes are Bash scripts. Profile recipes get these variables:
OCP_TARGET=profile
OCP_PROFILE=my-profile
OCP_PROFILE_DIR=$HOME/.opencode-profiles/my-profile
OCP_GLOBAL_DIR=$HOME/.config/opencode
OPENCODE_CONFIG_DIR=$OCP_PROFILE_DIRGlobal recipes get these variables:
OCP_TARGET=global
OCP_PROFILE=
OCP_PROFILE_DIR=
OCP_GLOBAL_DIR=$HOME/.config/opencode
OPENCODE_CONFIG_DIR=$OCP_GLOBAL_DIRProfile recipes isolate newly installed agent skills by default. Before running the recipe, ocp records the top-level entries under ~/.agents/skills. Afterward, it moves only newly added entries into the profile's skills/ directory. Skills that were already global are left untouched. This cleanup also runs when the recipe fails, so a skill installed before a later failing command does not remain global.
Recipe settings are optional headers before the commands. Omitted settings use the profile defaults: isolate-skills=true and rewrite-paths=false. Only overrides need to be written:
rewrite-paths=true
isolate-skills=falseUse --rewrite-paths with upgrade init to write the first override, or --no-isolate-skills to write the second. Path rewriting runs only after the recipe and skill isolation both succeed. These settings are invalid for global recipes, whose defaults are both false.
These examples create the profile recipe and then run it. If the recipe already exists, add --force to replace it.
Install oh-my-openagent into oma:
ocp new oma
ocp upgrade init oma <<'EOF'
bunx oh-my-openagent install
EOF
ocp upgrade omaInstall the stable oh-my-opencode-slim@v2 into oos and enable background subagents for that profile:
ocp new oos
ocp upgrade init oos <<'EOF'
bunx oh-my-opencode-slim@latest install
ocp env set "${OCP_PROFILE}" OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=1
EOF
ocp upgrade oosInstall the skills from mattpocock/skills into matt without exposing them globally:
ocp new matt
ocp upgrade init --rewrite-paths matt <<'EOF'
npx skills add mattpocock/skills
EOF
ocp upgrade mattThis also rewrites generated markdown references from ~/.agents/skills/... to ~/.opencode-profiles/matt/skills/.... Run npx skills add multiple times in the same recipe when a profile needs skills from multiple sources.
Install gstack into gs:
ocp new gs
ocp upgrade init --rewrite-paths gs <<'EOF'
if [ ! -d ~/gstack/.git ]; then
git clone --single-branch --depth 1 https://github.com/garrytan/gstack.git ~/gstack
fi
cd ~/gstack
git pull --ff-only
./setup --host opencode --prefix
mkdir -p "${OCP_PROFILE_DIR}/skills"
rm -rf "${OCP_PROFILE_DIR}"/skills/gstack*
mv "${OCP_GLOBAL_DIR}"/skills/gstack* "${OCP_PROFILE_DIR}/skills/"
EOF
ocp upgrade gsExport one profile:
ocp export my-profileExport all profiles, global OpenCode config, or both:
ocp export -a
ocp export -g
ocp export -a -gDefault archive names are based on what is exported:
<profile>.ocp-profile.tar.gz
all.ocp.tar.gz
global.ocp-global.tar.gz
all-with-global.ocp.tar.gz
Use -f or --force to overwrite an existing archive path.
--all and --global are long aliases for -a and -g.
Global migration archives include ~/.config/opencode plus opencode-profile-kit global metadata from ~/.config/opencode-profile-kit/global, including the global manifest and global upgrade recipe when present.
External allowlists let a profile or the global config include additional files or directories under $HOME that live outside the main profile/global config tree:
ocp external add my-profile ~/.local/share/my-tool
ocp external add -g ~/.local/share/global-tool
ocp external list my-profile
ocp external list -g
ocp external remove my-profile ~/.local/share/my-tool
ocp external remove -g ~/.local/share/global-toolImport restores the archive into the current $HOME and rebases external allowlist paths from the source home to the destination home:
ocp import archive.ocp.tar.gzWhen an import destination already exists, use one conflict policy:
ocp import -f archive.ocp.tar.gz # overwrite existing destinations
ocp import --force archive.ocp.tar.gz # same as -f
ocp import --skip-existing archive.ocp.tar.gz # keep existing destinations
ocp import -y archive.ocp.tar.gz # answer yes to prompts
ocp import --yes archive.ocp.tar.gz # same as -yWith --skip-existing, an existing profile or global config skips that whole unit, including its external path restore and manifest rebasing. Existing external destinations are also kept when their owning profile/global unit is imported.
If no archive is provided interactively, ocp import looks for one .ocp archive in the current directory and prompts when multiple candidates exist.
Some installers generate files containing hardcoded references to the global OpenCode config path.
These references are commonly embedded in generated agents/, skills/, and commands/ markdown files.
Currently, rewrite-paths only modifies *.md files.
These references may appear in different forms:
~/.config/opencode/skills
$HOME/.config/opencode/agents
${HOME}/.config/opencode/commands
...
Use rewrite-paths to rewrite them into the target profile directory:
ocp rewrite-paths my-profileBy default, this rewrites references under:
/.config/opencode
to the profile directory suffix:
/.opencode-profiles/my-profile
Only markdown files (*.md) are modified.
You can also rewrite only a specific subtree:
ocp rewrite-paths my-profile /.config/opencode/skills
ocp rewrite-paths my-profile /.config/opencode/agents
ocp rewrite-paths my-profile /.config/opencode/commandsFor skills installed by npx skills add, the exact agent skills root is also supported:
ocp rewrite-paths my-profile /.agents/skillsThis maps ~/.agents/skills/... to ~/.opencode-profiles/my-profile/skills/... rather than preserving the .agents directory.
For new global-path installer workflows, prefer an explicit recipe under Upgrade Recipes and use --rewrite-paths when generated markdown should be rewritten after a successful upgrade. With the default skill isolation enabled, upgrade rewrites both /.config/opencode and /.agents/skills after moving newly installed skills.
Upgrade recipe workflow:
ocp upgrade init my-profile
ocp upgrade edit my-profile
ocp upgrade my-profile
ocp rewrite-paths my-profileCreate a new profile by cloning an existing profile:
ocp clone my-profile other-profilenew --from is a convenience alias for clone:
ocp new other-profile --from my-profileBy default, clone preserves symlinks and skips common runtime/cache paths:
session tmp logs semantic index cache .cache
Copy everything:
ocp clone my-profile other-profile --fullLink a file or directory from ~/.config/opencode into a profile:
ocp link my-profile AGENTS.mdDo not overwrite existing profile paths unless --force is supplied:
ocp link my-profile AGENTS.md --forceLink into all registered profiles:
ocp link-all AGENTS.md
ocp link-all AGENTS.md --forceCreate a dedicated launcher command:
ocp bin create my-profile oc-my-profileThen run:
oc-my-profile
oc-my-profile --port 0The launcher calls the installed ocp path and runs:
ocp run my-profile "$@"Profiles are indexed in:
~/.local/share/opencode-profile-kit/state/profiles.tsv
List profiles:
ocp listPrint profile path:
ocp path my-profileRemove from registry:
ocp remove my-profileRemove from registry and delete the profile directory:
ocp remove my-profile --delete-dirRebuild registry from OC_PROFILES_DIR:
ocp refreshPrint completion script for the current shell:
ocp completionInstall completion for the current shell:
ocp completion --installThe shell is automatically detected from:
$SHELL
You can also explicitly specify the shell:
ocp completion bash
ocp completion zsh
ocp completion bash --install
ocp completion zsh --installBash completion is installed to the standard user completion directory:
${BASH_COMPLETION_USER_DIR:-${XDG_DATA_HOME:-$HOME/.local/share}/bash-completion}/completions/<command-name>
This does not modify ~/.bashrc. It requires bash-completion to be installed and loaded by your shell.
Zsh completion is installed under:
${XDG_DATA_HOME:-$HOME/.local/share}/opencode-profile-kit/completions/zsh/_<command-name>
For zsh, --install appends an idempotent marked block to ~/.zshrc with the required fpath / compinit setup.
The generated completion dynamically reads profiles from:
ocp listso newly created profiles do not require reinstalling completion.
ocp env and ocp clear print shell code.
To modify the current shell environment, use eval.
For Bash in ~/.bashrc:
ocd() {
eval "$(ocp env "$1" bash)"
}
ocr() {
eval "$(ocp clear bash)"
}For Zsh in ~/.zshrc:
ocd() {
eval "$(ocp env "$1" zsh)"
}
ocr() {
eval "$(ocp clear zsh)"
}Then use:
ocd my-profile
npx <package> install
ocrThese helpers internally use eval.
Check installation health:
ocp doctorCheck one profile:
ocp doctor my-profileIt reports:
- resolved config/state/profile/bin paths
- whether
opencodeis found - missing manifest
- broken symlinks
- common runtime/cache paths inside a profile