Pre-release Harn is pre-1.0 — the language, standard library, and CLI may change between releases. See the release notes

Skills

Harn discovers skills — bundled instructions, tool lists, and activation rules — from the filesystem and from the host process. Every skill is a directory containing a SKILL.md file with YAML frontmatter plus a Markdown body; the format matches Anthropic's Agent Skills and Claude Code specs, so skills you author once work across both environments.

This page describes:

  • the layered discovery hierarchy (CLI > env > project > manifest > user > package > system > host),
  • the SKILL.md frontmatter Harn recognizes, including the required compact short: card,
  • the body substitution ($ARGUMENTS, $N, ${HARN_SKILL_DIR}, ${HARN_SESSION_ID}) that runs over SKILL.md before the model sees it,
  • the harn.toml [skills] / [[skill.source]] tables, and
  • the harn doctor output for diagnosing collisions / missing entries.

The companion language form — skill NAME { ... } — is documented in Language basics and the skill builtins (skill_registry, skill_define, skill_find, skill_list, skill_render, load_skill, skills_catalog_entries, render_always_on_catalog, skills_activation_evidence, …) in Builtin functions.

The stable, host-consumable record of which cards were shown or omitted and why — for Burin, headless runs, cloud, and the portal — is documented in Skill activation evidence.

Layered discovery#

When harn run / harn test / harn check starts, every discovered skill is merged into a single registry and exposed as the pre-populated VM global skills. That startup registry is intentionally compact: it keeps the frontmatter card and activation metadata, but leaves the full Markdown body behind the lazy harness.agent.load_skill(...) path. The layers — in order of highest to lowest priority — are:

#LayerSourceWhen
1CLI--skill-dir <path> (repeatable)Ephemeral overrides, CI pinning
2Env$HARN_SKILLS_PATH (colon-separated on Unix, ; on Windows)Deployment config, Docker, cloud agents
3Project.harn/skills/<name>/SKILL.md walking up from the scriptDefault for repo-scoped skills
4Manifest[skills] paths + [[skill.source]] in harn.tomlMulti-root, shared across siblings
5User~/.harn/skills/<name>/SKILL.mdPersonal skills across projects
6Packagecurrent generation packages/**/skills/<name>/SKILL.mdSkills shipped via [dependencies]
7System/etc/harn/skills/ + $XDG_CONFIG_HOME/harn/skills/Managed / enterprise
8HostRegistered via the bridge at runtimeCloud / embedded hosts

Name collisions: when two layers both expose a skill named deploy, the higher layer wins. The shadowed entry is recorded so harn doctor can surface it. Scripts that need both at once can register a fully-qualified <namespace>/<skill> id via [[skill.source]] in the manifest (see below).

SKILL.md frontmatter#

The frontmatter is YAML, delimited by --- on its own line above and below. Unknown fields are not hard errors — harn doctor reports them as warnings so newer spec fields roll out cleanly.

See Skill provenance for detached signatures, trusted signers, and harness.agent.load_skill(..., require_signature: true).

---
name: deploy
short: Deploy the application when the user asks for a release
description: Deploy the application to production
when-to-use: User says deploy / ship / release
disable-model-invocation: false
user-invocable: true
allowed-tools: [bash, git]
paths:
  - infra/**
  - Dockerfile
context: fork
agent: ops-lead
model: claude-opus-4-7
effort: high
shell: bash
argument-hint: "<target-env>"
hooks:
  on-activate: echo "starting deploy"
  on-deactivate: echo "deploy ended"
---
# Deploy runbook
Ship it: `$ARGUMENTS`. Skill directory: `${HARN_SKILL_DIR}`.

Recognized fields (Harn normalizes hyphens to underscores, so when-to-use and when_to_use are the same key):

FieldTypePurpose
namestringOptional in frontmatter when the directory name already provides it; Harn falls back to the enclosing skill directory basename.
shortstringRequired. One-sentence compact card describing what the skill does and when to use it. Always loaded into the startup registry and catalogs.
descriptionstringOptional longer summary. Useful for richer CLI output or custom matchers, but not required for lazy discovery.
when-to-usestringLonger activation trigger.
disable-model-invocationboolIf true, never auto-activate — explicit use only.
allowed-toolslist of stringRestrict tool surface while the skill is active. Entries accept three shapes: an exact tool name ("deploy_service"), a namespace tag ("namespace:read" — matches every tool declared with namespace: "read"), or "*" (escape hatch that keeps the full surface, useful for skills that only carry prompt context).
user-invocableboolExpose the skill to end users via a slash menu.
pathslist of globFiles the skill expects to touch.
contextstring"fork" runs in an isolated subcontext.
agentstringSub-agent that owns the skill.
hooksmap or listShell commands for lifecycle events. Filesystem skills only surface hooks when their detached provenance verifies as trusted.
modelstringPreferred model alias.
effortstringlow / medium / high.
require-signatureboolRequire a valid detached signature before the skill is admitted to the startup registry or promoted by harness.agent.load_skill(...).
trusted-signerslist of stringOptional signer fingerprint allowlist layered on top of the trusted registry.
shellstringShell to run the body under when context is shell-ish.
argument-hintstringUI hint for $ARGUMENTS.

Tool scoping with namespace:<tag>#

Tool declarations that carry a namespace: field can be grouped into one allowed-tools entry instead of enumerating names. Given

tool_define(reg, "read_file", "...", {namespace: "read", ...})
tool_define(reg, "list_files", "...", {namespace: "read", ...})
tool_define(reg, "write_file", "...", {namespace: "write", ...})

a skill with allowed-tools: ["namespace:read"] scopes the turn to read_file + list_files and hides write_file. Exact tool names and the wildcard "*" remain valid and can mix freely:

allowed-tools: ["namespace:read", "grep", "*"]

Malformed entries fail loudly at skill_define time — a bare ":" without a tag or a colon-prefixed entry that isn't namespace: raises so authors don't silently scope to an empty set.

Body substitution#

When a skill body is rendered (via skill_render, load_skill, or by a host before handing the body to the model), the following substitutions run over the Markdown body:

  • $ARGUMENTS → all positional args joined with spaces
  • $N → the N-th positional arg (1-based). $0 is reserved.
  • ${HARN_SKILL_DIR} → absolute path to the skill directory
  • ${HARN_SESSION_ID} → opaque session id threaded through the run
  • ${OTHER_NAME} → looks up OTHER_NAME in the process environment
  • $ → literal $

Missing positional args ($3 when only $1 was supplied) pass through unchanged so authors see what wasn't supplied rather than a silent empty substitution.

const deploy = skill_find(skills, "deploy")
guard deploy != nil else {
  harness.runtime.exit(1)
}

const rendered = skill_render(deploy, ["prod", "us-east-1"])
// rendered now has $1 and $2 replaced with "prod" and "us-east-1".
const with_session = skill_render(
  r"Session: ${HARN_SESSION_ID}",
  [],
  {session_id: "sess-123"},
)
// with_session is "Session: sess-123"
// even when `HARN_SESSION_ID` is unset.

skill_render takes an optional third argument: a session-id string or {session_id: ...}. That explicit id wins over the process environment, so in-process and headless embedders can substitute without exporting HARN_SESSION_ID. agent_loop threads the live session id through this argument when a host-supplied registry entry is rendered after lazy load_skill fails.

Progressive disclosure with load_skill#

Lazy skill loading surfaces in two places, both resolving the skill id against the active registry and applying the same substitution rules:

  • harness.agent.load_skill("deploy") is the Harn-code entry point. It hydrates the full SKILL.md, applies substitution, and returns the rendered body as a string.
  • When an agent loop receives a skill registry through skills:, Harn also exposes a runtime-owned load_skill tool (arguments { name }) for the model. That tool calls the same lazy loader and returns the rendered body in the next turn.

Builtin example:

fn main(harness: Harness) {
  const runbook = harness.agent.load_skill("deploy")
  assert(
    contains(runbook, "Deploy runbook"),
    "full body is fetched lazily",
  )
}

If the target skill has disable-model-invocation: true, the runtime tool returns a typed error instead of leaking the body. Direct Harn-code harness.agent.load_skill("name") calls are explicit and are not blocked by that flag.

Always-on catalog helper#

The recommended harness convention is:

  1. Keep a compact catalog of available skills in the system prompt.
  2. Let the model call the runtime load_skill tool (arguments { name }) only when one of those entries looks relevant.

Harn ships two pure helpers for that pattern:

const entries = skills_catalog_entries(skills)
const catalog = render_always_on_catalog(entries, 2000)

skills_catalog_entries projects the resolved registry into compact {name, description, when_to_use} cards, with description sourced from the required short: frontmatter (sorted deterministically by skill id, using <namespace>/<name> when present). render_always_on_catalog formats those cards into a stable prompt block and trims the list to the requested character budget.

Copy-pasteable example:

const catalog = render_always_on_catalog(
  skills_catalog_entries(skills), 2000,
)

const result = agent_loop(harness,
  "Help me ship this release",
  catalog,
  {
    provider: "mock",
    model: "gpt-5.4",
    loop_until_done: true,
    skills: skills,
  },
)

On a later turn the model can emit:

load_skill({ name: "deploy" })

and the next turn will see the substituted SKILL.md body in the tool result, while any allowed-tools declared by that skill narrow the tool surface for subsequent turns.

harn.toml [skills] + [[skill.source]]#

Projects that share skills across siblings or pull them from a remote tag use the manifest instead of a per-script flag:

[skills]
paths = ["packages/*/skills", "../shared-skills"]
lookup_order = ["cli", "project", "manifest", "user", "package", "system", "host"]
disable = ["system"]
signer_registry_url = "./signers"

[skills.defaults]
tool_search = "bm25"
always_loaded = ["look", "edit", "bash"]

[[skill.source]]
type = "fs"
path = "../shared"

[[skill.source]]
type = "git"
url = "https://github.com/acme/harn-skills"
tag = "v1.2.0"

[[skill.source]]
type = "registry"   # reserved, inert until a marketplace exists
url = "https://skills.harnlang.com"
name = "acme/ops"
  • paths is joined against the directory holding harn.toml and supports a single trailing * component (packages/*/skills).
  • lookup_order inverts layer priority — for example, preferring user over project on a personal checkout without touching the repo.
  • disable removes entire layers from discovery; harn doctor reports the disabled set.
  • signer_registry_url points at a flat directory or URL prefix that serves <fingerprint>.pub signer files for skill signature verification.
  • Skills that declare require_signature = true are omitted from the startup registry unless their detached signature chain verifies.
  • User and system layer skills are also omitted when they carry a failed provenance check; unsigned entries can still load, but executable hook frontmatter is only surfaced for verified skills.
  • [[skill.source]] entries of type git expect their materialized checkout under the current package generation's packages/<name>/skills/ — run harn install to populate it.
  • registry entries are accepted but inert until a Harn Skills marketplace exists.

harn doctor#

harn doctor reports the resolved skill catalog:

  OK   skills                 3 loaded (1 cli, 1 project, 1 user)
  WARN skill:deploy           shadowed by cli layer; user version at /home/me/.harn/skills/deploy is hidden
  WARN skill:review           unknown frontmatter field(s) forwarded as metadata: future_field
  SKIP skills-layer:system    layer disabled by harn.toml [skills.disable]

CLI flags#

  • harn run --skill-dir <path> (repeatable) — highest-priority layer.
  • harn test --skill-dir <path> — same semantics for user tests and conformance fixtures.
  • $HARN_SKILLS_PATH — colon-separated list of directories, applied to every invocation.

Bridge protocol#

Hosts expose their own managed skill store through three RPCs:

  • skills/list (request) — response is an array of { id, name, description, source } entries.
  • skills/fetch (request) — payload { id: "<skill id>" }; response is the full manifest + body shape so the CLI can hydrate a SkillManifestRef into a Skill.
  • skills/update (notification, host → VM) — invalidates the VM's cached catalog. The CLI re-runs discovery on the next boundary.

See Bridge protocol for wire-format details.

Managing skills#

The harn skill noun covers two complementary surfaces:

  • Canonical corpus — the Harn skills normally shipped inside the harn binary (harn-language, harn-orchestration, harn-testing, …). Access these with harn skill list, harn skill get <name>, and harn skill dump --all. Set HARN_SKILLS_DIR to a directory of recursive SKILL.md files to make list, get, and dump read from disk while iterating locally; an unset, missing, or empty directory falls back to the embedded corpus.
  • Layered FS discovery — user-authored skills picked up from --skill-dir, HARN_SKILLS_PATH, project, manifest, user, packages, system, and host layers. Access these with harn skill resolved, harn skill inspect <name>, and harn skill match. What you see there is byte-for-byte the registry that harn run / harn test / harn check hand to the VM.

Run harn skill validate <path> before committing any filesystem skill. It uses the same parser and required-field checks as those runtime commands.

harn skill list#

Lists the canonical skill corpus for this build of harn. By default that is the embedded corpus; when HARN_SKILLS_DIR points at one or more recursive SKILL.md files, the disk corpus is listed instead. Pass --json for a versioned JsonEnvelope payload that agents and CI can pipe through jq.

$ harn skill list
Embedded canonical skills (16):
  harn-agent            Agent runtime, lifecycle, capabilities, and supervision.
  harn-apps             Build Harn apps with typed views, event handlers, and model jobs.
  harn-control-events   Stop, steer, and queue as events a session must answer, not requests it…
  harn-de-slop          Remove duplicated policy, shallow seams, and weak contracts.
  harn-diagnostics      Diagnostics, the HARN-* error-code index, explain output, repair plans,…
  harn-docs             Write task-shaped developer documentation in plain language.
  harn-language         Harn syntax, modules, types, diagnostics, and script structure.
  harn-mcp              Connect Harn to MCP servers and expose Harn pipelines as MCP servers.
  harn-orchestration    Workflows, triggers, workers, handoffs, and lifecycle ownership.
  harn-probe            Evidence-driven investigation for material or unstable claims.
  harn-product-quality  Launch-quality product behavior across Harn-powered surfaces.
  harn-providers        LLM provider configuration, model routing, and provider capability beha…
  harn-rules            Structural search, lint rules, and codemods with the Harn rule engine.
  harn-testing          Deterministic, claim-driven Harn verification.
  harn-tracing          Transcripts, receipts, traces, replay, and observability surfaces.
  release-harn          Merge-queue-safe Harn patch/minor/major release workflow.

Run `harn skill get <name>` for one entry's frontmatter.
Run `harn skill get <name> --full` to include the body.

The --json output uses the standard envelope (schemaVersion, ok, data, error, warnings). The data.skills array contains the same frontmatter fields shown above:

$ harn skill list --json | jq '.data.skills | length'
13

harn skill get <name>#

Prints frontmatter for a single canonical skill. Pass --full to include the SKILL.md body; pass --json to wrap the output in a JsonEnvelope for machine consumption. HARN_SKILLS_DIR follows the same disk-override/fallback behavior as harn skill list.

$ harn skill get harn-language
name:        harn-language
short:       Harn syntax, modules, types, diagnostics, and script structure.
description: Use for Harn language syntax, typechecking, modules, imports, and idiomatic script authoring.
when_to_use: Use when writing, reviewing, or explaining Harn source code and language-level behavior.

$ harn skill get harn-language --full --json | jq '.data | keys'
[
  "body",
  "description",
  "name",
  "short",
  "when_to_use"
]

Unknown names return ok: false with a skill_not_found error code and the list of available skills in error.details.available.

harn skill dump --all [--out <dir>]#

Writes every active canonical skill to disk as a <dir>/<name>/SKILL.md tree so agents and CI can review the corpus offline. By default that means the embedded corpus; when HARN_SKILLS_DIR contains recursive SKILL.md files, dump mirrors that disk corpus byte-for-byte instead. Defaults the output directory to ./skills. Refuses to overwrite existing files unless --force is passed.

$ harn skill dump --all --out /tmp/skills
Wrote 13 skill(s) to /tmp/skills
  /tmp/skills/harn-agent/SKILL.md
  /tmp/skills/harn-diagnostics/SKILL.md
  …

harn skill resolved#

Prints every FS-resolved skill with the layer it came from. Pass --all to include shadowed entries; pass --json for newline-delimited JSON records:

$ harn skill resolved
Resolved skills (3):
  deploy         [cli]       Deploy to production when release work is requested
  review         [project]   Review a pull request when asked for code review help
  helpers/utils  [package]   Shared helpers when the task needs the acme/ops package

Shadowed skills (1):
  deploy   winner=[cli] hidden=[user] origin=/home/me/.harn/skills/deploy

harn skill inspect <name>#

Dumps the resolved SKILL.md — frontmatter, bundled files under the skill directory, and the full body — for a specific skill. Accepts bare <name> or fully-qualified <namespace>/<name>:

$ harn skill inspect deploy
id:          deploy
name:        deploy
layer:       cli
short:       Deploy to production when release work is requested
description: Deploy to production with rollback support
skill_dir:   /repo/.harn/skills/deploy

Bundled files:
  files/runbook.md
  files/rollback.sh

---- SKILL.md body ----
Run the deploy. Confirm replicas and then flip traffic.

harn skill match "<query>"#

Runs the built-in metadata matcher (same scorer the agent loop uses) against a prompt and prints the ranked candidates with their scores. Supports --working-file to simulate path-glob matches:

$ harn skill match "deploy the staging service" --top-n 3
Match results for: deploy the staging service
   1. deploy              score=2.400  [cli]       prompt mentions 'deploy'; 1 keyword hit(s)
   2. review              score=0.400  [project]   1 keyword hit(s)

Confirms that a SKILL.md's short: and when_to_use: frontmatter attract the intended prompts.

harn skill install <spec>#

Materializes a git ref or local path into .harn/skills-cache/ so the filesystem package walker picks it up on the next run. The .harn/skills-cache/ uses the same package-per-directory shape as a generation's packages/:

$ harn skill install acme/harn-skills --tag v1.2.0
installing acme/harn-skills to .harn/skills-cache/harn-skills
installed — layer=package, path=.harn/skills-cache/harn-skills

<spec> accepts:

  • A full git URL: https://github.com/acme/harn-skills.git
  • owner/repo shorthand (expands to GitHub): acme/harn-skills
  • A local filesystem path: ../shared/skills/deploy

Pass --namespace <ns> to shelf the install under a subdirectory so it shows up in the resolver as <ns>/<skill>. Pass --tag <ref> to pin a git branch or tag. Every install rewrites .harn/skills-cache/skills.lock with the resolved source + commit.

harn skill new <name>#

Scaffolds a new SKILL.md and files/ directory under .harn/skills/:

$ harn skill new deploy --description "Deploy to production"
Scaffolded skill 'deploy' at .harn/skills/deploy
  SKILL.md
  files/README.md

Edit the SKILL.md frontmatter and body, then run `harn skill resolved`
after `harn skill validate .harn/skills/deploy` succeeds.

harn skill validate [<path>]#

Validates one skill directory or SKILL.md file through Harn's canonical parser. The path defaults to the current directory. A valid skill must have well-formed YAML frontmatter, a non-empty short card, and a name supplied by either frontmatter or the containing directory.

$ harn skill validate .harn/skills/deploy
valid: .harn/skills/deploy
id:    deploy
short: Deploy the application when the user asks for a release
files: 2 bundled file(s)

Unknown frontmatter fields are warnings by default so a newer skill format can still load on an older Harn release. Use --strict in CI to reject them. Use --json for the versioned envelope listed by harn --json-schemas:

harn skill validate .harn/skills/deploy --strict --json \
  | jq -e '.ok and .data.id == "deploy"'

Detached signing, endorsement, verification, and local signer trust live under the same harn skill command. See Skill provenance for the complete workflow.

Pass --dir <path> to target a different destination (for example ~/.harn/skills/deploy to scaffold under the user layer instead of the project layer), or --force to overwrite an existing directory.

Portal observability#

The Harn portal (harn portal) surfaces two skill-focused panels on every run detail page:

  • Skill timeline — horizontal bars showing which skills activated on which agent-loop iteration and when they deactivated. Hover a bar for the matcher score and the reason the skill was promoted.
  • Tool-load waterfall — one row per tool_search_query event, pairing each query with its tool_search_result so you can see which deferred tools entered the LLM's context in each turn.
  • Matcher decisions — per-iteration expansions showing every candidate the matcher considered, its score, and the working-file snapshot it scored against.

The runs index page takes a skill=<name> filter so you can narrow evals to runs where a specific skill was active. The same skill=<name> query parameter works from a URL, making it easy to link to "every run that used deploy".

Pre-release Harn is pre-1.0 — the language, standard library, and CLI may change between releases. See the release notes

Skills

Harn discovers skills — bundled instructions, tool lists, and activation rules — from the filesystem and from the host process. Every skill is a directory containing a SKILL.md file with YAML frontmatter plus a Markdown body; the format matches Anthropic's Agent Skills and Claude Code specs, so skills you author once work across both environments.

This page describes:

  • the layered discovery hierarchy (CLI > env > project > manifest > user > package > system > host),
  • the SKILL.md frontmatter Harn recognizes, including the required compact short: card,
  • the body substitution ($ARGUMENTS, $N, ${HARN_SKILL_DIR}, ${HARN_SESSION_ID}) that runs over SKILL.md before the model sees it,
  • the harn.toml [skills] / [[skill.source]] tables, and
  • the harn doctor output for diagnosing collisions / missing entries.

The companion language form — skill NAME { ... } — is documented in Language basics and the skill builtins (skill_registry, skill_define, skill_find, skill_list, skill_render, load_skill, skills_catalog_entries, render_always_on_catalog, skills_activation_evidence, …) in Builtin functions.

The stable, host-consumable record of which cards were shown or omitted and why — for Burin, headless runs, cloud, and the portal — is documented in Skill activation evidence.

Layered discovery#

When harn run / harn test / harn check starts, every discovered skill is merged into a single registry and exposed as the pre-populated VM global skills. That startup registry is intentionally compact: it keeps the frontmatter card and activation metadata, but leaves the full Markdown body behind the lazy harness.agent.load_skill(...) path. The layers — in order of highest to lowest priority — are:

#LayerSourceWhen
1CLI--skill-dir <path> (repeatable)Ephemeral overrides, CI pinning
2Env$HARN_SKILLS_PATH (colon-separated on Unix, ; on Windows)Deployment config, Docker, cloud agents
3Project.harn/skills/<name>/SKILL.md walking up from the scriptDefault for repo-scoped skills
4Manifest[skills] paths + [[skill.source]] in harn.tomlMulti-root, shared across siblings
5User~/.harn/skills/<name>/SKILL.mdPersonal skills across projects
6Packagecurrent generation packages/**/skills/<name>/SKILL.mdSkills shipped via [dependencies]
7System/etc/harn/skills/ + $XDG_CONFIG_HOME/harn/skills/Managed / enterprise
8HostRegistered via the bridge at runtimeCloud / embedded hosts

Name collisions: when two layers both expose a skill named deploy, the higher layer wins. The shadowed entry is recorded so harn doctor can surface it. Scripts that need both at once can register a fully-qualified <namespace>/<skill> id via [[skill.source]] in the manifest (see below).

SKILL.md frontmatter#

The frontmatter is YAML, delimited by --- on its own line above and below. Unknown fields are not hard errors — harn doctor reports them as warnings so newer spec fields roll out cleanly.

See Skill provenance for detached signatures, trusted signers, and harness.agent.load_skill(..., require_signature: true).

---
name: deploy
short: Deploy the application when the user asks for a release
description: Deploy the application to production
when-to-use: User says deploy / ship / release
disable-model-invocation: false
user-invocable: true
allowed-tools: [bash, git]
paths:
  - infra/**
  - Dockerfile
context: fork
agent: ops-lead
model: claude-opus-4-7
effort: high
shell: bash
argument-hint: "<target-env>"
hooks:
  on-activate: echo "starting deploy"
  on-deactivate: echo "deploy ended"
---
# Deploy runbook
Ship it: `$ARGUMENTS`. Skill directory: `${HARN_SKILL_DIR}`.

Recognized fields (Harn normalizes hyphens to underscores, so when-to-use and when_to_use are the same key):

FieldTypePurpose
namestringOptional in frontmatter when the directory name already provides it; Harn falls back to the enclosing skill directory basename.
shortstringRequired. One-sentence compact card describing what the skill does and when to use it. Always loaded into the startup registry and catalogs.
descriptionstringOptional longer summary. Useful for richer CLI output or custom matchers, but not required for lazy discovery.
when-to-usestringLonger activation trigger.
disable-model-invocationboolIf true, never auto-activate — explicit use only.
allowed-toolslist of stringRestrict tool surface while the skill is active. Entries accept three shapes: an exact tool name ("deploy_service"), a namespace tag ("namespace:read" — matches every tool declared with namespace: "read"), or "*" (escape hatch that keeps the full surface, useful for skills that only carry prompt context).
user-invocableboolExpose the skill to end users via a slash menu.
pathslist of globFiles the skill expects to touch.
contextstring"fork" runs in an isolated subcontext.
agentstringSub-agent that owns the skill.
hooksmap or listShell commands for lifecycle events. Filesystem skills only surface hooks when their detached provenance verifies as trusted.
modelstringPreferred model alias.
effortstringlow / medium / high.
require-signatureboolRequire a valid detached signature before the skill is admitted to the startup registry or promoted by harness.agent.load_skill(...).
trusted-signerslist of stringOptional signer fingerprint allowlist layered on top of the trusted registry.
shellstringShell to run the body under when context is shell-ish.
argument-hintstringUI hint for $ARGUMENTS.

Tool scoping with namespace:<tag>#

Tool declarations that carry a namespace: field can be grouped into one allowed-tools entry instead of enumerating names. Given

tool_define(reg, "read_file", "...", {namespace: "read", ...})
tool_define(reg, "list_files", "...", {namespace: "read", ...})
tool_define(reg, "write_file", "...", {namespace: "write", ...})

a skill with allowed-tools: ["namespace:read"] scopes the turn to read_file + list_files and hides write_file. Exact tool names and the wildcard "*" remain valid and can mix freely:

allowed-tools: ["namespace:read", "grep", "*"]

Malformed entries fail loudly at skill_define time — a bare ":" without a tag or a colon-prefixed entry that isn't namespace: raises so authors don't silently scope to an empty set.

Body substitution#

When a skill body is rendered (via skill_render, load_skill, or by a host before handing the body to the model), the following substitutions run over the Markdown body:

  • $ARGUMENTS → all positional args joined with spaces
  • $N → the N-th positional arg (1-based). $0 is reserved.
  • ${HARN_SKILL_DIR} → absolute path to the skill directory
  • ${HARN_SESSION_ID} → opaque session id threaded through the run
  • ${OTHER_NAME} → looks up OTHER_NAME in the process environment
  • $ → literal $

Missing positional args ($3 when only $1 was supplied) pass through unchanged so authors see what wasn't supplied rather than a silent empty substitution.

const deploy = skill_find(skills, "deploy")
guard deploy != nil else {
  harness.runtime.exit(1)
}

const rendered = skill_render(deploy, ["prod", "us-east-1"])
// rendered now has $1 and $2 replaced with "prod" and "us-east-1".
const with_session = skill_render(
  r"Session: ${HARN_SESSION_ID}",
  [],
  {session_id: "sess-123"},
)
// with_session is "Session: sess-123"
// even when `HARN_SESSION_ID` is unset.

skill_render takes an optional third argument: a session-id string or {session_id: ...}. That explicit id wins over the process environment, so in-process and headless embedders can substitute without exporting HARN_SESSION_ID. agent_loop threads the live session id through this argument when a host-supplied registry entry is rendered after lazy load_skill fails.

Progressive disclosure with load_skill#

Lazy skill loading surfaces in two places, both resolving the skill id against the active registry and applying the same substitution rules:

  • harness.agent.load_skill("deploy") is the Harn-code entry point. It hydrates the full SKILL.md, applies substitution, and returns the rendered body as a string.
  • When an agent loop receives a skill registry through skills:, Harn also exposes a runtime-owned load_skill tool (arguments { name }) for the model. That tool calls the same lazy loader and returns the rendered body in the next turn.

Builtin example:

fn main(harness: Harness) {
  const runbook = harness.agent.load_skill("deploy")
  assert(
    contains(runbook, "Deploy runbook"),
    "full body is fetched lazily",
  )
}

If the target skill has disable-model-invocation: true, the runtime tool returns a typed error instead of leaking the body. Direct Harn-code harness.agent.load_skill("name") calls are explicit and are not blocked by that flag.

Always-on catalog helper#

The recommended harness convention is:

  1. Keep a compact catalog of available skills in the system prompt.
  2. Let the model call the runtime load_skill tool (arguments { name }) only when one of those entries looks relevant.

Harn ships two pure helpers for that pattern:

const entries = skills_catalog_entries(skills)
const catalog = render_always_on_catalog(entries, 2000)

skills_catalog_entries projects the resolved registry into compact {name, description, when_to_use} cards, with description sourced from the required short: frontmatter (sorted deterministically by skill id, using <namespace>/<name> when present). render_always_on_catalog formats those cards into a stable prompt block and trims the list to the requested character budget.

Copy-pasteable example:

const catalog = render_always_on_catalog(
  skills_catalog_entries(skills), 2000,
)

const result = agent_loop(harness,
  "Help me ship this release",
  catalog,
  {
    provider: "mock",
    model: "gpt-5.4",
    loop_until_done: true,
    skills: skills,
  },
)

On a later turn the model can emit:

load_skill({ name: "deploy" })

and the next turn will see the substituted SKILL.md body in the tool result, while any allowed-tools declared by that skill narrow the tool surface for subsequent turns.

harn.toml [skills] + [[skill.source]]#

Projects that share skills across siblings or pull them from a remote tag use the manifest instead of a per-script flag:

[skills]
paths = ["packages/*/skills", "../shared-skills"]
lookup_order = ["cli", "project", "manifest", "user", "package", "system", "host"]
disable = ["system"]
signer_registry_url = "./signers"

[skills.defaults]
tool_search = "bm25"
always_loaded = ["look", "edit", "bash"]

[[skill.source]]
type = "fs"
path = "../shared"

[[skill.source]]
type = "git"
url = "https://github.com/acme/harn-skills"
tag = "v1.2.0"

[[skill.source]]
type = "registry"   # reserved, inert until a marketplace exists
url = "https://skills.harnlang.com"
name = "acme/ops"
  • paths is joined against the directory holding harn.toml and supports a single trailing * component (packages/*/skills).
  • lookup_order inverts layer priority — for example, preferring user over project on a personal checkout without touching the repo.
  • disable removes entire layers from discovery; harn doctor reports the disabled set.
  • signer_registry_url points at a flat directory or URL prefix that serves <fingerprint>.pub signer files for skill signature verification.
  • Skills that declare require_signature = true are omitted from the startup registry unless their detached signature chain verifies.
  • User and system layer skills are also omitted when they carry a failed provenance check; unsigned entries can still load, but executable hook frontmatter is only surfaced for verified skills.
  • [[skill.source]] entries of type git expect their materialized checkout under the current package generation's packages/<name>/skills/ — run harn install to populate it.
  • registry entries are accepted but inert until a Harn Skills marketplace exists.

harn doctor#

harn doctor reports the resolved skill catalog:

  OK   skills                 3 loaded (1 cli, 1 project, 1 user)
  WARN skill:deploy           shadowed by cli layer; user version at /home/me/.harn/skills/deploy is hidden
  WARN skill:review           unknown frontmatter field(s) forwarded as metadata: future_field
  SKIP skills-layer:system    layer disabled by harn.toml [skills.disable]

CLI flags#

  • harn run --skill-dir <path> (repeatable) — highest-priority layer.
  • harn test --skill-dir <path> — same semantics for user tests and conformance fixtures.
  • $HARN_SKILLS_PATH — colon-separated list of directories, applied to every invocation.

Bridge protocol#

Hosts expose their own managed skill store through three RPCs:

  • skills/list (request) — response is an array of { id, name, description, source } entries.
  • skills/fetch (request) — payload { id: "<skill id>" }; response is the full manifest + body shape so the CLI can hydrate a SkillManifestRef into a Skill.
  • skills/update (notification, host → VM) — invalidates the VM's cached catalog. The CLI re-runs discovery on the next boundary.

See Bridge protocol for wire-format details.

Managing skills#

The harn skill noun covers two complementary surfaces:

  • Canonical corpus — the Harn skills normally shipped inside the harn binary (harn-language, harn-orchestration, harn-testing, …). Access these with harn skill list, harn skill get <name>, and harn skill dump --all. Set HARN_SKILLS_DIR to a directory of recursive SKILL.md files to make list, get, and dump read from disk while iterating locally; an unset, missing, or empty directory falls back to the embedded corpus.
  • Layered FS discovery — user-authored skills picked up from --skill-dir, HARN_SKILLS_PATH, project, manifest, user, packages, system, and host layers. Access these with harn skill resolved, harn skill inspect <name>, and harn skill match. What you see there is byte-for-byte the registry that harn run / harn test / harn check hand to the VM.

Run harn skill validate <path> before committing any filesystem skill. It uses the same parser and required-field checks as those runtime commands.

harn skill list#

Lists the canonical skill corpus for this build of harn. By default that is the embedded corpus; when HARN_SKILLS_DIR points at one or more recursive SKILL.md files, the disk corpus is listed instead. Pass --json for a versioned JsonEnvelope payload that agents and CI can pipe through jq.

$ harn skill list
Embedded canonical skills (16):
  harn-agent            Agent runtime, lifecycle, capabilities, and supervision.
  harn-apps             Build Harn apps with typed views, event handlers, and model jobs.
  harn-control-events   Stop, steer, and queue as events a session must answer, not requests it…
  harn-de-slop          Remove duplicated policy, shallow seams, and weak contracts.
  harn-diagnostics      Diagnostics, the HARN-* error-code index, explain output, repair plans,…
  harn-docs             Write task-shaped developer documentation in plain language.
  harn-language         Harn syntax, modules, types, diagnostics, and script structure.
  harn-mcp              Connect Harn to MCP servers and expose Harn pipelines as MCP servers.
  harn-orchestration    Workflows, triggers, workers, handoffs, and lifecycle ownership.
  harn-probe            Evidence-driven investigation for material or unstable claims.
  harn-product-quality  Launch-quality product behavior across Harn-powered surfaces.
  harn-providers        LLM provider configuration, model routing, and provider capability beha…
  harn-rules            Structural search, lint rules, and codemods with the Harn rule engine.
  harn-testing          Deterministic, claim-driven Harn verification.
  harn-tracing          Transcripts, receipts, traces, replay, and observability surfaces.
  release-harn          Merge-queue-safe Harn patch/minor/major release workflow.

Run `harn skill get <name>` for one entry's frontmatter.
Run `harn skill get <name> --full` to include the body.

The --json output uses the standard envelope (schemaVersion, ok, data, error, warnings). The data.skills array contains the same frontmatter fields shown above:

$ harn skill list --json | jq '.data.skills | length'
13

harn skill get <name>#

Prints frontmatter for a single canonical skill. Pass --full to include the SKILL.md body; pass --json to wrap the output in a JsonEnvelope for machine consumption. HARN_SKILLS_DIR follows the same disk-override/fallback behavior as harn skill list.

$ harn skill get harn-language
name:        harn-language
short:       Harn syntax, modules, types, diagnostics, and script structure.
description: Use for Harn language syntax, typechecking, modules, imports, and idiomatic script authoring.
when_to_use: Use when writing, reviewing, or explaining Harn source code and language-level behavior.

$ harn skill get harn-language --full --json | jq '.data | keys'
[
  "body",
  "description",
  "name",
  "short",
  "when_to_use"
]

Unknown names return ok: false with a skill_not_found error code and the list of available skills in error.details.available.

harn skill dump --all [--out <dir>]#

Writes every active canonical skill to disk as a <dir>/<name>/SKILL.md tree so agents and CI can review the corpus offline. By default that means the embedded corpus; when HARN_SKILLS_DIR contains recursive SKILL.md files, dump mirrors that disk corpus byte-for-byte instead. Defaults the output directory to ./skills. Refuses to overwrite existing files unless --force is passed.

$ harn skill dump --all --out /tmp/skills
Wrote 13 skill(s) to /tmp/skills
  /tmp/skills/harn-agent/SKILL.md
  /tmp/skills/harn-diagnostics/SKILL.md
  …

harn skill resolved#

Prints every FS-resolved skill with the layer it came from. Pass --all to include shadowed entries; pass --json for newline-delimited JSON records:

$ harn skill resolved
Resolved skills (3):
  deploy         [cli]       Deploy to production when release work is requested
  review         [project]   Review a pull request when asked for code review help
  helpers/utils  [package]   Shared helpers when the task needs the acme/ops package

Shadowed skills (1):
  deploy   winner=[cli] hidden=[user] origin=/home/me/.harn/skills/deploy

harn skill inspect <name>#

Dumps the resolved SKILL.md — frontmatter, bundled files under the skill directory, and the full body — for a specific skill. Accepts bare <name> or fully-qualified <namespace>/<name>:

$ harn skill inspect deploy
id:          deploy
name:        deploy
layer:       cli
short:       Deploy to production when release work is requested
description: Deploy to production with rollback support
skill_dir:   /repo/.harn/skills/deploy

Bundled files:
  files/runbook.md
  files/rollback.sh

---- SKILL.md body ----
Run the deploy. Confirm replicas and then flip traffic.

harn skill match "<query>"#

Runs the built-in metadata matcher (same scorer the agent loop uses) against a prompt and prints the ranked candidates with their scores. Supports --working-file to simulate path-glob matches:

$ harn skill match "deploy the staging service" --top-n 3
Match results for: deploy the staging service
   1. deploy              score=2.400  [cli]       prompt mentions 'deploy'; 1 keyword hit(s)
   2. review              score=0.400  [project]   1 keyword hit(s)

Confirms that a SKILL.md's short: and when_to_use: frontmatter attract the intended prompts.

harn skill install <spec>#

Materializes a git ref or local path into .harn/skills-cache/ so the filesystem package walker picks it up on the next run. The .harn/skills-cache/ uses the same package-per-directory shape as a generation's packages/:

$ harn skill install acme/harn-skills --tag v1.2.0
installing acme/harn-skills to .harn/skills-cache/harn-skills
installed — layer=package, path=.harn/skills-cache/harn-skills

<spec> accepts:

  • A full git URL: https://github.com/acme/harn-skills.git
  • owner/repo shorthand (expands to GitHub): acme/harn-skills
  • A local filesystem path: ../shared/skills/deploy

Pass --namespace <ns> to shelf the install under a subdirectory so it shows up in the resolver as <ns>/<skill>. Pass --tag <ref> to pin a git branch or tag. Every install rewrites .harn/skills-cache/skills.lock with the resolved source + commit.

harn skill new <name>#

Scaffolds a new SKILL.md and files/ directory under .harn/skills/:

$ harn skill new deploy --description "Deploy to production"
Scaffolded skill 'deploy' at .harn/skills/deploy
  SKILL.md
  files/README.md

Edit the SKILL.md frontmatter and body, then run `harn skill resolved`
after `harn skill validate .harn/skills/deploy` succeeds.

harn skill validate [<path>]#

Validates one skill directory or SKILL.md file through Harn's canonical parser. The path defaults to the current directory. A valid skill must have well-formed YAML frontmatter, a non-empty short card, and a name supplied by either frontmatter or the containing directory.

$ harn skill validate .harn/skills/deploy
valid: .harn/skills/deploy
id:    deploy
short: Deploy the application when the user asks for a release
files: 2 bundled file(s)

Unknown frontmatter fields are warnings by default so a newer skill format can still load on an older Harn release. Use --strict in CI to reject them. Use --json for the versioned envelope listed by harn --json-schemas:

harn skill validate .harn/skills/deploy --strict --json \
  | jq -e '.ok and .data.id == "deploy"'

Detached signing, endorsement, verification, and local signer trust live under the same harn skill command. See Skill provenance for the complete workflow.

Pass --dir <path> to target a different destination (for example ~/.harn/skills/deploy to scaffold under the user layer instead of the project layer), or --force to overwrite an existing directory.

Portal observability#

The Harn portal (harn portal) surfaces two skill-focused panels on every run detail page:

  • Skill timeline — horizontal bars showing which skills activated on which agent-loop iteration and when they deactivated. Hover a bar for the matcher score and the reason the skill was promoted.
  • Tool-load waterfall — one row per tool_search_query event, pairing each query with its tool_search_result so you can see which deferred tools entered the LLM's context in each turn.
  • Matcher decisions — per-iteration expansions showing every candidate the matcher considered, its score, and the working-file snapshot it scored against.

The runs index page takes a skill=<name> filter so you can narrow evals to runs where a specific skill was active. The same skill=<name> query parameter works from a URL, making it easy to link to "every run that used deploy".

\n\nMissing positional args (`$3` when only `$1` was supplied) **pass\nthrough unchanged** so authors see what wasn't supplied rather than a\nsilent empty substitution.\n\n```harn\nconst deploy = skill_find(skills, \"deploy\")\nguard deploy != nil else {\n harness.runtime.exit(1)\n}\n\nconst rendered = skill_render(deploy, [\"prod\", \"us-east-1\"])\n// rendered now has $1 and $2 replaced with \"prod\" and \"us-east-1\".\nconst with_session = skill_render(\n r\"Session: ${HARN_SESSION_ID}\",\n [],\n {session_id: \"sess-123\"},\n)\n// with_session is \"Session: sess-123\"\n// even when `HARN_SESSION_ID` is unset.\n```\n\n`skill_render` takes an optional third argument: a session-id string or\n`{session_id: ...}`. That explicit id wins over the process environment, so\nin-process and headless embedders can substitute without exporting\n`HARN_SESSION_ID`. `agent_loop` threads the live session id through this\nargument when a host-supplied registry entry is rendered after lazy\n`load_skill` fails.\n\n## Progressive disclosure with `load_skill`\n\nLazy skill loading surfaces in two places, both resolving the skill id\nagainst the active registry and applying the same substitution rules:\n\n- `harness.agent.load_skill(\"deploy\")` is the Harn-code entry point. It\n hydrates the full `SKILL.md`, applies substitution, and returns the\n rendered body as a string.\n- When an agent loop receives a skill registry through `skills:`, Harn\n also exposes a runtime-owned `load_skill` tool (arguments\n `{ name }`) for the model. That tool calls the same lazy loader and\n returns the rendered body in the next turn.\n\nBuiltin example:\n\n```harn\nfn main(harness: Harness) {\n const runbook = harness.agent.load_skill(\"deploy\")\n assert(\n contains(runbook, \"Deploy runbook\"),\n \"full body is fetched lazily\",\n )\n}\n```\n\nIf the target skill has `disable-model-invocation: true`, the runtime\ntool returns a typed error instead of leaking the body. Direct Harn-code\n`harness.agent.load_skill(\"name\")` calls are explicit and are not blocked by\nthat flag.\n\n### Always-on catalog helper\n\nThe recommended harness convention is:\n\n1. Keep a compact catalog of available skills in the system prompt.\n2. Let the model call the runtime `load_skill` tool (arguments\n `{ name }`) only when one of those entries looks relevant.\n\nHarn ships two pure helpers for that pattern:\n\n```harn\nconst entries = skills_catalog_entries(skills)\nconst catalog = render_always_on_catalog(entries, 2000)\n```\n\n`skills_catalog_entries` projects the resolved registry into compact\n`{name, description, when_to_use}` cards, with `description` sourced\nfrom the required `short:` frontmatter (sorted deterministically by\nskill id, using `\u003cnamespace>/\u003cname>` when present).\n`render_always_on_catalog` formats those cards into a stable prompt\nblock and trims the list to the requested character budget.\n\nCopy-pasteable example:\n\n```harn\nconst catalog = render_always_on_catalog(\n skills_catalog_entries(skills), 2000,\n)\n\nconst result = agent_loop(harness,\n \"Help me ship this release\",\n catalog,\n {\n provider: \"mock\",\n model: \"gpt-5.4\",\n loop_until_done: true,\n skills: skills,\n },\n)\n```\n\nOn a later turn the model can emit:\n\n```text\nload_skill({ name: \"deploy\" })\n```\n\nand the next turn will see the substituted SKILL.md body in the tool\nresult, while any `allowed-tools` declared by that skill narrow the\ntool surface for subsequent turns.\n\n## harn.toml `[skills]` + `[[skill.source]]`\n\nProjects that share skills across siblings or pull them from a remote\ntag use the manifest instead of a per-script flag:\n\n```toml\n[skills]\npaths = [\"packages/*/skills\", \"../shared-skills\"]\nlookup_order = [\"cli\", \"project\", \"manifest\", \"user\", \"package\", \"system\", \"host\"]\ndisable = [\"system\"]\nsigner_registry_url = \"./signers\"\n\n[skills.defaults]\ntool_search = \"bm25\"\nalways_loaded = [\"look\", \"edit\", \"bash\"]\n\n[[skill.source]]\ntype = \"fs\"\npath = \"../shared\"\n\n[[skill.source]]\ntype = \"git\"\nurl = \"https://github.com/acme/harn-skills\"\ntag = \"v1.2.0\"\n\n[[skill.source]]\ntype = \"registry\" # reserved, inert until a marketplace exists\nurl = \"https://skills.harnlang.com\"\nname = \"acme/ops\"\n```\n\n- `paths` is joined against the directory holding harn.toml and\n supports a single trailing `*` component (`packages/*/skills`).\n- `lookup_order` inverts layer priority — for example, preferring\n `user` over `project` on a personal checkout without touching the\n repo.\n- `disable` removes entire layers from discovery; `harn doctor` reports\n the disabled set.\n- `signer_registry_url` points at a flat directory or URL prefix that\n serves `\u003cfingerprint>.pub` signer files for skill signature\n verification.\n- Skills that declare `require_signature = true` are omitted from the\n startup registry unless their detached signature chain verifies.\n- User and system layer skills are also omitted when they carry a failed\n provenance check; unsigned entries can still load, but executable hook\n frontmatter is only surfaced for verified skills.\n- `[[skill.source]]` entries of type `git` expect their materialized checkout\n under the current package generation's `packages/\u003cname>/skills/` — run\n `harn install` to populate it.\n- `registry` entries are accepted but inert until a Harn Skills\n marketplace exists.\n\n## harn doctor\n\n`harn doctor` reports the resolved skill catalog:\n\n```text\n OK skills 3 loaded (1 cli, 1 project, 1 user)\n WARN skill:deploy shadowed by cli layer; user version at /home/me/.harn/skills/deploy is hidden\n WARN skill:review unknown frontmatter field(s) forwarded as metadata: future_field\n SKIP skills-layer:system layer disabled by harn.toml [skills.disable]\n```\n\n## CLI flags\n\n- `harn run --skill-dir \u003cpath>` (repeatable) — highest-priority layer.\n- `harn test --skill-dir \u003cpath>` — same semantics for user tests and\n conformance fixtures.\n- `$HARN_SKILLS_PATH` — colon-separated list of directories, applied\n to every invocation.\n\n## Bridge protocol\n\nHosts expose their own managed skill store through three RPCs:\n\n- `skills/list` (request) — response is an array of\n `{ id, name, description, source }` entries.\n- `skills/fetch` (request) — payload `{ id: \"\u003cskill id>\" }`; response\n is the full manifest + body shape so the CLI can hydrate a\n `SkillManifestRef` into a `Skill`.\n- `skills/update` (notification, host → VM) — invalidates the VM's\n cached catalog. The CLI re-runs discovery on the next boundary.\n\nSee [Bridge protocol](./bridge-protocol.md) for wire-format details.\n\n## Managing skills\n\nThe `harn skill` noun covers two complementary surfaces:\n\n- **Canonical corpus** — the Harn skills normally shipped *inside* the\n `harn` binary (`harn-language`, `harn-orchestration`, `harn-testing`,\n …). Access these with `harn skill list`, `harn skill get \u003cname>`,\n and `harn skill dump --all`. Set `HARN_SKILLS_DIR` to a directory of\n recursive `SKILL.md` files to make `list`, `get`, and `dump` read\n from disk while iterating locally; an unset, missing, or empty\n directory falls back to the embedded corpus.\n- **Layered FS discovery** — user-authored skills picked up from\n `--skill-dir`, `HARN_SKILLS_PATH`, project, manifest, user, packages,\n system, and host layers. Access these with `harn skill resolved`,\n `harn skill inspect \u003cname>`, and `harn skill match`. What you see\n there is byte-for-byte the registry that `harn run` / `harn test` /\n `harn check` hand to the VM.\n\nRun `harn skill validate \u003cpath>` before committing any filesystem skill.\nIt uses the same parser and required-field checks as those runtime commands.\n\n### `harn skill list`\n\nLists the canonical skill corpus for this build of `harn`. By default\nthat is the embedded corpus; when `HARN_SKILLS_DIR` points at one or\nmore recursive `SKILL.md` files, the disk corpus is listed instead.\nPass `--json` for a versioned `JsonEnvelope` payload that agents and\nCI can pipe through `jq`.\n\n```text\n$ harn skill list\nEmbedded canonical skills (16):\n harn-agent Agent runtime, lifecycle, capabilities, and supervision.\n harn-apps Build Harn apps with typed views, event handlers, and model jobs.\n harn-control-events Stop, steer, and queue as events a session must answer, not requests it…\n harn-de-slop Remove duplicated policy, shallow seams, and weak contracts.\n harn-diagnostics Diagnostics, the HARN-* error-code index, explain output, repair plans,…\n harn-docs Write task-shaped developer documentation in plain language.\n harn-language Harn syntax, modules, types, diagnostics, and script structure.\n harn-mcp Connect Harn to MCP servers and expose Harn pipelines as MCP servers.\n harn-orchestration Workflows, triggers, workers, handoffs, and lifecycle ownership.\n harn-probe Evidence-driven investigation for material or unstable claims.\n harn-product-quality Launch-quality product behavior across Harn-powered surfaces.\n harn-providers LLM provider configuration, model routing, and provider capability beha…\n harn-rules Structural search, lint rules, and codemods with the Harn rule engine.\n harn-testing Deterministic, claim-driven Harn verification.\n harn-tracing Transcripts, receipts, traces, replay, and observability surfaces.\n release-harn Merge-queue-safe Harn patch/minor/major release workflow.\n\nRun `harn skill get \u003cname>` for one entry's frontmatter.\nRun `harn skill get \u003cname> --full` to include the body.\n```\n\nThe `--json` output uses the standard envelope (`schemaVersion`, `ok`,\n`data`, `error`, `warnings`). The `data.skills` array contains the\nsame frontmatter fields shown above:\n\n```bash\n$ harn skill list --json | jq '.data.skills | length'\n13\n```\n\n### `harn skill get \u003cname>`\n\nPrints frontmatter for a single canonical skill. Pass `--full` to\ninclude the SKILL.md body; pass `--json` to wrap the output in a\n`JsonEnvelope` for machine consumption. `HARN_SKILLS_DIR` follows the\nsame disk-override/fallback behavior as `harn skill list`.\n\n```text\n$ harn skill get harn-language\nname: harn-language\nshort: Harn syntax, modules, types, diagnostics, and script structure.\ndescription: Use for Harn language syntax, typechecking, modules, imports, and idiomatic script authoring.\nwhen_to_use: Use when writing, reviewing, or explaining Harn source code and language-level behavior.\n\n$ harn skill get harn-language --full --json | jq '.data | keys'\n[\n \"body\",\n \"description\",\n \"name\",\n \"short\",\n \"when_to_use\"\n]\n```\n\nUnknown names return `ok: false` with a `skill_not_found` error code\nand the list of available skills in `error.details.available`.\n\n### `harn skill dump --all [--out \u003cdir>]`\n\nWrites every active canonical skill to disk as a `\u003cdir>/\u003cname>/SKILL.md`\ntree so agents and CI can review the corpus offline. By default that\nmeans the embedded corpus; when `HARN_SKILLS_DIR` contains recursive\n`SKILL.md` files, `dump` mirrors that disk corpus byte-for-byte instead.\nDefaults the output directory to `./skills`. Refuses to overwrite\nexisting files unless `--force` is passed.\n\n```text\n$ harn skill dump --all --out /tmp/skills\nWrote 13 skill(s) to /tmp/skills\n /tmp/skills/harn-agent/SKILL.md\n /tmp/skills/harn-diagnostics/SKILL.md\n …\n```\n\n### `harn skill resolved`\n\nPrints every FS-resolved skill with the layer it came from. Pass\n`--all` to include shadowed entries; pass `--json` for newline-delimited\nJSON records:\n\n```text\n$ harn skill resolved\nResolved skills (3):\n deploy [cli] Deploy to production when release work is requested\n review [project] Review a pull request when asked for code review help\n helpers/utils [package] Shared helpers when the task needs the acme/ops package\n\nShadowed skills (1):\n deploy winner=[cli] hidden=[user] origin=/home/me/.harn/skills/deploy\n```\n\n### `harn skill inspect \u003cname>`\n\nDumps the resolved SKILL.md — frontmatter, bundled files under the\nskill directory, and the full body — for a specific skill. Accepts\nbare `\u003cname>` or fully-qualified `\u003cnamespace>/\u003cname>`:\n\n```text\n$ harn skill inspect deploy\nid: deploy\nname: deploy\nlayer: cli\nshort: Deploy to production when release work is requested\ndescription: Deploy to production with rollback support\nskill_dir: /repo/.harn/skills/deploy\n\nBundled files:\n files/runbook.md\n files/rollback.sh\n\n---- SKILL.md body ----\nRun the deploy. Confirm replicas and then flip traffic.\n```\n\n### `harn skill match \"\u003cquery>\"`\n\nRuns the built-in metadata matcher (same scorer the agent loop uses)\nagainst a prompt and prints the ranked candidates with their scores.\nSupports `--working-file` to simulate path-glob matches:\n\n```text\n$ harn skill match \"deploy the staging service\" --top-n 3\nMatch results for: deploy the staging service\n 1. deploy score=2.400 [cli] prompt mentions 'deploy'; 1 keyword hit(s)\n 2. review score=0.400 [project] 1 keyword hit(s)\n```\n\nConfirms that a SKILL.md's `short:` and `when_to_use:` frontmatter\nattract the intended prompts.\n\n### `harn skill install \u003cspec>`\n\nMaterializes a git ref or local path into `.harn/skills-cache/` so\nthe filesystem package walker picks it up on the next run. The\n`.harn/skills-cache/` uses the same package-per-directory shape as a generation's `packages/`:\n\n```text\n$ harn skill install acme/harn-skills --tag v1.2.0\ninstalling acme/harn-skills to .harn/skills-cache/harn-skills\ninstalled — layer=package, path=.harn/skills-cache/harn-skills\n```\n\n`\u003cspec>` accepts:\n\n- A full git URL: `https://github.com/acme/harn-skills.git`\n- `owner/repo` shorthand (expands to GitHub): `acme/harn-skills`\n- A local filesystem path: `../shared/skills/deploy`\n\nPass `--namespace \u003cns>` to shelf the install under a subdirectory so\nit shows up in the resolver as `\u003cns>/\u003cskill>`. Pass `--tag \u003cref>` to\npin a git branch or tag. Every install rewrites\n`.harn/skills-cache/skills.lock` with the resolved source + commit.\n\n### `harn skill new \u003cname>`\n\nScaffolds a new SKILL.md and `files/` directory under `.harn/skills/`:\n\n```text\n$ harn skill new deploy --description \"Deploy to production\"\nScaffolded skill 'deploy' at .harn/skills/deploy\n SKILL.md\n files/README.md\n\nEdit the SKILL.md frontmatter and body, then run `harn skill resolved`\nafter `harn skill validate .harn/skills/deploy` succeeds.\n```\n\n### `harn skill validate [\u003cpath>]`\n\nValidates one skill directory or `SKILL.md` file through Harn's canonical\nparser. The path defaults to the current directory. A valid skill must have\nwell-formed YAML frontmatter, a non-empty `short` card, and a name supplied by\neither frontmatter or the containing directory.\n\n```text\n$ harn skill validate .harn/skills/deploy\nvalid: .harn/skills/deploy\nid: deploy\nshort: Deploy the application when the user asks for a release\nfiles: 2 bundled file(s)\n```\n\nUnknown frontmatter fields are warnings by default so a newer skill format can\nstill load on an older Harn release. Use `--strict` in CI to reject them. Use\n`--json` for the versioned envelope listed by `harn --json-schemas`:\n\n```bash\nharn skill validate .harn/skills/deploy --strict --json \\\n | jq -e '.ok and .data.id == \"deploy\"'\n```\n\nDetached signing, endorsement, verification, and local signer trust live under\nthe same `harn skill` command. See [Skill provenance](./skill-provenance.md) for\nthe complete workflow.\n\nPass `--dir \u003cpath>` to target a different destination (for example\n`~/.harn/skills/deploy` to scaffold under the user layer instead of\nthe project layer), or `--force` to overwrite an existing directory.\n\n## Portal observability\n\nThe Harn portal (`harn portal`) surfaces two skill-focused panels on\nevery run detail page:\n\n- **Skill timeline** — horizontal bars showing which skills activated\n on which agent-loop iteration and when they deactivated. Hover a\n bar for the matcher score and the reason the skill was promoted.\n- **Tool-load waterfall** — one row per `tool_search_query` event,\n pairing each query with its `tool_search_result` so you can see\n which deferred tools entered the LLM's context in each turn.\n- **Matcher decisions** — per-iteration expansions showing every\n candidate the matcher considered, its score, and the working-file\n snapshot it scored against.\n\nThe runs index page takes a `skill=\u003cname>` filter so you can narrow\nevals to runs where a specific skill was active. The same\n`skill=\u003cname>` query parameter works from a URL, making it easy to\nlink to \"every run that used `deploy`\".\n"}