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

> Learn the skills CLI lock file architecture. Discover the difference between global .skill-lock.json for machine updates and local skills-lock.json for project installs to ensure reproducible builds.

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

---

**The skills CLI uses two separate lock files—global [`.skill-lock.json`](https://github.com/vercel-labs/skills/blob/main/.skill-lock.json) for machine-wide update tracking and local [`skills-lock.json`](https://github.com/vercel-labs/skills/blob/main/skills-lock.json) for project-specific reproducible installs.**

The **skills** CLI from [vercel-labs/skills](https://github.com/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`](https://github.com/vercel-labs/skills/blob/main/.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:

```bash

# 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`](https://github.com/vercel-labs/skills/blob/main/src/skill-lock.ts), the global lock uses version 3 with rich metadata:

```json
{
  "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`](https://github.com/vercel-labs/skills/blob/main/src/blob.ts) provides `fetchRepoTree` and `getSkillFolderHashFromTree` for this purpose.

### Update Detection Logic

```typescript
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`](https://github.com/vercel-labs/skills/blob/main/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`](https://github.com/vercel-labs/skills/blob/main/src/local-lock.ts), the local lock uses a minimal version 1 schema:

```json
{
  "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.

```typescript
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:

```typescript
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`](https://github.com/vercel-labs/skills/blob/main/.skill-lock.json) | Local [`skills-lock.json`](https://github.com/vercel-labs/skills/blob/main/skills-lock.json) |
|--------|---------------------------|--------------------------|
| **File path** | `~/.agents/.skill-lock.json` or `$XDG_STATE_HOME/skills/.skill-lock.json` | [`./skills-lock.json`](https://github.com/vercel-labs/skills/blob/main/./skills-lock.json) in project root |
| **Source file** | [`src/skill-lock.ts`](https://github.com/vercel-labs/skills/blob/main/src/skill-lock.ts) | [`src/local-lock.ts`](https://github.com/vercel-labs/skills/blob/main/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`](https://github.com/vercel-labs/skills/blob/main/src/skill-lock.ts) | Global lock file operations, version 3 schema, GitHub tree hash integration |
| [`src/local-lock.ts`](https://github.com/vercel-labs/skills/blob/main/src/local-lock.ts) | Local lock file operations, version 1 schema, content hash verification |
| [`src/blob.ts`](https://github.com/vercel-labs/skills/blob/main/src/blob.ts) | `fetchRepoTree` and `getSkillFolderHashFromTree` for GitHub API calls |
| [`src/hash.ts`](https://github.com/vercel-labs/skills/blob/main/src/hash.ts) | `computeSkillFolderHash` for local SHA-256 calculation |
| [`src/add.ts`](https://github.com/vercel-labs/skills/blob/main/src/add.ts) | Writes both lock files during skill installation |
| [`src/cli.ts`](https://github.com/vercel-labs/skills/blob/main/src/cli.ts) | `skills check` and `skills update` commands using global lock data |

---

## Summary

- The **global [`.skill-lock.json`](https://github.com/vercel-labs/skills/blob/main/.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`](https://github.com/vercel-labs/skills/blob/main/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`](https://github.com/vercel-labs/skills/blob/main/.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`](https://github.com/vercel-labs/skills/blob/main/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.