Lockfile and dependency graph manager for recursive AI agent skills.
sklock brings Cargo.toml-style dependency management to AI agent skill workspaces.
Each skill lives in its own directory with a SKILL.md file — YAML frontmatter for metadata, Markdown body for documentation. Skills can nest infinitely deep (skills/*/skills/*), with sub-skills displayed under their parent in the tree. sklock scans these files, resolves the dependency graph, generates a reproducible skill.lock, and gives you visualization tools — all from a single CLI.
Follows the agentskills.io open standard. Compatible with Claude Code, OpenAI Codex, and OpenCode.
AI agent skill trees grow complex fast. Without a lockfile:
- You can't tell if a skill's dependencies changed between runs
- CI has no way to detect drift or missing skills
- Refactoring is guesswork — "what depends on this skill?"
skill.lock is the reproducibility snapshot: every discovered skill with two hashes — contentHash (the skill’s own files only, excluding sub-skills) and closureHash (the full subtree including all descendant sub-skills) — plus parent and resolved requires. A top-level workspaceHash lets CI verify the entire workspace with a single value.
Installing from github:… clones the repository and runs the prepare script (npm run build), which compiles the CLI into dist/. You do not need dist/ committed. If your package manager skips prepare in some edge cases, run npm run build in the package directory once.
# npm
npm install -g github:artieax/sklock
# pnpm
pnpm add -g github:artieax/sklock
# bun
bun add -g github:artieax/sklockOr run without installing:
npx github:artieax/sklock --help
bunx github:artieax/sklock --helpInstall sklock and use the sklock/initialize skill — it scaffolds the workspace, runs static inference, and does a semantic pass to wire up requires[].
npm install -g github:artieax/sklock # or pnpm / bun
sklock --versionWhy the initialize skill, not just
sklock infer?sklock inferuses static analysis (file-level cross-references). On a fresh workspace, skills have norequires[]yet and no cross-file references between them, so it will always return nothing. Thesklock/initializeskill prompts the agent to read each skill's description and reason about dependencies semantically — the only reliable approach at initialization time.
# Scaffold a skills workspace with example skills
sklock init --example
# Validate all skill dependencies
sklock validate
# Generate skill.lock
sklock lock
# Check lockfile (warns if stale; use --frozen in CI to fail the job)
sklock check
sklock check --frozen
# Infer requires[] from file-level cross-references (static analysis only)
sklock infer
sklock infer --apply
# Visualize the dependency graph
sklock graph --mermaid
# Show nested skill tree
sklock treeEach skill is defined by a SKILL.md file with YAML frontmatter:
---
name: research-report
description: Gather sources and produce a structured report
version: "1.0.0"
requires:
- citation-formatter
tags: [research, output]
---
# Research Report
Full documentation goes here...name— required Agent Skills identifier; must match the directory name and use lowercase kebab-casedescription— optional in sklock (recommended);sklock lintwarns when it is missingrequires— list of other skill IDs this skill depends onlicense,compatibility,metadata,allowed-tools— optional Agent Skills metadataversion,tags,author,requires— sklock extensions- Other frontmatter keys (for example Claude Code extensions) are accepted and ignored by sklock unless added to the schema later
- A JSON Schema for the frontmatter model is in
schemas/skill-frontmatter.json(regenerate withnpm run json-schemawhenSkillSchemachanges)
Skills can contain sub-skills by placing them in a skills/ subdirectory:
skills/
research-report/
SKILL.md ← requires: [citation-formatter]
skills/
summarize/
SKILL.md ← internal to research-report (level 1)
skills/
fetch-content/
SKILL.md ← internal to summarize (level 2)
format-output/
SKILL.md
citation-formatter/
SKILL.md ← external skill
Nested sub-skills are rendered under their parent, but skill IDs are workspace-wide and must be unique. requires declares explicit cross-skill dependencies by ID.
Using examples/basic (6 skills; nesting depth 2 from root to the deepest sub-skill):
graph TD
"research-report" --> "citation-formatter"
"hello"
"summarize"
"fetch-content"
"format-output"
├── citation-formatter@0.3.0
├── hello@0.1.0
└── research-report@1.0.0
├── [internal] summarize@0.2.0
│ ├── [internal] fetch-content@0.1.0
│ └── [internal] format-output@0.1.0
└── requires: citation-formatter
Skill: research-report
Version: 1.0.0
Description: Gather sources, summarize findings, and produce a structured report
Requires: citation-formatter
Tags: research, output
✓ All 6 skill(s) are valid.
| Command | Description |
|---|---|
init |
Scaffold a skills workspace |
scan |
Discover and list all skills |
validate |
Validate dependency graph (--strict enforces Agent Skills spec compliance) |
lock |
Generate skill.lock |
check |
Verify lockfile is current (default: warn if stale; --frozen fails for CI) |
tree |
Display skills as a nested tree |
graph |
Render dependency graph (--mermaid; --mode deps|containment|both) |
why <id> |
Show what depends on a skill |
explain <id> |
Show full skill details |
export |
Export graph as JSON / YAML / Mermaid |
lint |
Report quality issues in SKILL.md files |
add <id> --dep <dep> |
Add a dependency to a skill and refresh skill.lock |
infer |
Infer requires[] from skill descriptions (--apply writes to SKILL.md) |
doctor |
Run full workspace health check: validation, lockfile drift, and lint summary |
sklock add updates YAML frontmatter using a document-aware parser so comments and layout are preserved when possible; if parsing fails, it falls back to rewriting the frontmatter block.
See Quickstart → For agents above.
For ongoing work after the workspace is established:
sklock validate && sklock lock # after any SKILL.md change
sklock infer --apply # pick up new file-level cross-references| Error | Fix |
|---|---|
Missing required skill: X |
Create skills/X/SKILL.md or add X to the correct parent's skills/ directory |
Circular dependency: A → B → A |
Remove one of the requires entries to break the cycle |
skill.lock is stale |
Run sklock lock and commit the updated lockfile |
Warning: invalid SKILL.md at ... |
Check the frontmatter is valid YAML and the file starts with --- |
sklock is permissive by default so it works with any existing workspace. Use --strict to enforce the Agent Skills specification:
| Default | --strict |
|
|---|---|---|
description |
optional | required |
metadata values |
any type | strings only |
# validate workspace against the Agent Skills spec
sklock validate --strictIn CI, combine both checks:
- run: sklock validate --strict
- run: sklock check --frozenWithout --frozen, sklock check prints a warning when skill.lock is stale but exits successfully. In CI, pass --frozen so a drifted lockfile fails the job.
# .github/workflows/ci.yml
- name: Check skill.lock is up to date
run: sklock check --frozen| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | General CLI error (missing arguments, invalid flags, I/O errors, etc.) |
| 2 | Invalid workspace or SKILL.md parse/schema failure (SkillScanError), or validation errors from validate / lock / add / graph / export / lint |
| 3 | skill.lock is stale (check --frozen only; without --frozen, stale lockfiles warn and exit 0) |
| 4 | skill.lock is missing (check --frozen only; without --frozen, missing lockfiles warn and exit 0) |
Use sklock check --json to distinguish missing vs stale lockfiles without relying on exit codes: a missing lockfile includes "missingLockfile": true in the JSON object; a stale lockfile includes "stale": true with added / removed / changed arrays (and no missingLockfile field on the normal stale path).
Early development. Breaking changes may occur between versions.
Apache License 2.0. See LICENSE.
Made by artieax — part of the artie project.