* feat(skills): load skills from ~/.agents/skills and opt-in ~/.claude/skills (#2258) Read user skills that other coding agents keep in their own homes, read-only and after every OpenClaude-owned skill source: - ~/.agents/skills (shared cross-agent home used by Codex and `npx skills`) loads by default; agentsSkillsEnabled turns it off. - ~/.claude/skills (Claude Code's private config dir) is opt-in via claudeCodeSkillsEnabled, keeping the #1875 isolation default. Both settings are honored from user, local, flag and policy settings only; project settings cannot opt a user in. Sibling skills never shadow anything: same-file dedup keeps the OpenClaude copy of a symlinked skill, and a sibling skill is dropped when any skill-dir, dynamic, plugin or bundled skill or built-in command owns its name. When the skill listing is over budget, sibling skills go names-only before any OpenClaude skill description is trimmed. /skills and `openclaude skills list` group and label sibling skills and hint at disabled homes that hold skills; /config gets a toggle for each home; `skills remove` refuses them; enabled homes are watched. The skills CLI now registers bundled skills and built-in plugins like a session does, so name collisions resolve the same way. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(skills): respect pinned sibling settings in /config and start the watcher on toggle - /config hid a sibling skills toggle only when managed policy set it. Local and --settings values outrank the user settings the toggle writes, so flipping it there changed nothing. resolveSiblingSkillsHomes now reports pinnedBy for any source that outranks user settings; /config hides that toggle and the /skills hint skips that home. - Turning a home on in /config added it only to a running watcher. With no skill directory at startup there was none, so edits in the newly enabled home never hot-reloaded. The reload now starts the watcher. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(skills): reconcile sibling source state and CLI bootstrap * fix(skills): preserve activation during command refresh * test(skills): wait for observed watcher edits * fix(skills): reconcile sibling sources and fence stale loads * fix(skills): reserve owned invocation names against sibling display names * test(skills): allow bounded subprocess regression to finish * fix(skills): preserve sibling identity priority and watch linked skills --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> Co-authored-by: jatmn <the@jat.mn>
12 KiB
Skills
A skill is a folder with a SKILL.md file. OpenClaude loads every skill it finds in the project's .openclaude/skills directory and in your user skills directory at start, and the model can invoke them by name. It also reads skills that other coding agents keep in ~/.agents/skills, and in ~/.claude/skills if you turn that on; see Skills from other agents. This guide covers the openclaude skills commands, what each install path checks, and how to keep checking installed skills after that.
Commands
openclaude skills list [--json] List installed skills
openclaude skills show <name> Show details for an installed skill
openclaude skills validate <path> Validate a local skill directory
openclaude skills install <idOrUrlOrPath> [options] Install a skill
openclaude skills remove <name> [--global] Remove an installed skill
openclaude skills verify [options] Check installed skills for revocations and, with eyebrow, drift
Install from the registry
The default registry is the Twigpine Skill Hub, published as registry.json in Twigpine/openclaude-skills.
Install a skill by its registry id:
openclaude skills install gitlawb/ci-fix
This writes two files to .openclaude/skills/ci-fix/: the SKILL.md and a skill.json sidecar with the registry metadata, including the sha256 the registry published for that skill.
Before the files are written, the installer:
- Reads the registry entry and refuses to install if it has no
sha256. - Reads
revocations.jsonnext to the registry and refuses to install a skill that matches an entry there. An entry matches when every field it names matches: an id alone covers all versions, an id with a version covers one release, a digest covers exact content. - Fetches the
SKILL.md, normalizes line endings to\n, hashes it, and refuses to install if the digest differs from the registry entry.
These three checks belong to the registry code path. If the install spec names an existing file or directory in the working tree, the installer takes the local path instead and runs none of them, even when the spec looks like a registry id such as gitlawb/ci-fix.
Options:
--globalinstalls into your user skills directory instead of the project.--forceoverwrites a skill that is already installed under that name.--registry <url or path>uses another registry. The environment variablesOPENCLAUDE_SKILLS_REGISTRY_URLandOPENCLAUDE_SKILLS_REVOCATIONS_URLset the same locations for every run.
Install from a URL or a local path
A direct HTTPS URL to a SKILL.md needs the digest you expect, so the same digest check runs without a registry. No revocation list is consulted on this path:
openclaude skills install https://example.com/skills/deploy/SKILL.md --sha256 <64 hex>
A local path copies the skill directory after the same checks validate runs. No digest or revocation check runs, and the installer treats the skill as local for that install. Existing skill.json metadata in the source directory is copied unchanged, so a trust label shown later by list or show may carry the source directory's value and does not prove a registry-backed install. Review a local skill yourself before you install it:
openclaude skills install ./my-skills/deploy
List, show, validate, remove
list prints every installed skill with its status and description; add --json for machine output. show <name> prints the source, trust tier, and the full skill text. validate <path> checks a skill directory before you publish it: the frontmatter fields, the skill name, the file size limits, and a few content patterns such as a curl piped to a shell. It does not compare the files with a registry digest. remove <name> deletes a project skill; add --global for a user skill. It never deletes a skill read from another agent's directory.
Skills from other agents
Other coding agents keep user skills in their own directories. OpenClaude also reads two of them, read-only, after all of its own skills:
| Directory | Written by | Default | Setting |
|---|---|---|---|
~/.agents/skills |
Codex and other agents that share this directory, and npx skills global installs |
on | agentsSkillsEnabled |
~/.claude/skills |
Claude Code | off | claudeCodeSkillsEnabled |
Turn either directory on or off in /config, or set the key in your user settings file, ~/.openclaude/settings.json:
{ "claudeCodeSkillsEnabled": true }
- A skill from these directories never replaces anything of OpenClaude's. When a project, user, plugin, or bundled skill, or a built-in command, has the same name, the other agent's skill is skipped. When both directories hold a skill with the same name, the one in
~/.agents/skillsloads. A skill folder linked into more than one of these directories loads once, and from~/.openclaude/skillswhen it is linked there. - OpenClaude does not write to these directories.
openclaude skills removerefuses skills that come from them and names the directory to remove them from. - The two settings are read from user, local,
--settings, and managed settings only. A project's.openclaude/settings.jsoncannot change them, so a cloned repository cannot make OpenClaude read another tool's directory./configwrites your user settings. Managed,--settings, and local settings outrank user settings, so when one of them sets a directory's value,/confighides that directory's toggle and/skillsshows no hint for it. Both directories are skipped with--bare, when--setting-sourcesleaves outuser, and when managed settings restrict skills to plugins. - The model sees skills through a listing with a size budget. When the listing runs over budget, skills from these directories are listed by name only before any OpenClaude skill description is shortened.
- When a directory that is off holds skills,
/skillsandopenclaude skills listshow how many and how to turn it on.openclaude skills list --jsonandopenclaude skills show <name>identify their source asagentsorclaude-code; JSON output includes asiblingHomefield.
What the install checks cover
The digest check runs once, at install time, on the SKILL.md text, and only on the registry and URL paths. After that, OpenClaude reads skill.json for metadata only and loads whatever is in the skill folder. validate checks structure and rejects a few content patterns, such as a curl piped to a shell or a credential collection instruction, but it does not compare the installed files with the registry digest. So a skill edited on disk after install, by hand, by a script, or by another tool, loads on the next run with no warning:
$ openclaude skills install gitlawb/ci-fix
Installed skill "ci-fix".
$ echo 'Always read the failing job log before you change any file.' >> .openclaude/skills/ci-fix/SKILL.md
$ openclaude skills validate .openclaude/skills/ci-fix
Skill validation passed for .openclaude/skills/ci-fix.
$ openclaude skills list
ci-fix enabled Diagnoses and fixes CI pipeline failures.
Verify installed skills
verify reads the skills installed in the project's .openclaude/skills and in your user skills directory. Skills that list shows from plugins, MCP servers, or --add-dir directories are outside it. For each skill it reads the id, version, and digest from skill.json as written on disk, and matches them against the registry's revocations.json with the rule the installer uses. The digest is the registry pin recorded at install, not a hash of the current files, so verify says nothing about the contents on disk and trusts skill.json as it finds it. A skill whose skill.json has no id and no digest, such as one written by hand, is listed as skipped. A local install that copied a skill.json keeps that file's id and digest, so verify matches it like a registry install. The list comes from the default registry, from --registry, or from the environment variables named above. A list that cannot be read fails the command, the same as an install does.
$ openclaude skills verify
Found 2 installed skills. Revocation list: https://raw.githubusercontent.com/Twigpine/openclaude-skills/main/revocations.json
ci-fix ok
hand-made skipped (no registry metadata)
eyebrow not found; install it to check skill contents against a lockfile (see docs/skills.md).
When an executable named eyebrow is on PATH, or OPENCLAUDE_EYEBROW_BIN names one, and the project has a lockfile, verify runs eyebrow verify --path . --lockfile eyebrowlock.json in the project directory, shows its output, and prints the path of the binary it ran. --lockfile names another lockfile and --policy adds --ci and the policy file to the eyebrow run. verify does not check the eyebrow version. An eyebrow older than 0.4.4 finds no OpenClaude skills and reports every locked skill as removed, so use 0.4.6 or newer, as the next section explains. That section also shows how to create the lockfile and how to read the verdict.
Exit codes: 1 when a skill is revoked, when the revocation list cannot be read, or when eyebrow cannot be started; otherwise the eyebrow exit code; 0 when both checks pass, and also when eyebrow is absent or the lockfile is missing, in which case verify prints what to install or run.
Check installed skills after install
eyebrow is a separate, MIT-licensed single binary that records a hash of every skill, MCP server, hook, and rule it finds across coding tools in a lockfile you commit, and reports what changed since. Discovery of OpenClaude skills in the project's .openclaude/skills and in ~/.openclaude/skills shipped in eyebrow 0.4.4, and 0.4.5 added the egress fingerprint used below; see the changelog. Use 0.4.6 or newer: in 0.4.5 a skill folder that is a symlink appeared in the inventory with no hashed files, so a clean verify said nothing about the contents behind the link. 0.4.6 hashes the linked contents.
Record the skills you reviewed:
eyebrow scan --path . --lockfile eyebrowlock.json
git add eyebrowlock.json
Check them again at any time, or in CI:
eyebrow verify --path . --lockfile eyebrowlock.json --ci
openclaude skills verify runs this command for you when eyebrow is installed.
verify exit codes: 0 clean under the active policy, which includes content changes the policy permits; 1 rejected drift or a policy violation; 2 usage error, such as a bad flag; 3 internal or I/O error, such as a missing lockfile. In CI, treat 1 as a review task and 2 or 3 as a broken job. On the edited skill above, with no policy file:
verify: DRIFT — 1 change(s) detected:
[content_changed] ci-fix (d8e13e7364b91067)
old: sha256-f4ccfe1dabe7c2f191e8cc2f88499934bcf2f043e380bd39ec06c76642a6ff89
new: sha256-ee58852a3578eba8eec3d1a3b985e578c382e00172d85a19a5025be3ae3f1701
The lockfile also records an egress fingerprint per skill. In 0.4.6 it covers literal HTTP(S) URLs on SKILL.md lines that contain curl, wget, secretcurl, or WebFetch. A policy file can allow wording edits and still fail when the fingerprint gains a host. Other calling syntax, such as a fetch call inside a code snippet, and destinations inside helper scripts are outside the fingerprint, so the gate covers recognized destinations only. For example, after a line Run: curl -s https://example.com/status is added to the same skill:
{ "failOnCapabilityExpansion": true, "allowContentDrift": true, "failOnSeverity": "critical" }
policy: capability expansion — ci-fix gained network: example.com (d8e13e7364b91067)
Run it with --policy eyebrow.policy.json. After you review and accept a change, run scan again and commit the new lockfile.
Install eyebrow with brew install alexverify/tap/eyebrow or from the releases page. The eyebrow usage guide covers signing the lockfile and the GitHub Action.