Understanding the Lock File Architecture in the Skills CLI: Global `.skill-lock.json` vs Local `skills-lock.json`

The skills CLI uses two separate lock files—global .skill-lock.json for machine-wide update tracking and local skills-lock.json for project-specific reproducible installs.

The skills CLI from vercel-labs/skills implements a dual-lock architecture that separates global state management from project-level reproducibility. This design lets you track updates across your entire machine while keeping individual projects deterministic and merge-friendly.


Global Lock File: .skill-lock.json

The global lock file serves as the source of truth for CLI operations like skills check and skills update. It persists installation metadata across all projects on a machine.

Location and Environment Detection

The CLI resolves the global lock path using platform-aware logic:


# Default location

~/.agents/.skill-lock.json

# XDG-compliant alternative (when $XDG_STATE_HOME is set)

$XDG_STATE_HOME/skills/.skill-lock.json

Schema Version 3 Structure

Defined in src/skill-lock.ts, the global lock uses version 3 with rich metadata:

{
  "version": 3,
  "skills": {
    "react-best-practices": {
      "source": "vercel-labs/agent-skills",
      "sourceType": "github",
      "sourceUrl": "https://github.com/vercel-labs/agent-skills",
      "skillPath": "skills/react-best-practices",
      "skillFolderHash": "abc123def456...",
      "pluginName": undefined,
      "installedAt": "2024-01-15T09:23:17.000Z",
      "updatedAt": "2024-01-15T09:23:17.000Z"
    }
  },
  "dismissed": {},
  "lastSelectedAgents": []
}

GitHub Tree SHA Hashing

The skillFolderHash field stores a GitHub tree SHA fetched via the GitHub Trees API. This approach detects any change in the remote repository folder without downloading content.

The implementation in src/blob.ts provides fetchRepoTree and getSkillFolderHashFromTree for this purpose.

Update Detection Logic

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

async function checkForUpdates(skillName: string) {
  const lock = await readSkillLock();
  const entry = lock.skills[skillName];
  
  // Compare stored hash with fresh GitHub API call
  const currentHash = await fetchSkillFolderHash(entry.source, entry.skillPath);
  
  if (entry.skillFolderHash !== currentHash) {
    console.log(`Update available for ${skillName}`);
  }
}

UI State Persistence

The global lock includes dismissed (tracks UI prompts) and lastSelectedAgents (remembers user selections), keeping machine-specific state out of version control.


Local Lock File: skills-lock.json

The local lock file provides project-scoped metadata designed for version control. It ensures reproducible installs without the merge conflicts that timestamps and UI state would cause.

Location and Design Philosophy


./skills-lock.json  (in project root, committed to git)

The design deliberately excludes timestamps and machine-specific state to keep diffs minimal and deterministic.

Schema Version 1 Structure

Defined in src/local-lock.ts, the local lock uses a minimal version 1 schema:

{
  "version": 1,
  "skills": {
    "react-best-practices": {
      "source": "vercel-labs/agent-skills",
      "sourceType": "github",
      "ref": "main",
      "computedHash": "deadbeef0123456789abcdef..."
    }
  }
}

Local Content Hashing

Unlike the global lock's GitHub tree SHA, the local lock stores a computed SHA-256 hash of the actual files on disk. This computedHash is calculated from the skill folder contents after installation, making it independent of remote state.

import { computeSkillFolderHash } from './hash';

// SHA-256 of local file contents
const computedHash = await computeSkillFolderHash('/path/to/skill/folder');

Project Reproducibility

The local lock enables deterministic reinstalls:

import { readLocalLock, addSkillToLocalLock } from './local-lock';

async function reproduceInstall(projectPath: string) {
  const lock = await readLocalLock(projectPath);
  
  for (const [name, entry] of Object.entries(lock.skills)) {
    // Reinstall using exact ref and verify against computedHash
    await installSkill(entry.source, entry.ref, {
      verifyHash: entry.computedHash
    });
  }
}

Comparing Global and Local Lock Approaches

Aspect Global .skill-lock.json Local skills-lock.json
File path ~/.agents/.skill-lock.json or $XDG_STATE_HOME/skills/.skill-lock.json ./skills-lock.json in project root
Source file src/skill-lock.ts src/local-lock.ts
Schema version version: 3 version: 1
Hash type GitHub tree SHA (remote state) SHA-256 of local file contents
Timestamps installedAt, updatedAt None (deterministic)
UI state dismissed, lastSelectedAgents None
Git strategy .gitignore (machine-specific) Committed to version control
Primary use Update detection, global state Reproducible installs, CI/CD

Key Implementation Files

File Responsibility
src/skill-lock.ts Global lock file operations, version 3 schema, GitHub tree hash integration
src/local-lock.ts Local lock file operations, version 1 schema, content hash verification
src/blob.ts fetchRepoTree and getSkillFolderHashFromTree for GitHub API calls
src/hash.ts computeSkillFolderHash for local SHA-256 calculation
src/add.ts Writes both lock files during skill installation
src/cli.ts skills check and skills update commands using global lock data

Summary

  • The global .skill-lock.json tracks machine-wide skill installations with version: 3, GitHub tree SHA hashes, and timestamps for update detection.

  • The local skills-lock.json provides project-scoped reproducibility with version: 1, content-based SHA-256 hashes, and no timestamps for merge-friendly version control.

  • Both files are written together during skills add, but serve fundamentally different purposes: global state persistence versus deterministic project setup.


Frequently Asked Questions

What happens if I delete the global .skill-lock.json?

The CLI will recreate it on the next operation, but you will lose update tracking history and UI state like dismissed prompts. Your installed skills remain in ~/.agents/, but the CLI won't know when they were last checked for updates.

Should I commit skills-lock.json to my repository?

Yes. The local lock file is designed specifically for version control. Its deterministic, timestamp-free format ensures reproducible installs across environments and avoids merge conflicts that timestamps would cause.

Why does the global lock use GitHub tree SHA while the local lock uses content hash?

The global lock needs to detect remote changes without downloading content, so it stores the GitHub tree SHA fetched via API. The local lock needs to verify the actual files on disk, so it computes a SHA-256 hash from local content.

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 →