# How experimental_sync Integrates with node_modules in the Skills CLI

> Discover how experimental_sync integrates with node_modules in the Skills CLI, scanning npm packages and installing them into agent skill directories via symlinks for seamless skill management.

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

---

**The `experimental_sync` command scans your project's `node_modules` directory for skills bundled as npm packages, then installs them into canonical agent skill directories with symlinks to your selected agents.**

The `experimental_sync` command in the Vercel Labs Skills CLI bridges the gap between npm-distributed skills and local agent configurations. This experimental feature enables developers to treat skills as versioned dependencies while automatically wiring them into agent-specific skill directories. Understanding how this command discovers, validates, and installs skills from `node_modules` helps you manage complex multi-agent workflows efficiently.

## How experimental_sync Discovers Skills in node_modules

The discovery phase begins in [`src/sync.ts`](https://github.com/vercel-labs/skills/blob/main/src/sync.ts) with the `discoverNodeModuleSkills` function. This crawler treats your `node_modules` directory as a searchable registry of potential skills.

### Discovery Algorithm

The function performs the following operations:

1. **Reads the directory structure** using `readdir`, skipping hidden entries and standard npm artifacts
2. **Handles scoped packages** (`@org/pkg`) by descending into organization directories
3. **Searches multiple locations** within each package for skill definitions:
   - Package root ([`SKILL.md`](https://github.com/vercel-labs/skills/blob/main/SKILL.md))
   - `<pkg>/skills/` subdirectory
   - `<pkg>/.agents/skills/` subdirectory
   - Any subfolder containing a [`SKILL.md`](https://github.com/vercel-labs/skills/blob/main/SKILL.md) file

Each discovered skill returns a structured object with `name`, `description`, `path`, and `packageName` properties.

```typescript
// Conceptual extraction from src/sync.ts
interface DiscoveredSkill {
  name: string;
  description: string;
  path: string;          // Absolute path to skill directory
  packageName: string;   // Source npm package
}

```

## Lock File Comparison and Change Detection

After discovery, `experimental_sync` consults the local lock file to avoid redundant installations.

### Lock File Mechanics

The `readLocalLock` function in [`src/local-lock.ts`](https://github.com/vercel-labs/skills/blob/main/src/local-lock.ts) parses [`skills-lock.json`](https://github.com/vercel-labs/skills/blob/main/skills-lock.json) (project-scoped) or `~/.agents/.skill-lock.json` (global). This file stores cryptographic hashes of previously installed skill directories.

The `runSync` function (lines 71-84 in [`src/sync.ts`](https://github.com/vercel-labs/skills/blob/main/src/sync.ts)) implements the change detection logic:

```typescript
// Pseudocode from src/sync.ts → runSync
if (force) {
  toInstall = allDiscoveredSkills;
} else {
  toInstall = allDiscoveredSkills.filter(skill => {
    const currentHash = computeSkillFolderHash(skill.path);
    const lockedHash = localLock.get(skill.name);
    return currentHash !== lockedHash; // True if new or changed
  });
}

```

The `--force` (`-f`) flag bypasses this optimization for complete reinstallation.

## Agent Selection and Targeting

Once the installation candidate list is determined, `experimental_sync` resolves which agents should receive the skills.

### Agent Resolution Logic

The `runSync` function (lines 98-140 in [`src/sync.ts`](https://github.com/vercel-labs/skills/blob/main/src/sync.ts)) handles this through several pathways:

| Scenario | Behavior |
|----------|----------|
| `--agent` specified | Validates provided agent names against known agents |
| No agents installed | Defaults to **universal agents** (those sharing `.agents/skills`) |
| Agents installed + interactive mode | Launches `searchMultiselect` prompt from [`src/prompts/search-multiselect.ts`](https://github.com/vercel-labs/skills/blob/main/src/prompts/search-multiselect.ts) |
| `-y` flag present | Auto-confirms default agent selection |

Universal agents—identified via `getUniversalAgents` in [`src/agents.ts`](https://github.com/vercel-labs/skills/blob/main/src/agents.ts)—don't require symlinks because they already reference the canonical skill directory.

## Installation and Symlink Creation

The actual installation delegates to `installSkillForAgent` in [`src/installer.ts`](https://github.com/vercel-labs/skills/blob/main/src/installer.ts), which performs filesystem operations with fallback safety mechanisms.

### Installation Pipeline

```typescript
// Conceptual flow from src/installer.ts
function installSkillForAgent(skill, agent, options) {
  const sanitizedName = sanitizeName(skill.name);
  const canonicalDir = getCanonicalSkillsDir(options.global);
  
  // 1. Create canonical location
  const skillCanonicalPath = path.join(canonicalDir, sanitizedName);
  copyDirectory(skill.path, skillCanonicalPath);
  
  // 2. Handle universal agents (skip symlink)
  if (isUniversalAgent(agent) && options.global) {
    return { installed: true, symlink: false };
  }
  
  // 3. Create agent-specific symlink or copy
  const agentSkillPath = getAgentSkillPath(agent, sanitizedName);
  try {
    createSymlink(skillCanonicalPath, agentSkillPath);
    return { installed: true, symlink: true };
  } catch (error) {
    // Fallback: copy when symlinks fail (Windows, etc.)
    copyDirectory(skillCanonicalPath, agentSkillPath);
    return { installed: true, symlink: false, fallback: 'copy' };
  }
}

```

The canonical directory defaults to `.agents/skills` (project-scoped) or `~/.agents/skills` (with `--global`). The `createSymlink` function handles cross-platform differences, falling back to full copies when symbolic links are unavailable or permission-restricted.

## Lock File Update and Telemetry

Post-installation, `experimental_sync` finalizes its operation with state persistence and analytics.

### Finalization Steps

1. **Hash recomputation**: `computeSkillFolderHash` regenerates the cryptographic digest of the installed skill directory
2. **Lock entry writing**: `addSkillToLocalLock` in [`src/local-lock.ts`](https://github.com/vercel-labs/skills/blob/main/src/local-lock.ts) persists the new state to [`skills-lock.json`](https://github.com/vercel-labs/skills/blob/main/skills-lock.json)
3. **Telemetry emission**: The `track` function sends an anonymous `experimental_sync` event containing:
   - Count of successfully installed skills
   - Count of failed installations
   - Names of targeted agents

## Common Commands for experimental_sync

| Command | Purpose |
|---------|---------|
| `skills experimental_sync` | Interactive sync with agent selection prompt |
| `skills experimental_sync -y` | Auto-confirm agent selection for CI/CD |
| `skills experimental_sync -f` | Force reinstall all discovered skills |
| `skills experimental_sync -a cursor claude-code` | Target specific agents only |
| `skills experimental_sync --global` | Use global canonical directory (`~/.agents/skills`) |

## Key Source Files Reference

| File | Responsibility |
|------|---------------|
| [`src/sync.ts`](https://github.com/vercel-labs/skills/blob/main/src/sync.ts) | Command orchestration, discovery, UI, lock handling, telemetry |
| [`src/installer.ts`](https://github.com/vercel-labs/skills/blob/main/src/installer.ts) | File operations: canonical copy, symlinking, fallback copies |
| [`src/agents.ts`](https://github.com/vercel-labs/skills/blob/main/src/agents.ts) | Agent definitions, universal agent detection, path resolution |
| [`src/local-lock.ts`](https://github.com/vercel-labs/skills/blob/main/src/local-lock.ts) | Lock file I/O and hash-based change detection |
| [`src/constants.ts`](https://github.com/vercel-labs/skills/blob/main/src/constants.ts) | Path constants: `AGENTS_DIR`, `SKILLS_SUBDIR` |
| [`src/prompts/search-multiselect.ts`](https://github.com/vercel-labs/skills/blob/main/src/prompts/search-multiselect.ts) | Interactive agent selection UI |

## Summary

- **`experimental_sync` discovers skills** by crawling `node_modules` for [`SKILL.md`](https://github.com/vercel-labs/skills/blob/main/SKILL.md) files and conventional skill subdirectories
- **Change detection** via [`skills-lock.json`](https://github.com/vercel-labs/skills/blob/main/skills-lock.json) prevents redundant installations unless `--force` is used
- **Agent targeting** supports explicit selection, interactive prompts, or automatic universal agent assignment
- **Installation uses a canonical-symlink pattern**: skills copy to `.agents/skills/` or `~/.agents/skills/`, then symlink to agent-specific directories with copy fallback
- **Cross-platform compatibility** handles Windows symlink limitations through automatic copy fallback

## Frequently Asked Questions

### What file pattern does experimental_sync use to identify skills in node_modules?

The command searches for [`SKILL.md`](https://github.com/vercel-labs/skills/blob/main/SKILL.md) files in multiple locations within each package: the package root, a `skills/` subdirectory, a `.agents/skills/` subdirectory, and any nested folder containing a [`SKILL.md`](https://github.com/vercel-labs/skills/blob/main/SKILL.md). This convention-based discovery allows skills to be bundled as standard npm packages without custom metadata.

### How does experimental_sync handle skills that are already installed?

The command computes a cryptographic hash of each discovered skill's directory and compares it against entries in [`skills-lock.json`](https://github.com/vercel-labs/skills/blob/main/skills-lock.json). Skills with matching hashes are skipped unless you pass the `--force` flag, which bypasses the lock file and reinstalls all discovered skills regardless of their current state.

### What happens when symlinking fails during installation?

The `installSkillForAgent` function in [`src/installer.ts`](https://github.com/vercel-labs/skills/blob/main/src/installer.ts) attempts to create a symbolic link from the agent's skill directory to the canonical location. If this fails—common on Windows without developer mode or elevated permissions—it automatically falls back to a full directory copy, ensuring the skill is always available to the agent.

### Can I use experimental_sync in CI/CD pipelines without interactive prompts?

Yes. Pass the `-y` flag to auto-confirm agent selection, or use `-a <agent1> <agent2>` to explicitly specify target agents. Combine with `--global` if your CI environment requires skills in a centralized location. The `-f` flag ensures fresh installations regardless of lock state.