How the Skills CLI Differentiates Between Symlink and Copy Installation Modes
The skills CLI distinguishes between symlink and copy installation modes through explicit --mode flags, with symlink as the default that gracefully falls back to copy when filesystem permissions block symlink creation.
The skills CLI from vercel-labs/skills provides two fundamentally different ways to install AI agent skills: symlink mode creates a central canonical copy with agent-specific links, while copy mode duplicates files directly into each agent directory. Understanding how the skills CLI differentiates between symlink and copy installation modes helps you optimize for development workflows, disk space, and cross-platform compatibility.
The Core Distinction: Canonical Storage vs. Direct Duplication
The skills CLI's installation mode differentiation centers on one architectural decision: whether to maintain a canonical centralized copy of each skill.
Symlink Mode (Default)
In symlink mode, the CLI:
- Copies skill files once to a canonical directory at
~/.agents/skills/<skill> - Creates a filesystem symlink from the agent-specific directory (e.g.,
~/.claude/skills/<skill>) to that canonical location - Falls back automatically to copy mode if symlink creation fails, recording
symlinkFailed: true
This mode is selected when no --mode flag is provided, or when explicitly passing --mode symlink.
Copy Mode
In copy mode, the CLI:
- Bypasses the canonical directory entirely
- Copies skill files directly into each agent-specific directory
- Creates no symlinks whatsoever
This mode is selected via --mode copy or -m copy.
Where the Differentiation Logic Lives
The skills CLI differentiates between symlink and copy installation modes in src/installer.ts, with four key implementation points.
1. Type Definition
The valid modes are strictly typed:
export type InstallMode = 'symlink' | 'copy';
Source: src/installer.ts lines 23-24
2. Default Resolution
All installation entry points—installSkillForAgent, installRemoteSkillForAgent, installWellKnownSkillForAgent, and installBlobSkillForAgent—resolve the mode with a simple fallback:
const installMode = options.mode ?? 'symlink';
Source: src/installer.ts lines 43-45
3. Copy Mode Branch
When installMode === 'copy', the code takes an early return path that skips all canonical directory logic:
if (installMode === 'copy') {
await cleanAndCreateDirectory(agentDir);
await copyDirectory(skill.path, agentDir);
return { success: true, path: agentDir, mode: 'copy' };
}
Source: src/installer.ts lines 64-71
4. Symlink Mode with Fallback
The symlink path first establishes the canonical copy, then attempts symlink creation:
await cleanAndCreateDirectory(canonicalDir);
await copyDirectory(skill.path, canonicalDir);
// ...
const symlinkCreated = await createSymlink(canonicalDir, agentDir);
If createSymlink fails, the CLI automatically falls back to copy mode while preserving the canonical path information:
if (!symlinkCreated) {
await cleanAndCreateDirectory(agentDir);
await copyDirectory(skill.path, agentDir);
return {
success: true,
path: agentDir,
canonicalPath: canonicalDir,
mode: 'symlink',
symlinkFailed: true,
};
}
Source: src/installer.ts lines 92-107
Special Case: Universal Agents
The skills CLI includes an optimization for universal agents like Claude and Cursor. When installing globally for these agents, the canonical directory is the agent directory, making symlinks redundant.
This check occurs in src/installer.ts:
if (isGlobal && isUniversalAgent(agentType)) {
return { success: true, path: canonicalDir, canonicalPath: canonicalDir, mode: 'symlink' };
}
Source: src/installer.ts lines 81-84
The isUniversalAgent determination comes from src/agents.ts, which defines which agent types support global skill sharing.
Practical Usage Examples
Default Symlink Installation
# Installs skill with symlink to canonical location
skills add my-skill
Explicit Copy Mode
# Bypasses canonical directory, copies files directly
skills add my-skill --mode copy
# or
skills add my-skill -m copy
Detecting Symlink Failures Programmatically
The CLI returns structured results that reveal when fallback occurred:
{
"success": true,
"path": "/home/user/.claude/skills/my-skill",
"canonicalPath": "/home/user/.agents/skills/my-skill",
"mode": "symlink",
"symlinkFailed": true
}
When symlinkFailed: true appears, the files were copied directly despite the mode: "symlink" designation—typically due to Windows without Developer Mode or insufficient filesystem permissions.
Summary
- Symlink mode (default) creates a canonical copy at
~/.agents/skills/<skill>and links agent directories to it, falling back to copy automatically on failure - Copy mode bypasses the canonical directory entirely, duplicating files directly into agent-specific locations
- The differentiation logic resides primarily in
src/installer.ts, with type definitions insrc/types.tsand agent classification insrc/agents.ts - Universal agents like Claude and Cursor receive optimized handling where the canonical directory equals the agent directory, eliminating redundant symlinks
Frequently Asked Questions
How do I force copy mode instead of symlink?
Pass the --mode copy or -m copy flag to any skills add command. The CLI will bypass the canonical directory and copy files directly to the agent-specific location.
What happens when symlinks fail on Windows?
The skills CLI automatically detects symlink creation failures—common on Windows without Developer Mode—and falls back to copy mode. The result includes symlinkFailed: true so you can identify when this occurred.
Where does the canonical directory get created?
The canonical directory defaults to ~/.agents/skills/<skill-name> on Unix systems, with equivalent paths on Windows. This location is controlled by constants in src/constants.ts defining AGENTS_DIR and SKILLS_SUBDIR.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →