Standard: skill
The agent skill-bundle contract for every CLI in the shll toolkit. Each tool exposes a <tool> skill subcommand that prints a stable, one-page markdown skill bundle for the agent using the tool — embedded in the binary, versioned with it, byte-identical to the tool repo’s canonical docs/site/skill.md. It closes a real gap: nothing else serves an agent operating an installed tool from any repo, offline.
This page is the producer-facing standard: what your tool’s skill bundle must be and how the subcommand must behave. It is a sibling of the help-dump standard — where help-dump serves the structure of the command tree, skill serves the usage knowledge an agent needs to wield the tool well. Together with readme-extraction they implement principles №3 and №10 of the toolkit CLI principles. Scope: binary + repo (the subcommand ships in the binary; the canonical bundle lives in the repo).
The gap it fills
Three existing surfaces each fall short for an agent that just wants to use an installed tool:
-h/help-dumpis flag reference — the shape of every command, not when to reach for which, or how the tool composes.- README /
docs/siteneeds the repo checked out or a network round-trip to shll.ai. fab/projectcontext is repo-development-scoped — it orients a contributor, not a caller.
A <tool> skill bundle is offline (embedded), present on every machine that has the tool, and version-locked by construction: the prose ships inside the same binary as the flags it describes, so it can never document a capability the installed binary lacks.
Precedent: run-kit context
The toolkit’s prior art is run-kit context (a.k.a. rk context) — roughly 102 lines of agent-optimized markdown that a harness loads to learn what run-kit can do. It proves the shape works. One nuance the skill genre draws a line on: run-kit context mixes static capability prose with a small dynamic Environment header (current session, pane, server URL) computed at invocation. The skill bundle is static-only — embedded, byte-identical across invocations, drift-guarded. Dynamic, environment-derived state stays in separate commands like run-kit context; a skill bundle never varies with where or when it runs.
Invocation contract
<tool> skill is uniform across every tool that adopts it:
- The command name is exactly
skill— notagent, notcontext(see Name rationale). - Prints the bundle as raw markdown to stdout, byte-identical to the repo’s canonical
docs/site/skill.md. - stderr is empty on success, and the exit code is 0.
- No rendering, no pager, no added framing — the agent consumes the bytes directly (principle №2: stdout is data).
Content: what belongs in the bundle
The bundle is a usage briefing, not a second README and not flag reference. Cover, in agent-first language:
- When to use — the situations this tool is the right reach for, and when it isn’t.
- Capabilities map — the handful of things the tool does, one line each, keyed to the subcommand that does it.
- Composition patterns — how the tool plays with the rest of the toolkit (what it shells out to, what shells out to it) per principle №7.
- Output & exit-code contracts — stdout-vs-stderr split,
--jsonavailability, the exit-code convention (0/1/2) a caller branches on. - Gotchas — the non-obvious traps an agent hits on first use.
Explicitly out of the bundle: exhaustive flag tables (defer to -h), full command trees (defer to help-dump and the shll.ai commands page), and installation prose (that is README / docs/site/install.md).
Rules with teeth
- Static only. The bytes are identical on every invocation, on every machine, for a given release. No timestamps, no environment lookups, no session state (contrast
run-kit context). - Bounded — ≤150 lines. A hard budget, per principle №9. Agents pull a bundle into context at use time via
shll skill <tool>(see Landed design), and the bareshll skillglossary lists one line per installed tool; a bloated bundle taxes every conversation that pulls it. If it doesn’t fit in 150 lines, it is trying to be a README — or it is a large-scope tool whose depth belongs in topic pages, never in a bigger core. - Byte-identical to the canonical file.
<tool> skillstdout MUST equaldocs/site/skill.mdbyte-for-byte. The content is embedded at build time via a sync + drift-guard pattern — committed embedded copies, a sync script that refreshes them from the canonicaldocs/site/source, and a drift-guard test that fails the build when they diverge. This is the exact mechanismshll standardsestablished for the standards documents; reuse it. - Renders on the site for free. Because
docs/site/skill.mdis part of the pulleddocs/site/**tree, the bundle also renders at/<tool>/skillon shll.ai automatically — the same page an agent reads offline via<tool> skill.
Topic pages (large-scope tools)
The ≤150-line budget prices the use-time pull — each shll skill <tool> serves exactly one core bundle (see Landed design) — so it deliberately does not scale with tool size. A tool whose usage knowledge genuinely exceeds one page — run-kit and fab-kit are the expected cases — does not get a bigger budget; it splits depth into topic pages:
<tool> skill <topic>prints one topic page (e.g.rk skill windows,fab skill dispatch) under the same invocation contract: raw markdown to stdout, stderr empty on success, exit 0.- Each topic page is canonical at
docs/site/skill/<topic>.mdand independently bounded at ≤150 lines, with the same rules with teeth — static-only, byte-identical to its canonical file, embedded via the sync + drift-guard pattern. (The core staysdocs/site/skill.md; the file and theskill/directory coexist, and each topic renders at/<tool>/skill/<topic>on shll.ai as part of the pulled tree.) - The core bundle carries a topic index — one line per topic naming what it covers and the command that serves it — so depth is discovered from the core and pulled at use time by the agent that needs it.
- Bare
<tool> skillnever inlines topic pages, and the runtime two-step (shll skill <tool>) serves core bundles only, never topic pages — a tool’s ambient context cost stays ≤150 lines no matter how many topics it ships. - An unknown topic fails fast: non-zero exit, an error on stderr naming the valid topics — never a silent empty stdout.
- Sprawl guard. Topics carry depth a caller reaches for deliberately — a subsystem’s contract, a composition recipe — not a mirror of the command tree. A tool SHOULD ship a handful of topics at most; a topic page is still a briefing, and flag reference still defers to
-h/help-dump.
Name rationale
The subcommand is skill, deliberately not agent. agent was rejected: it collides with fab agent (which launches an agent session), it reads as an imperative (“run an agent”) rather than “the tool’s skill bundle”, and run-kit’s agent-* family already means harness wiring. skill is collision-free across all seven command trees and is the anc.dev P8 vocabulary — SKILL.md skill bundles — that agents already recognize.
Adoption
Phased, per-repo — like help-dump’s rollout was. This standard is the contract each repo conforms to as it adopts, on its own release cadence (no seven-repo flag-day). A tool that has not yet adopted is not in violation — principle №10 is a SHOULD, and the bundle is its most forward-leaning obligation. Topic pages are equally per-tool: a tool adds them when its bundle presses the budget, and a tool shipping only a core bundle is fully conformant.
Landed design: shll setup agent
shll setup agent wires a machine’s agent harnesses to the toolkit, graduating that responsibility up from run-kit agent setup. It ships today (renamed from the hidden-deprecated shll agent-setup), and it is recorded here because it is why bundles must stay small and static. It landed as skills placement plus a runtime two-step, not as context aggregation:
- Skills placement, not context aggregation.
shll setup agentplaces one thin bootstrap Agent Skill (shll-toolkit) into the harnesses’ global skills directories (~/.agents/skills/and~/.claude/skills/). The skill’s description is roster-driven — it front-loads the tool names as trigger words so the skill activates when an agent is about to reach for a toolkit tool — and its body teaches the runtime two-step below. Aggregating every tool’s bundle into the agent’s context, and placing per-tool bundles as their own skill files, were both rejected: placed copies go stale between updates, and per-tool skills multiply listing lines. - The runtime two-step. Bare
shll skillprints an installed-only glossary — one line per tool.shll skill <tool>then streams that tool’s core bundle on demand, byte-identical from the installed binary, so bundle content stays version-locked by construction and is fetched only when an agent actually needs it. - Hook-wiring delegation.
shll setup agentdelegates run-kit’s dashboard-hook wiring torun-kit agent setup, which is hook-only — its context-injection responsibility was removed as designed, leaving it to do only hook wiring.
The mechanism changed from the original sketch, but the budget and static-only motive survives it: every shll skill <tool> call pulls the core bundle into a paying context, and the glossary lists every installed tool — so a bloated bundle still taxes every conversation that pulls it, which is the whole reason for the static-only rule and the ≤150-line budget above.
Verifying conformance
Before shipping a change that touches your tool’s skill bundle:
<tool> skillexits 0, writes the bundle to stdout only, stderr empty.- stdout is byte-identical to the repo’s canonical
docs/site/skill.md(a drift-guard test pins this). - The bundle is ≤150 lines and carries no dynamic, environment-derived content.
- The bundle stays in genre — usage briefing, not a README clone or a flag table.
docs/site/skill.mdrenders at/<tool>/skillon shll.ai (it is part of the pulled tree).- If the tool ships topic pages: each
<tool> skill <topic>meets the same contract (stdout-only, static, ≤150 lines, byte-identical todocs/site/skill/<topic>.md), the core’s topic index lists every shipped topic, and an unknown topic exits non-zero with the valid topics on stderr.