Skip to main content

Lock File

The Lock File maintains a skills.lock.json manifest that records installed skills with SHA-256 content hashes for integrity verification. It uses atomic writes to prevent corruption and tracks version history.

createLockFile

Creates a lock file instance for managing skill integrity.
import { createLockFile } from 'auto-skill/core/lock-file';

const lockFile = createLockFile('/path/to/skills.lock.json');
lockFile.load();
lockFilePath
string
Path to lock file. Defaults to ~/.claude/auto-skill/skills.lock.json
lockFile
LockFile
Returns a LockFile instance with skill tracking and integrity verification methods

LockedSkill Type

Represents a skill entry in the lock file.
interface LockedSkill {
  name: string;
  path: string;                    // Absolute path to SKILL.md file
  contentHash: string;             // SHA-256 hash of content
  source: string;                  // "auto" | "graduated" | "manual"
  lockedAt: string;                // ISO-8601 timestamp
  metadata: Record<string, unknown>;
}

Lock File Format

The skills.lock.json file structure:
{
  "version": 3,
  "updated_at": "2026-03-03T10:30:00.000Z",
  "skills": {
    "typescript-debugger": {
      "name": "typescript-debugger",
      "path": "/home/user/.claude/skills/auto/typescript-debugger.md",
      "contentHash": "a1b2c3d4e5f6...",
      "source": "auto",
      "lockedAt": "2026-03-01T08:00:00.000Z",
      "metadata": {
        "pattern_id": "pat_123",
        "confidence": 0.92
      }
    }
  }
}

Methods

load

Load the lock file from disk.
lockFile.load();
this
LockFile
Returns this for method chaining
Behavior:
  • Reads and parses JSON from lockFilePath
  • Returns empty structure if file doesn’t exist
  • Returns empty structure if file is corrupted

save

Save the lock file atomically.
lockFile.save();
Side effects:
  • Increments version counter
  • Updates updated_at timestamp
  • Writes to temporary file, then atomically renames (prevents corruption)
  • Creates parent directories if needed

addSkill

Add or update a skill in the lock file.
const content = fs.readFileSync('skill.md', 'utf-8');

lockFile.addSkill(
  'typescript-debugger',
  '/path/to/skill.md',
  content,
  'auto',
  { pattern_id: 'pat_123', confidence: 0.92 }
);
lockFile.save();
name
string
required
Skill name (unique identifier)
skillPath
string
required
Absolute path to the SKILL.md file
content
string
required
The SKILL.md file content (used for SHA-256 hashing)
source
string
default:"auto"
Source of the skill: "auto" | "graduated" | "manual"
metadata
Record<string, unknown>
default:"{}"
Optional metadata to store with the skill
Side effects:
  • Overwrites existing entry if skill name already exists
  • Generates SHA-256 content hash
  • Sets lockedAt timestamp to current time
  • Does NOT save to disk (call save() separately)

removeSkill

Remove a skill from the lock file.
const removed = lockFile.removeSkill('typescript-debugger');
if (removed) {
  lockFile.save();
}
name
string
required
Skill name to remove
removed
boolean
true if skill was removed, false if not found

getSkill

Get a locked skill by name.
const skill = lockFile.getSkill('typescript-debugger');
if (skill) {
  console.log(`Locked at: ${skill.lockedAt}`);
  console.log(`Hash: ${skill.contentHash}`);
}
name
string
required
Skill name to look up
skill
LockedSkill | undefined
Locked skill entry if found, otherwise undefined

listSkills

List all locked skills.
const skills = lockFile.listSkills();
for (const skill of skills) {
  console.log(`${skill.name} (${skill.source})`);
}
skills
LockedSkill[]
Array of all locked skill entries

verifyIntegrity

Verify a skill’s content matches its locked hash.
const content = fs.readFileSync('/path/to/skill.md', 'utf-8');
const isValid = lockFile.verifyIntegrity('typescript-debugger', content);

if (!isValid) {
  console.error('Skill has been tampered with!');
}
name
string
required
Skill name to verify
content
string
required
Current SKILL.md file content
isValid
boolean
true if content hash matches locked hash, false if tampered or skill not found

verifyAll

Verify integrity of all locked skills.
const results = lockFile.verifyAll();

for (const [name, isValid] of Object.entries(results)) {
  if (!isValid) {
    console.error(`❌ ${name}: integrity check failed`);
  } else {
    console.log(`✓ ${name}: verified`);
  }
}
skillsDir
string
Root directory containing skill subdirectories (unused, paths from lock file)
results
Record<string, boolean>
Map of skill name to integrity check result. Returns false for:
  • Files that don’t exist
  • Files with mismatched hashes
  • Files that can’t be read

Properties

version

Get the current version counter.
console.log(`Lock file version: ${lockFile.version}`);
version
number
Version counter (increments on each save)

skillCount

Get the number of locked skills.
console.log(`Total locked skills: ${lockFile.skillCount}`);
skillCount
number
Number of skills in the lock file

path / filePath

Get the lock file path.
console.log(`Lock file: ${lockFile.path}`);
console.log(`Lock file: ${lockFile.filePath}`); // alias
path
string
Absolute path to the lock file

Usage Example

import { createLockFile } from 'auto-skill/core/lock-file';
import fs from 'node:fs';

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

// Add a skill
const skillPath = '/home/user/.claude/skills/auto/fix-types.md';
const content = fs.readFileSync(skillPath, 'utf-8');

lockFile.addSkill(
  'fix-types',
  skillPath,
  content,
  'auto',
  { pattern_id: 'pat_456', confidence: 0.88 }
);
lockFile.save();

console.log(`Lock file version: ${lockFile.version}`);
console.log(`Total skills: ${lockFile.skillCount}`);

// List all skills
const skills = lockFile.listSkills();
for (const skill of skills) {
  console.log(`${skill.name}:`);
  console.log(`  Path: ${skill.path}`);
  console.log(`  Source: ${skill.source}`);
  console.log(`  Hash: ${skill.contentHash.substring(0, 16)}...`);
  console.log(`  Locked: ${skill.lockedAt}`);
}

// Verify integrity
const results = lockFile.verifyAll();
const tampered = Object.entries(results)
  .filter(([_, isValid]) => !isValid)
  .map(([name, _]) => name);

if (tampered.length > 0) {
  console.error(`⚠️  Tampered skills detected:`);
  tampered.forEach(name => console.error(`  - ${name}`));
} else {
  console.log(`✓ All ${lockFile.skillCount} skills verified`);
}

// Remove a skill
if (lockFile.removeSkill('old-skill')) {
  lockFile.save();
  console.log('Removed old-skill from lock file');
}

Integrity Verification Flow

import { createLockFile } from 'auto-skill/core/lock-file';
import fs from 'node:fs';

function verifySkillIntegrity(skillName: string): boolean {
  const lockFile = createLockFile().load();
  const locked = lockFile.getSkill(skillName);
  
  if (!locked) {
    console.error(`Skill '${skillName}' not found in lock file`);
    return false;
  }
  
  if (!fs.existsSync(locked.path)) {
    console.error(`Skill file missing: ${locked.path}`);
    return false;
  }
  
  const content = fs.readFileSync(locked.path, 'utf-8');
  const isValid = lockFile.verifyIntegrity(skillName, content);
  
  if (!isValid) {
    console.error(`⚠️  Integrity check failed for '${skillName}'`);
    console.error(`   Expected hash: ${locked.contentHash}`);
    console.error(`   File may have been modified`);
    return false;
  }
  
  console.log(`✓ '${skillName}' integrity verified`);
  return true;
}

verifySkillIntegrity('typescript-debugger');

Source Reference

src/core/lock-file.ts:64-218

Build docs developers (and LLMs) love