Lukas WolfsteinerOne Skills Brain for Codex, Claude, Cursor, and Copilot with Chezmoi When you use multiple...
When you use multiple coding agents across multiple machines, the same prompt often behaves differently for a simple reason: your agent skills and MCP wiring drift over time.
Typical failure modes:
The initial problem is not just dotfiles sync. It is maintaining one consistent agent behavior contract across Codex, Claude, Cursor, and Copilot despite different config formats and file locations.
The solution in this guide is to separate concerns:
~/.agent-skills/skills
Why this works:
All path references below follow:
Codex
User config: ~/.codex/config.toml
Project config: .codex/config.toml
Skills: .agents/skills and SKILL.md folders
Claude Code
Main MCP config: ~/.claude.json
Dedicated MCP file: ~/.claude/mcp_servers.json (lower precedence than ~/.claude.json)
Settings: ~/.claude/settings.json, .claude/settings.json, .claude/settings.local.json
Sub-agents: ~/.claude/agents/, .claude/agents/
Cursor
MCP config: ~/.cursor/mcp.json, .cursor/mcp.json
Skills: ~/.cursor/skills/, .cursor/skills/
Compatibility skills paths include ~/.codex/skills/, ~/.claude/skills/
GitHub Copilot
MCP config: ~/.copilot/mcp-config.json
Skills: ~/.copilot/skills/, .copilot/skills/
Use symlinks for shared skills directories.
readlink)symlink_ filesThis is the chezmoi source-state tree ($(chezmoi source-path)):
~/.local/share/chezmoi/
├── dot_agent-skills/
│ ├── README.md
│ ├── skills/
│ │ ├── commit-style/
│ │ │ └── SKILL.md
│ │ └── test-triage/
│ │ └── SKILL.md
│ └── mcp/
│ ├── codex-mcp.toml.tmpl
│ ├── generic-mcp.json.tmpl
│ └── copilot-mcp.json.tmpl
├── dot_codex/
│ ├── config.toml.tmpl
│ └── symlink_skills.tmpl
├── private_dot_claude.json.tmpl
├── dot_claude/
│ └── settings.json.tmpl
├── dot_cursor/
│ ├── mcp.json.tmpl
│ └── symlink_skills.tmpl
└── dot_copilot/
├── mcp-config.json.tmpl
└── symlink_skills.tmpl
Chezmoi attribute reminders:
dot_ -> leading dot in targetprivate_ -> stricter permissions in targetsymlink_ -> create a symlink target.tmpl -> template renderingchezmoi init git@github.com:<you>/<dotfiles>.git
cd "$(chezmoi source-path)"
# Optional: import existing files
chezmoi add ~/.codex/config.toml
chezmoi add ~/.claude.json
chezmoi add ~/.claude/settings.json
chezmoi add ~/.cursor/mcp.json
chezmoi add ~/.copilot/mcp-config.json
chezmoi diff
chezmoi apply -v
git add .
git commit -m "feat: unify skills and mcp adapters"
git push
chezmoi init git@github.com:<you>/<dotfiles>.git
chezmoi update -v
chezmoi edit ~/.codex/config.toml
chezmoi diff
chezmoi apply -v
Create all skills once in dot_agent-skills/skills.
Example dot_agent-skills/skills/commit-style/SKILL.md:
# commit-style
## Goal
Create clear conventional commits.
## Rules
- Keep subject under 50 chars.
- Use imperative present tense.
- Explain user impact in body.
After apply, this becomes ~/.agent-skills/skills/... and is your canonical skill bank.
Keep one MCP intent and render per tool schema.
dot_agent-skills/mcp/codex-mcp.toml.tmpl
[mcp_servers.filesystem]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-filesystem", "{{ .homeDir }}/.agent-skills/skills"]
[mcp_servers.git]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-git", "--repository", "{{ .homeDir }}/Developer"]
dot_agent-skills/mcp/generic-mcp.json.tmpl (Claude/Cursor style)
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "{{ .homeDir }}/.agent-skills/skills"]
},
"git": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-git", "--repository", "{{ .homeDir }}/Developer"]
}
}
}
dot_agent-skills/mcp/copilot-mcp.json.tmpl
{
"mcpServers": {
"filesystem": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "{{ .homeDir }}/.agent-skills/skills"]
},
"git": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-git", "--repository", "{{ .homeDir }}/Developer"]
}
}
}
dot_codex/config.toml.tmpl
model = "gpt-5-codex"
approval_policy = "on-request"
[mcp_servers.filesystem]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-filesystem", "{{ .homeDir }}/.agent-skills/skills"]
[mcp_servers.git]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-git", "--repository", "{{ .homeDir }}/Developer"]
dot_codex/symlink_skills.tmpl
{{ .homeDir }}/.agent-skills/skills
Result: ~/.codex/skills -> ~/.agent-skills/skills
Use this mainly as a compatibility anchor for tools that read Codex-style skills paths.
For Codex itself, keep project skills in .agents/skills when you want repository-local behavior.
private_dot_claude.json.tmpl
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "{{ .homeDir }}/.agent-skills/skills"]
},
"git": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-git", "--repository", "{{ .homeDir }}/Developer"]
}
}
}
dot_claude/settings.json.tmpl
{
"permissions": {
"allow": [],
"deny": []
}
}
For project-level MCP, use .mcp.json with the same filesystem path.
For agent logic, prefer documented .claude/agents/ over .claude/skills/.
dot_cursor/mcp.json.tmpl
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "{{ .homeDir }}/.agent-skills/skills"]
},
"git": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-git", "--repository", "{{ .homeDir }}/Developer"]
}
}
}
dot_cursor/symlink_skills.tmpl
{{ .homeDir }}/.agent-skills/skills
Result: ~/.cursor/skills -> ~/.agent-skills/skills
dot_copilot/mcp-config.json.tmpl
{
"mcpServers": {
"filesystem": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "{{ .homeDir }}/.agent-skills/skills"]
},
"git": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-git", "--repository", "{{ .homeDir }}/Developer"]
}
}
}
dot_copilot/symlink_skills.tmpl
{{ .homeDir }}/.agent-skills/skills
Result: ~/.copilot/skills -> ~/.agent-skills/skills
readlink ~/.codex/skills
readlink ~/.cursor/skills
readlink ~/.copilot/skills
ls ~/.agent-skills/skills
chezmoi status
chezmoi diff
chezmoi verify
Expected: all symlinks resolve to ~/.agent-skills/skills, and each MCP file targets that same path.
skills-sync
I reviewed skills-sync feature usage and mapped it to chezmoi primitives.
Single source workspace
Chezmoi equivalent: one source state with canonical dot_agent-skills/skills.
Symlink distribution
Chezmoi equivalent: symlink_*.tmpl entries per tool.
Profile switching (use <profile>)
Chezmoi equivalent: .chezmoidata/profiles.yaml + template conditionals.
Runtime materialization (sync)
Chezmoi equivalent: chezmoi apply -v.
Seed content (init --seed)
Chezmoi equivalent: commit baseline skills/templates in source state.
Upstream ingestion (add-upstream, add-skill)
Chezmoi equivalent: external_ or scripted run_onchange_ sync jobs.
Drift/inventory (agents drift, agents inventory)
Chezmoi equivalent: chezmoi status/diff/verify plus symlink checks.
Create .chezmoidata/profiles.yaml:
active_profile: personal
profiles:
personal:
dev_root: "~/Developer"
work:
dev_root: "~/Work"
Use in template:
{{- $p := index .profiles .active_profile -}}
[mcp_servers.git]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-git", "--repository", "{{ $p.dev_root }}"]
Switch profile by changing active_profile, then:
chezmoi apply -v
run_onchange_after_50-sync-upstream-skills.sh.tmpl
#!/usr/bin/env bash
set -eu
ROOT="$HOME/.agent-skills/skills/upstream"
mkdir -p "$ROOT"
sync_repo() {
name="$1"
url="$2"
dir="$ROOT/$name"
if [ -d "$dir/.git" ]; then
git -C "$dir" pull --ff-only
else
git clone "$url" "$dir"
fi
}
sync_repo "matlab_skills" "https://github.com/matlab-deep-learning/llm-agents-with-mcp.git"
Apply with:
chezmoi apply -v
private_ for files that may carry sensitive values.{{ .homeDir }} in templates for cross-machine portability.This pattern gives one skill bank, one MCP intent, four adapters, and minimal drift.
Cheers,
Lukas