Symlink Manager — Cross-Platform Skill
The Core Problem
Git symlinks break across platforms because:
| Issue | macOS/Linux | Windows |
|---|---|---|
| Git setting | core.symlinks=true (default) |
core.symlinks=false (default, unless Dev Mode enabled) |
| Link type | ln -s symlink |
NTFS symlink or Junction Point or Hardlink |
| Permissions | Any user | Requires Developer Mode or admin elevation |
| Git behaviour | Stores as symlink object | Stores as plain text file containing the target path |
When core.symlinks=false, Git checks out a symlink as a plain text file whose contents are the target path. When you then git pull on the other machine, that text file arrives instead of a real link — silent, no error.
The Hardlink Trap
When core.symlinks=false and Developer Mode is disabled, symlinks can be accidentally replaced with hardlinks. Hardlinks cannot be committed as symlinks to Git, so they break the cross-platform workflow:
- macOS user commits real symlinks (core.symlinks=true by default)
- Windows user with Developer Mode off: symlinks checkout as plain-text files, then get replaced with hardlinks
- Windows user pushes: Git sees plain-text file, commits it as a file, not a symlink
- macOS user pulls: receives text file instead of symlink — broken
Solution: Always use real symlinks, never hardlinks. Enable Developer Mode on Windows first, then use /create-sym-link command to create proper symlinks that Git recognizes.
Workflow
Step 1 — Diagnose the environment
Run the diagnosis script first. It checks:
- OS and Python version
git config core.symlinks(local + global)- Whether Developer Mode is active (Windows)
- Whether the script is running as admin (Windows)
- Existing symlinks vs broken links vs text-file stand-ins in the repo
python ./scripts/symlink_manager.py diagnose
Step 2 — Fix Git config
On Windows (needs Developer Mode enabled first, or run as admin):
git config core.symlinks true
git rm --cached -r . # unstage everything
git reset --hard # re-checkout with symlinks honoured
On macOS / Linux (usually already correct):
git config --get core.symlinks # should say "true"
Add a .gitattributes line to lock symlinks in the repo:
* text=auto
*.symlink -text
Step 3 — Create symlinks (Automatic Platform Detection)
For cross-platform teams: Use the Python script — it automatically handles OS differences without requiring bash, PowerShell, or .sh scripts.
Use the /create-sym-link command in Claude Code for an interactive workflow:
/create-sym-link
This prompts for source and destination paths and uses the Python symlink manager.
Or use the Python script directly (works on Windows, macOS, and Linux):
# Create a single symlink (automatically detects OS)
python ./scripts/symlink_manager.py create --src plugins/plugin-manager/scripts/plugin_installer.py --dst plugins/plugin-manager/skills/plugin-installer/scripts/plugin_installer.py
# Re-create ALL links from the manifest
python ./scripts/symlink_manager.py restore
# Audit: list broken or missing links
python ./scripts/symlink_manager.py audit
# Full diagnosis of the environment
python ./scripts/symlink_manager.py diagnose
The Python script automatically:
- ✓ macOS/Linux: Creates true symlinks
- ✓ Windows with Developer Mode: Creates true symlinks
- ✓ Windows without Developer Mode: Falls back to junctions (dirs) or hardlinks (files)
- ✓ No external shell scripts needed — pure Python with standard library only
Critical: If symlinks were created as hardlinks or plain-text files:
- Delete them:
rm plugins/plugin-manager/skills/*/scripts/plugin_installer.py - Enable Developer Mode on Windows (Settings → System → For Developers)
- Set git config:
git config core.symlinks true - Use
/create-sym-linkcommand orpython ./scripts/symlink_manager.py create ... - Commit:
git add -A && git commit -m "fix: replace hardlinks with proper symlinks"
Step 4 — Bulk Fix Symlinks in Folders
If you have multiple text-file stand-ins in a folder hierarchy, use the bulk fixer:
# Scan folder, generate inventory, and fix all broken symlinks
python ./scripts/bulk_symlink_fixer.py plugins/plugin-manager/skills/maintain-plugins/scripts
The bulk fixer:
- Scans the folder recursively for text-file stand-ins and broken symlinks
- Generates an inventory report (count and list of issues)
- Calls
symlink_manager.py createin a loop to fix each one - Reports summary (fixed, skipped, failed counts)
Step 5 — Commit the manifest
Commit symlinks.json to the repo. On a fresh checkout (or after a git pull breaks links on Windows), any developer runs:
python ./scripts/symlink_manager.py restore
…and all links are recreated correctly for their platform.
Windows-Specific Notes
- Developer Mode (Settings → System → For Developers) allows unprivileged symlink creation. Recommend enabling this for all devs on the team.
- Without Developer Mode, the script falls back to Junction Points for directories and hardlinks for files. This covers 90% of use-cases but junctions only work within the same volume.
- Running the script as Administrator bypasses the Developer Mode requirement entirely.
- Set
git config --global core.symlinks trueafter enabling Developer Mode.
macOS/Linux Notes
- Symlinks always work. The main risk is accidentally committing with
core.symlinks=falseinherited from a shared config. - Run
git config --list --show-origin | grep symlinksto see where the setting comes from.
Reference Files
references/troubleshooting.md— Common error messages and fixesscripts/symlink_manager.py— The cross-platform Python script.agent/rules/plugin-architecture-policy.md— Section 5: Mandatory Symlink Workflow & Cross-Platform Protocol
Read references/troubleshooting.md when the user reports specific error messages.
Output Conventions
- Always show the user the diagnose output before making changes
- When creating links, print a table: Source → Target, Type (symlink/junction/hardlink), Status ✓/✗
- When restoring from manifest, report counts: X created, Y skipped (already exist), Z failed
- On failure, always print the OS error and the recommended fix