How experimental_sync Integrates with node_modules in the Skills CLI

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 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)
    • <pkg>/skills/ subdirectory
    • <pkg>/.agents/skills/ subdirectory
    • Any subfolder containing a SKILL.md file

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

// 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 parses 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) implements the change detection logic:

// 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) 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
-y flag present Auto-confirms default agent selection

Universal agents—identified via getUniversalAgents in src/agents.ts—don't require symlinks because they already reference the canonical skill directory.

The actual installation delegates to installSkillForAgent in src/installer.ts, which performs filesystem operations with fallback safety mechanisms.

Installation Pipeline

// 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 persists the new state to 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 Command orchestration, discovery, UI, lock handling, telemetry
src/installer.ts File operations: canonical copy, symlinking, fallback copies
src/agents.ts Agent definitions, universal agent detection, path resolution
src/local-lock.ts Lock file I/O and hash-based change detection
src/constants.ts Path constants: AGENTS_DIR, SKILLS_SUBDIR
src/prompts/search-multiselect.ts Interactive agent selection UI

Summary

  • experimental_sync discovers skills by crawling node_modules for SKILL.md files and conventional skill subdirectories
  • Change detection via 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 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. 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. 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 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.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →