Skip to main content
The Skills CLI uses two types of lock files to track installed skills: a global lock file for user-wide installations and a local lock file for project-scoped installations.

Global Lock File

The global lock file tracks all skills installed to your user directory (~/.agents/). It is located at:
~/.agents/.skill-lock.json

File Structure

{
  "version": 3,
  "skills": {
    "frontend-design": {
      "source": "vercel-labs/agent-skills",
      "sourceType": "github",
      "sourceUrl": "https://github.com/vercel-labs/agent-skills",
      "skillPath": "skills/frontend-design",
      "skillFolderHash": "a3f2c1d9e8b7...",
      "installedAt": "2024-01-15T10:30:00.000Z",
      "updatedAt": "2024-01-20T14:45:00.000Z",
      "pluginName": "web-design-tools"
    }
  },
  "dismissed": {
    "findSkillsPrompt": true
  },
  "lastSelectedAgents": ["claude-code", "cursor"]
}

Schema Fields

version
number
required
Schema version for future migrations. Current version is 3.
skills
object
required
Map of skill name to skill entry. Each entry contains:
dismissed
object
Tracks dismissed prompts so they’re not shown again.
lastSelectedAgents
string[]
Array of agent IDs last selected for installation

Version History

Added skillFolderHash field for folder-based update detection.Breaking change: Lock files from v2 and earlier are automatically wiped when read. Users must reinstall skills to populate the new format.
Used content hashing for update detection.
Initial version.

Skill Folder Hash Calculation

The skillFolderHash is a GitHub tree SHA computed by the GitHub API. It represents the state of the entire skill folder.
1

Request tree data

The CLI calls GitHub’s Trees API for the repository:
GET https://api.github.com/repos/{owner}/{repo}/git/trees/{branch}?recursive=1
2

Find skill folder

The response contains all files/folders. The CLI finds the tree entry matching the skill folder path.
3

Extract SHA

The tree entry’s sha field is the skill folder hash. This SHA changes whenever any file in the folder changes.
For root-level skills (no subpath), the root tree SHA is used.

GitHub Token Resolution

To avoid rate limits, the CLI attempts to use a GitHub token in this order:
  1. GITHUB_TOKEN environment variable
  2. GH_TOKEN environment variable
  3. gh CLI auth token (via gh auth token)
If no token is found, unauthenticated requests are made (lower rate limits apply).

Local Lock File

The local lock file tracks skills installed to a project directory. It is located at:
./skills-lock.json
This file is meant to be checked into version control and shared with your team.

File Structure

{
  "version": 1,
  "skills": {
    "frontend-design": {
      "source": "vercel-labs/agent-skills",
      "sourceType": "github",
      "computedHash": "e8a9f1c2d3b4..."
    },
    "skill-creator": {
      "source": "../my-local-skills",
      "sourceType": "local",
      "computedHash": "f9b0e1d2c3a4..."
    }
  }
}

Design Philosophy

The local lock file is intentionally minimal to:
  • Minimize merge conflicts - Skills are sorted alphabetically, and no timestamps are stored
  • Deterministic output - Same skills = same JSON every time
  • Git-friendly - Two branches adding different skills produce non-overlapping keys that auto-merge cleanly

Schema Fields

version
number
required
Schema version for future migrations. Current version is 1.
skills
object
required
Map of skill name to skill entry. Sorted alphabetically by key when written.

Computed Hash Algorithm

The local lock file uses content-based hashing:
1

Collect all files

Recursively read all files in the skill directory (excluding .git/ and node_modules/)
2

Sort by path

Sort files alphabetically by relative path for deterministic hashing
3

Hash contents

Create a SHA-256 hash from:
  • Each file’s relative path (to detect renames)
  • Each file’s content (binary or text)
import { createHash } from 'crypto';
import { readdir, readFile } from 'fs/promises';
import { join, relative } from 'path';

async function computeSkillFolderHash(skillDir: string): Promise<string> {
  const files: Array<{ relativePath: string; content: Buffer }> = [];
  
  // Collect all files recursively
  await collectFiles(skillDir, skillDir, files);
  
  // Sort by relative path for determinism
  files.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
  
  // Hash path + content for each file
  const hash = createHash('sha256');
  for (const file of files) {
    hash.update(file.relativePath);
    hash.update(file.content);
  }
  
  return hash.digest('hex');
}

Working with Lock Files

Reading Lock Files

import { readSkillLock } from './skill-lock';

const lock = await readSkillLock();
console.log(lock.skills); // All installed global skills

Adding Skills

import { addSkillToLock } from './skill-lock';

await addSkillToLock('my-skill', {
  source: 'owner/repo',
  sourceType: 'github',
  sourceUrl: 'https://github.com/owner/repo',
  skillPath: 'skills/my-skill',
  skillFolderHash: 'abc123...'
});

Removing Skills

import { removeSkillFromLock } from './skill-lock';

const removed = await removeSkillFromLock('my-skill');
console.log(removed); // true if skill existed

Update System

Learn how the update checking system uses lock files

Plugin Manifests

Discover how plugin manifests integrate with lock files

Build docs developers (and LLMs) love