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();
Path to lock file. Defaults to ~/.claude/auto-skill/skills.lock.json
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>;
}
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.
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.
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();
Skill name (unique identifier)
Absolute path to the SKILL.md file
The SKILL.md file content (used for SHA-256 hashing)
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();
}
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}`);
}
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})`);
}
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!');
}
Current SKILL.md file content
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`);
}
}
Root directory containing skill subdirectories (unused, paths from lock file)
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 counter (increments on each save)
skillCount
Get the number of locked skills.
console.log(`Total locked skills: ${lockFile.skillCount}`);
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
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