Anthropic pitched Agent Skills as portable — the same SKILL.md should run on any compatible platform, just as MCP promised for tools. That promise holds for the format, not for everything advanced skills actually rely on.
What agentskills.io mandates — and what it intentionally omits

The agentskills.io standard is deliberately minimal. It requires a directory containing one SKILL.md file whose YAML frontmatter carries exactly two mandatory fields: name (capped at 64 characters) and description (capped at 1024 characters) . Everything else — license, compatibility, metadata, experimental allowed-tools, and the scripts/, references/, assets/ directories — is optional or implementor-defined . Anthropic published the spec and SDK on December 18, 2025, roughly two months after skills first shipped in Claude Code.
The standard defines progressive disclosure across three tiers: a compact catalog entry of about 50–100 tokens per skill, the full SKILL.md body loaded on trigger, and bundled resources fetched on demand — with activated instructions recommended under 5000 tokens . Critically, the runtime contract reduces to three primitives: filesystem access, bash, and code execution. There is no hook ABI, no permission-pool mutation, and no subagent-fork primitive in the shared spec.
Nous Research's Hermes Agent — an MIT-licensed, model-agnostic runtime — satisfies all three tiers. It exposes skills_list() for the catalog index and skill_view(name) for the full body, stores skills under ~/.hermes/skills/, and runs them across six terminal backends: local, Docker, SSH, Singularity, Modal, and Daytona . That is exactly why plain skills port cleanly — and why the next section's harness-bound fields do not.
PreToolUse, CLAUDE_* env vars, fork-context: what quietly disappears on Hermes

Claude Code follows the agentskills.io standard but extends it with three capability layers the open spec never defines: invocation control, subagent execution, and dynamic context injection . On Hermes those extensions are unrecognized frontmatter keys and unset variables. The SKILL.md still loads — but the enforcement, isolation, and pre-render guarantees are gone, and nothing warns you.
The trap is silence. A skill that gates a destructive query behind a PreToolUse matcher running ./scripts/validate-readonly-query.sh parses fine on Hermes, but the callback never fires — Hermes has no Claude Code hook ABI, so the matcher is just an unknown key . Enforcement that was meant to live in the harness silently falls back to the model. The same happens field by field:
| Claude Code extension | Purpose | Hermes behavior |
|---|---|---|
hooks: (PreToolUse, etc.) | Deterministic tool gating outside the model | Unknown frontmatter key — callback never runs |
allowed-tools / disallowed-tools | Permission syntax like Bash(git *), Skill(name *) | Read but not acted on — permission surface stays fully open |
context: fork + agent: Explore | Run skill body as an isolated subagent | Unrecognized key — skill runs inline, no forked isolation |
CLAUDE_* env vars | Session/effort/dir injection into subprocess | Unset on any non-Claude runtime |
Inline !`cmd` substitution | Pre-render command output into the body | Never resolved — arrives as a literal marker |
The permission gap is the sharpest edge. Claude-specific syntax such as Bash(git *) and Skill(name *) has no portable equivalent; Hermes reads the keys but applies no gate, so a skill authored to allow only read tools runs unrestricted . Context injection breaks just as quietly: CLAUDE_SESSION_ID, CLAUDE_EFFORT, CLAUDE_SKILL_DIR, and CLAUDE_PROJECT_DIR — the last requiring Claude Code v2.1.196+ — resolve to empty strings, and an inline !`git diff HEAD` block lands in model context unexecuted. The skill looks intact; every guarantee that made it safe is gone.
Four questions to ask before transferring any SKILL.md to Hermes

Before you copy a SKILL.md into ~/.hermes/skills/, audit its frontmatter and body against four questions. Each one maps a Claude Code extension to the exact way it degrades on Hermes — silently, with no error — so you know whether to rewrite, re-enforce, or accept the loss before the skill ships.
- Does the frontmatter declare a hook block? Look for
PreToolUse,PostToolUse,SessionStart,PermissionRequest, or any other lifecycle event . Hermes does not read a skill's active hook block, so the guard never fires. Rewrite the logic as a Hermes plugin hook viactx.register_hook()— the closest analog ispre_tool_call— or strip the block and document the missing enforcement in the skill body so no one assumes it still runs . - Does it use
allowed-toolsordisallowed-tools? These mutate Claude Code's per-turn permission pool; Hermes ignores them entirely, leaving the full tool surface exposed . Re-enforce access through a Hermes gateway hook (HOOK.yamlplushandler.py), or accept the unrestricted surface knowingly. - Does the body contain
!`cmd`substitution or readCLAUDE_*variables? If so, the markers arrive verbatim in model context and the variables resolve to empty strings. Replace them with explicit tool calls or static reference text. - Does it set
context: forkoragent:a subagent type? Subagent isolation disappears — the skill runs in the main conversation thread with the same memory and tool surface as the calling session . Move any genuinely isolated work to a separate Hermes invocation.
The distinction to hold onto comes straight from the vendor docs:
"Claude Code skills follow the Agent Skills standard but extend it with invocation control, subagent execution, and dynamic context injection." — Claude Code documentation (source: code.claude.com)
Answer "yes" to any question and the skill is harness-bound. The format still loads on Hermes; the behavior does not travel with it.
Replacing no-op entries with Hermes pre_tool_call registration
Once triage flags a harness-bound skill, the fix is a rewrite against Hermes' own hook API, not a copy. Hermes exposes plugin hooks through ctx.register_hook() with eight documented names — pre_tool_call, post_tool_call, pre_llm_call, post_llm_call, pre_verify, subagent_start, subagent_stop, and transform_tool_result . A Claude Code PreToolUse matcher that shell-blocked a write maps roughly onto a pre_tool_call plugin hook returning a deny response. The intent overlaps; the ABI does not. Event names only partially align — subagent_start is a direct match — but payload schemas and return contracts differ, so you port the logic, not the file.
"Claude Code skills follow the Agent Skills standard but extend it with invocation control, subagent execution, and dynamic context injection." — Claude Code documentation (source: code.claude.com)
Plain packages need none of this. A SKILL.md that is just instructions plus bundled scripts, with no Claude-specific frontmatter, transfers unchanged; Hermes installs from official, skills-sh, GitHub taps, ClawHub, and direct URLs . At install time Hermes adds its own supply-chain defense: hub-installed skills are scanned for data-exfiltration patterns, prompt injection, and destructive commands, and a dangerous verdict blocks installation even with --force . That is defense-in-depth, not a replacement for the hook enforcement you lost.
The takeaway: the agentskills.io spec ships no versioned compatibility matrix declaring which extension fields a conformant runtime must honor versus silently ignore . Until one exists, treat every migration as manual triage — port plain skills as-is, and rebuild anything encoding policy as a Hermes pre_tool_call hook.
Frequently asked questions
Does agentskills.io compliance guarantee a SKILL.md runs identically on Hermes and Claude Code?
No. Compliance covers only the base format: the required name (capped at 64 characters) and description (capped at 1024 characters) frontmatter, bundled scripts/, references/, and assets/ directories, and three-tier progressive disclosure . Claude Code's own docs state it extends the standard with invocation control, subagent execution, and dynamic context injection . Those extensions sit above the spec and silently no-op on any conformant but non-Claude runtime such as Hermes.
Which Claude Code hook events have a functional equivalent in Hermes?
Only a partial overlap. Claude Code's PreToolUse and PostToolUse map roughly to Hermes' pre_tool_call and post_tool_call plugin hooks registered via ctx.register_hook(), and subagent_start has a direct name match . But Claude Code's broader lifecycle — SessionStart, UserPromptSubmit, PermissionRequest, PreCompact, WorktreeCreate, and more — has no direct counterpart . Event payloads and return contracts differ, so any hook migration is a rewrite, not a direct port.
What happens to allowed-tools / disallowed-tools declarations on Hermes?
Hermes reads the frontmatter but does not implement Claude Code's per-turn permission pool, so the fields are treated as unrecognized keys. Claude-specific syntax like Bash(git *) or Skill(name *) has no portable equivalent . The practical result: tool access is unrestricted by default unless a separate Hermes gateway hook (HOOK.yaml + handler.py) enforces it explicitly .
Can a skill installed from ClawHub include active hook blocks, and will they fire?
A hub-distributed SKILL.md may carry Claude Code hook frontmatter, but the blocks will not fire on Hermes. Hermes runs a security scan at install time — flagging data exfiltration, prompt injection, destructive commands, and supply-chain signals, and blocking dangerous verdicts even with --force . At execution time, though, it treats the hook fields as unknown keys, so they never execute. That install-time scan is defense-in-depth, not a substitute for runtime enforcement.
Is there a spec-level compatibility matrix that lists which extension fields a runtime must support?
No. The agentskills.io spec, published as an open standard on December 18, 2025, defines the minimum viable format but does not enumerate a versioned matrix of extension fields that conformant runtimes must honor or explicitly ignore . The runtime contract reduces to filesystem access, bash, and code execution . Portability is therefore a per-field manual check, not a version declaration.
Enjoyed this article? Subscribe to get new stories by email whenever they're published.