Skip to main content

Overview

Auto-Skill maintains a lock file (skills.lock.json) that tracks installed skills with SHA-256 content hashes for integrity verification. This ensures skills haven’t been tampered with and provides a reproducible installation state.
Lock File Location: ~/.claude/auto-skill/skills.lock.json

Lock File Structure

{
  "version": 5,
  "updated_at": "2024-03-15T10:30:00Z",
  "skills": {
    "react-testing": {
      "name": "react-testing",
      "path": "/home/user/.claude/skills/auto/react-testing.md",
      "contentHash": "a3c8f9d2e1b4567890abcdef1234567890abcdef1234567890abcdef12345678",
      "source": "auto",
      "lockedAt": "2024-03-15T08:00:00Z",
      "metadata": {
        "author": "Vercel",
        "tags": ["react", "testing"],
        "graduated": true
      }
    }
  }
}

Fields

version
number
Version counter, incremented on each save. Tracks lock file changes.
updated_at
string
ISO-8601 timestamp of last update.
skills
Record<string, LockedSkill>
Map of skill name to LockedSkill entries.

LockedSkill Structure

export interface LockedSkill {
  name: string;
  path: string; // Absolute path to SKILL.md
  contentHash: string; // SHA-256 hex
  source: string; // "auto" | "external" | "local"
  lockedAt: string; // ISO-8601
  metadata: Record<string, unknown>;
}
import type { LockedSkill } from "../types";

interface LockFileData {
  version: number;
  updated_at: string;
  skills: Record<string, LockedSkill>;
}

Integrity Verification

The lock file uses SHA-256 hashing to verify skill content hasn’t been modified:
import { createHash } from "node:crypto";

function hashContent(content: string): string {
  return createHash("sha256").update(content, "utf8").digest("hex");
}

Verification Process

1

Read Skill File

Read the current SKILL.md content from disk
2

Hash Content

Compute SHA-256 hash of the content
3

Compare

Compare computed hash with locked hash
4

Result

Return true if hashes match, false if tampered
verifyIntegrity(name: string, content: string): boolean {
  const skill = this.getSkill(name);
  if (!skill) return false;
  return skill.contentHash === hashContent(content);
}

Usage

Create Lock File

import { createLockFile } from "auto-skill";

const lock = createLockFile();
lock.load();

console.log(`Lock file version: ${lock.version}`);
console.log(`Skills tracked: ${lock.skillCount}`);

Add a Skill

import fs from "fs";

const skillPath = "/home/user/.claude/skills/auto/react-testing.md";
const content = fs.readFileSync(skillPath, "utf-8");

lock.addSkill(
  "react-testing",
  skillPath,
  content,
  "auto",
  {
    author: "Vercel",
    tags: ["react", "testing"],
    graduated: true,
  }
);

lock.save();

console.log("✅ Skill locked");

Verify Integrity

const skillPath = "/home/user/.claude/skills/auto/react-testing.md";
const content = fs.readFileSync(skillPath, "utf-8");

const isValid = lock.verifyIntegrity("react-testing", content);

if (isValid) {
  console.log("✅ Skill integrity verified");
} else {
  console.log("❌ Skill has been tampered with!");
}

Verify All Skills

const results = lock.verifyAll();

for (const [name, isValid] of Object.entries(results)) {
  if (isValid) {
    console.log(`✅ ${name}`);
  } else {
    console.log(`❌ ${name} - TAMPERED OR MISSING`);
  }
}
✅ react-testing
✅ nextjs-deployment
❌ typescript-strict - TAMPERED OR MISSING
✅ jest-patterns

List Locked Skills

const skills = lock.listSkills();

console.log(`Locked Skills (${skills.length}):\n`);

for (const skill of skills) {
  console.log(`- ${skill.name}`);
  console.log(`  Path: ${skill.path}`);
  console.log(`  Source: ${skill.source}`);
  console.log(`  Locked: ${skill.lockedAt}`);
  console.log(`  Hash: ${skill.contentHash.slice(0, 16)}...`);
}

Get Specific Skill

const skill = lock.getSkill("react-testing");

if (skill) {
  console.log(`Name: ${skill.name}`);
  console.log(`Path: ${skill.path}`);
  console.log(`Hash: ${skill.contentHash}`);
  console.log(`Metadata:`, skill.metadata);
} else {
  console.log("Skill not found in lock file");
}

Remove a Skill

const removed = lock.removeSkill("react-testing");

if (removed) {
  lock.save();
  console.log("✅ Skill removed from lock file");
} else {
  console.log("❌ Skill not found");
}

Atomic Writes

The lock file uses atomic writes to prevent corruption:
import { writeFileAtomic } from "../util/atomic-write";

save(): void {
  this.data.version = (this.data.version ?? 0) + 1;
  this.data.updated_at = new Date().toISOString();
  writeFileAtomic(this.path, JSON.stringify(this.data, null, 2));
}
import fs from "fs";
import path from "path";

export function writeFileAtomic(filePath: string, content: string): void {
  const dir = path.dirname(filePath);
  fs.mkdirSync(dir, { recursive: true });

  const tempPath = `${filePath}.tmp`;
  fs.writeFileSync(tempPath, content, "utf-8");
  fs.renameSync(tempPath, filePath);
}
Atomic writes ensure the lock file is never left in a corrupted state, even if the process crashes during writing.

API Reference

createLockFile(lockFilePath?)

Factory function to create a LockFile instance.
lockFilePath
string
Path to the lock file.Default: ~/.claude/auto-skill/skills.lock.json

LockFile Class

path
string
Absolute path to the lock file.
version
number
Current version counter.
skillCount
number
Number of skills in the lock file.
load
() => this
Load the lock file from disk.Returns: this (for chaining)
save
() => void
Save the lock file atomically.Increments version counter on each save.
addSkill
(name, skillPath, content, source?, metadata?) => void
Add or update a skill in the lock file.Parameters:
  • name: string - Skill name
  • skillPath: string - Path to SKILL.md file
  • content: string - SKILL.md content (for hashing)
  • source?: string - Source of the skill (default: “auto”)
  • metadata?: Record<string, unknown> - Optional metadata
removeSkill
(name) => boolean
Remove a skill from the lock file.Returns: true if removed, false if not found
getSkill
(name) => LockedSkill | undefined
Get a locked skill by name.
listSkills
() => LockedSkill[]
List all locked skills.
verifyIntegrity
(name, content) => boolean
Verify a skill’s content matches its locked hash.Parameters:
  • name: string - Skill name
  • content: string - Current SKILL.md content
Returns: true if content matches, false if tampered or missing
verifyAll
(skillsDir?) => Record<string, boolean>
Verify integrity of all locked skills.Parameters:
  • skillsDir?: string - Root directory containing skills (optional)
Returns: Map of skill name to verification result

CLI Commands

Lock Current Skills

npx auto-skill lock
Scans ~/.claude/skills/auto/ and adds all skills to the lock file.

Verify Integrity

npx auto-skill lock verify
Verifying integrity of 4 skills...

✅ react-testing
✅ nextjs-deployment
❌ typescript-strict - HASH MISMATCH
✅ jest-patterns

Results: 3 verified, 1 failed

View Lock File

npx auto-skill lock show

Update Hashes

npx auto-skill lock update
Recalculates hashes for all locked skills (useful after intentional modifications).

Best Practices

Always lock newly generated or graduated skills to track their initial state.
Before using a skill from an external source, verify its integrity against the lock file.
If your project uses Auto-Skill, commit skills.lock.json to Git to ensure reproducible skill installations across your team.
If you manually edit a skill file, run npx auto-skill lock update to update the lock file with new hashes.

Integration with Multi-Agent

When creating symlinks for multi-agent support, the lock file tracks the canonical source:
import { createAgentRegistry, createLockFile } from "auto-skill";

const registry = createAgentRegistry();
const lock = createLockFile().load();

const skillPath = "~/.claude/skills/auto/react-testing.md";
const content = fs.readFileSync(skillPath, "utf-8");

// Lock the canonical skill
lock.addSkill("react-testing", skillPath, content);
lock.save();

// Create symlinks for other agents
const links = registry.createSkillSymlinks(
  skillPath,
  "react-testing",
  "claude-code"
);

console.log(`Created ${links.length} symlinks`);
The lock file only tracks the canonical skill file, not the symlinks. This ensures integrity verification checks the original source.

See Also

Multi-Agent Support

Cross-agent skill sharing

Skill Graduation

Promote external skills to local

Build docs developers (and LLMs) love