⏱ 7 min read
Your Claude Code setup is yours. Your team's repo shouldn't have to carry it.
You've spent time building up a nice set of Claude Code agents and skills. Custom reviewers, project-specific helpers, the kind of thing that makes your day a bit smoother. Then you open a pull request and realise: that .claude/ folder is sitting right there in the diff, ready to land in a shared repository that your whole team owns.
Now you have a decision to make, and neither option feels great.
You can commit your .claude/ folder to the repo. It works. But now you're adding personal AI tooling to a shared codebase that your team didn't ask for, and the next time a colleague clones the repo they get your configuration whether they want it or not. That's not a disaster, but it's noise - and noise compounds.
The other option is to leave it local. Simple. Except local means you lose it the moment you switch machines, rebuild your environment, or hand this work off to someone else. Local-only configuration doesn't travel with you.
There's a third path that most people don't think of: keep your agents and skills in a separate, private repository and use symbolic links to make Claude discover them as if they lived inside each project.
The idea is straightforward. Instead of scattering .claude/ folders across every repo you work in, you maintain a single claude-configs repository that mirrors your project structure. Each project gets a folder in there, and your actual project repos get a symlink that points back to it.
Let's take a childish example to make it concrete. Say you have two projects: my-api and my-worker. Your layout ends up looking like this:
workspace/
my-api/ ← shared, committed, team-owned
my-worker/ ← shared, committed, team-owned
claude-configs/ ← private, yours, committed to your own repo
my-api/
.claude/
agents/
skills/
my-worker/
.claude/
agents/
skills/
Claude Code sees my-api/.claude/agents/ and reads your agents from it. It has no idea - and doesn't need to know - that the folder is actually a symlink into claude-configs.
Start by creating the structure and moving your existing agents and skills over.
mkdir claude-configs
cd claude-configs
git init
mkdir -p my-api/.claude/agents
mkdir -p my-api/.claude/skills
mkdir -p my-worker/.claude/agents
mkdir -p my-worker/.claude/skills
Then copy your existing agents and skills into their new home (run these from your workspace/ root, not from inside claude-configs):
cp -r my-api/.claude/agents/* claude-configs/my-api/.claude/agents/
cp -r my-api/.claude/skills/* claude-configs/my-api/.claude/skills/
Now the source of truth lives in claude-configs. Your projects will just point to it.
Remove the real folders from your project and replace them with symbolic links.
On Linux, macOS, or WSL:
cd my-api
# Remove the original folders (content is already in claude-configs)
rm -rf .claude/agents .claude/skills
# Create the symlinks
ln -s ../../claude-configs/my-api/.claude/agents .claude/agents
ln -s ../../claude-configs/my-api/.claude/skills .claude/skills
On Windows (PowerShell - requires Developer Mode or Administrator):
cd my-api
Remove-Item -Recurse -Force .claude\agents, .claude\skills
New-Item -ItemType SymbolicLink -Path ".claude\agents" -Target "..\..\claude-configs\my-api\.claude\agents"
New-Item -ItemType SymbolicLink -Path ".claude\skills" -Target "..\..\claude-configs\my-api\.claude\skills"
Use absolute paths on Windows if relative paths give you trouble. The target has to exist before you create the link, so make sure claude-configs is set up first.
Here's the part that trips people up. If you add .claude/agents to .gitignore, that file gets committed and your teammates see it. That's exactly what we're trying to avoid.
The right tool is .git/info/exclude. It behaves like .gitignore but it's machine-local and never committed. Your colleagues will never know it exists.
# Inside my-api
echo ".claude/agents" >> .git/info/exclude
echo ".claude/skills" >> .git/info/exclude
On Windows, Git sometimes treats directory symlinks as a single file entry rather than a directory. Add both forms to cover your bases:
.claude/agents
.claude/agents/
.claude/skills
.claude/skills/
Then verify:
git status
The agents/ and skills/ folders should not appear. If they do, double-check the entries in .git/info/exclude - a typo there is the usual culprit.
cd claude-configs
git add .
git commit -m "Add Claude agents and skills for my-api and my-worker"
git remote add origin <your-private-repo-url>
git push -u origin main
You now have version history, a backup, and a clean way to reproduce your setup on any machine. Clone claude-configs, re-run the ln -s commands, and you're back to where you were.
Once it's running, you mostly forget it's there. You edit files in claude-configs, commit inside that repo, and the changes are immediately visible through the symlink. No sync step, no copy-paste.
| Action | Where |
|---|---|
| Edit an agent or skill | claude-configs/my-api/.claude/agents/ |
| Save changes | git commit inside claude-configs |
| Use on a new machine | Clone claude-configs, re-run the ln -s commands |
| Add a new project | Create the folder in claude-configs, symlink, exclude |
.git/info/exclude and not .gitignore 🔗This deserves a proper answer because the difference matters.
.gitignore |
.git/info/exclude |
|
|---|---|---|
| Committed to repo | Yes | No |
| Affects teammates | Yes | No |
| Reversible without a commit | No | Yes |
.gitignore is a shared agreement between everyone on the team. When you add something there, you're making a statement on behalf of the project. .git/info/exclude is for exactly this kind of case: personal, local configuration that is nobody else's business.
Windows requires Developer Mode or an Administrator terminal to create symlinks. If you get an access error, that's why.
You also need to make sure Git is configured to handle symlinks correctly:
git config --global core.symlinks true
Without this, Git may check out a symlink as a plain text file containing the target path as a string. The folder won't exist, Claude won't find anything, and you'll spend twenty minutes wondering what went wrong. Set it once, globally, and move on.
This pattern is adapted from Tamir Dresher's approach to using GitHub Copilot's agent framework without touching shared repositories. The same idea maps cleanly onto Claude Code's .claude/agents and .claude/skills structure.
Your tooling is part of how you work, not part of what the team ships. Keeping those things separate has always been the right instinct - symlinks just make it practical.