# Universal Agents vs. Agent-Specific Symlinked Installations in the Skills CLI

> Understand universal agents and agent-specific symlinked installations in Vercel Skills CLI. Learn how shared directories and symlinks impact your setup.

- Repository: [Vercel Labs/skills](https://github.com/vercel-labs/skills)
- Tags: deep-dive
- Published: 2026-04-23

---

**Universal agents share a single skill directory and skip symlinks entirely, while agent-specific installations create symlinks from the canonical store to each agent's isolated folder.**

The **skills CLI** from Vercel Labs manages AI skills across multiple agents using two distinct installation strategies. Understanding how **universal agents versus agent-specific symlinked installations** work helps you predict where files land, why some agents need symlinks, and how to troubleshoot installation issues.

---

## How the Skills CLI Installs Skills

Every skill installation begins at the **canonical location**: `.agents/skills/<skill-name>` for project installs or `$XDG_CONFIG_HOME/agents/skills/<skill-name>` for global installs. From there, the CLI decides whether additional steps are needed based on the target agent's type.

This logic lives in [`src/installer.ts`](https://github.com/vercel-labs/skills/blob/main/src/installer.ts), which handles file writing, symlink creation, and fallback copying when symlinks aren't supported.

---

## Universal Agents: Shared Directory, No Symlinks Needed

**Universal agents** read skills directly from the canonical `.agents/skills` directory. No per-agent symlinks are created because these agents are already configured to look in the shared location.

### How Universal Agents Are Identified

In [`src/agents.ts`](https://github.com/vercel-labs/skills/blob/main/src/agents.ts), the `getUniversalAgents()` function filters the agent registry using two criteria:

```ts
// src/agents.ts
export function getUniversalAgents(): AgentType[] {
  return (Object.entries(agents) as [AgentType, AgentConfig][])
    .filter(
      ([_, config]) => config.skillsDir === '.agents/skills' && config.showInUniversalList !== false
    )
    .map(([type]) => type);
}

```

A universal agent must have:
- `skillsDir` set to `'.agents/skills'` — the canonical shared path
- `showInUniversalList` not explicitly set to `false`

### Installation Behavior for Universal Agents

When installing to a universal agent, [`src/installer.ts`](https://github.com/vercel-labs/skills/blob/main/src/installer.ts) returns early after writing to the canonical directory:

```ts
// src/installer.ts (symlink mode)
if (isGlobal && isUniversalAgent(agentType)) {
  return {
    success: true,
    path: canonicalDir,
    canonicalPath: canonicalDir,
    mode: 'symlink',
  };
}

```

The `mode: 'symlink'` here is slightly misleading — no actual symlink is created. The field indicates this path *would* be the symlink target for non-universal agents, but universal agents use the canonical path directly.

---

## Agent-Specific Symlinked Installations: Isolated Folders with Symlinked Access

Non-universal agents maintain their own isolated skill directories. The CLI creates **symlinks** from the canonical store into each agent's private folder, preserving a single source of truth while respecting the agent's expected layout.

### Installation Flow for Symlinked Agents

In [`src/installer.ts`](https://github.com/vercel-labs/skills/blob/main/src/installer.ts), when the target agent is **not** universal and the installation is global, the full symlink creation runs:

1. Write skill files to the canonical directory
2. Create the agent's skills directory if missing
3. Create a symlink: `~/.agents/<agent>/skills/<skill-name>` → `../../.agents/skills/<skill-name>`
4. Fall back to file copying if symlinks aren't supported

```ts
// src/installer.ts - conceptual flow for symlinked agents
const agentSkillsDir = path.join(AGENTS_DIR, agentType, 'skills');
const symlinkPath = path.join(agentSkillsDir, skillName);

// Create symlink to canonical location
fs.symlinkSync(relativePathToCanonical, symlinkPath);

```

### Why Symlinks Instead of Copies?

Symlinks serve three purposes in the skills CLI architecture:

- **Single source of truth** — Update or delete the canonical skill, and all symlinked agents see the change immediately
- **Disk efficiency** — Skills aren't duplicated across every agent
- **Agent isolation** — Agents that require specific folder structures still get their expected layout

When symlinks fail (Windows without developer mode, certain CI environments), the CLI transparently falls back to full file copies.

---

## CLI Output: How the Skills CLI Shows the Difference

When you run `skills add`, the CLI groups agents visually to make the installation behavior transparent. In [`src/add.ts`](https://github.com/vercel-labs/skills/blob/main/src/add.ts), the `splitAgentsByType` function separates universal and symlinked agents:

```ts
// src/add.ts - splitAgentsByType
function splitAgentsByType(agentTypes: AgentType[]) {
  const universal: string[] = [];
  const symlinked: string[] = [];

  for (const a of agentTypes) {
    if (isUniversalAgent(a)) {
      universal.push(agents[a].displayName);
    } else {
      symlinked.push(agents[a].displayName);
    }
  }
  return { universal, symlinked };
}

```

Typical CLI output shows:

```bash
$ skills add vercel-labs/github-copilot

Installing github-copilot...

✓ Installed to .agents/skills/github-copilot

Available to:
  universal: GitHub Copilot, Vercel CLI
  symlink →: ChatGPT, Claude Desktop

```

This output makes it immediately clear which agents use the skill directly versus which receive symlinks.

---

## Comparing Universal and Symlinked Installations

| Aspect | Universal Agents | Agent-Specific Symlinked |
|--------|-----------------|--------------------------|
| **Skill directory** | Shared `.agents/skills` | Private `~/.agents/<name>/skills` |
| **Symlink created?** | No | Yes (with copy fallback) |
| **Code path in [`installer.ts`](https://github.com/vercel-labs/skills/blob/main/installer.ts)** | Early return after canonical write | Full symlink creation logic |
| **Identification** | `skillsDir === '.agents/skills'` and `showInUniversalList !== false` | Any agent not matching universal criteria |
| **Use case** | Agents designed for shared skill discovery | Agents requiring isolated skill environments |
| **Update propagation** | Immediate for all universal agents | Follows symlinks from canonical source |

---

## Practical Examples

### Installing a Skill for All Universal Agents Globally

```bash

# Install globally to universal agents only

skills add vercel-labs/github-copilot -g

```

Result: Files written to `~/.config/agents/skills/github-copilot`. No symlinks created. Universal agents like GitHub Copilot and Vercel CLI see the skill immediately.

### Installing a Skill for a Specific Non-Universal Agent

```bash

# Install to a specific agent that requires symlinked access

skills add my-custom-skill --agent chatgpt

```

Result: Files written to `./.agents/skills/my-custom-skill`, then symlinked to `~/.agents/chatgpt/skills/my-custom-skill`.

### Checking Agent Type Programmatically

```ts
import { isUniversalAgent, getUniversalAgents } from 'skills/src/agents';

// Check if a specific agent is universal
console.log(isUniversalAgent('copilot')); // true

// List all universal agents
console.log(getUniversalAgents()); // ['copilot', 'vercel-cli', ...]

```

---

## Summary

- **Universal agents** share the canonical `.agents/skills` directory and require no symlinks — the skills CLI writes once and all universal agents immediately have access.
- **Agent-specific installations** use symlinks to bridge the canonical store into private agent folders, maintaining single-source-of-truth while respecting agent isolation requirements.
- The distinction is controlled by agent configuration in [`src/agents.ts`](https://github.com/vercel-labs/skills/blob/main/src/agents.ts), specifically `skillsDir` and `showInUniversalList` properties.
- The [`src/installer.ts`](https://github.com/vercel-labs/skills/blob/main/src/installer.ts) file implements the branching logic: early return for universal agents, full symlink creation for others.

---

## Frequently Asked Questions

### How do I know if an agent is universal or requires symlinked installation?

Check the agent's configuration in [`src/agents.ts`](https://github.com/vercel-labs/skills/blob/main/src/agents.ts). Universal agents have `skillsDir === '.agents/skills'` and `showInUniversalList !== false`. You can also run `skills add` with verbose output — the CLI groups agents into "universal" and "symlink →" sections.

### Can I force a symlink for a universal agent?

No. The skills CLI intentionally skips symlinks for universal agents because they already read from the canonical directory. Creating symlinks would be redundant and could cause path resolution issues.

### What happens if symlinks aren't supported on my system?

The installer in [`src/installer.ts`](https://github.com/vercel-labs/skills/blob/main/src/installer.ts) falls back to copying files when symlink creation fails. This ensures skills work on Windows without developer mode, in CI environments, or on filesystems without symlink support — though updates require re-installation rather than automatically following the symlink.