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 counter, incremented on each save. Tracks lock file changes.
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
Read Skill File
Read the current SKILL.md content from disk
Hash Content
Compute SHA-256 hash of the content
Compare
Compare computed hash with locked hash
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 ));
}
writeFileAtomic Implementation
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.
Path to the lock file. Default: ~/.claude/auto-skill/skills.lock.json
LockFile Class
Absolute path to the lock file.
Number of skills in the lock file.
Load the lock file from disk. Returns: this (for chaining)
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
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.
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
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
Update Hashes
npx auto-skill lock update
Recalculates hashes for all locked skills (useful after intentional modifications).
Best Practices
Lock after skill generation
Always lock newly generated or graduated skills to track their initial state.
Verify before using external skills
Before using a skill from an external source, verify its integrity against the lock file.
Commit lock file to version control
If your project uses Auto-Skill, commit skills.lock.json to Git to ensure reproducible skill installations across your team.
Update hashes after intentional edits
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