Skills Overview
Purpose
Explain what Clawperator skills are, how the registry model works, how skills are discovered, and what the clawperator skills wrapper actually does.
For the post-install decision of when to start with clawperator skills
instead of MCP or direct CLI automation, read
Host Agent Orientation first. This page is the detailed
runtime-skills contract.
Sources
- Registry contract:
apps/node/src/contracts/skills.ts - Registry loading:
apps/node/src/adapters/skills-repo/localSkillsRegistry.ts - Runtime wrapper:
apps/node/src/domain/skills/runSkill.ts - Listing and search:
apps/node/src/domain/skills/listSkills.ts,apps/node/src/domain/skills/searchSkills.ts - CLI surface:
apps/node/src/cli/commands/skills.ts,apps/node/src/cli/registry.ts - Installer outputs:
install.sh - Serve API wrapper:
apps/node/src/cli/commands/serve.ts
What Skills Are
Skills are deterministic wrappers around repeatable workflows.
Current role split:
- Clawperator is the execution substrate
- a skill defines a reusable wrapper or artifact
- the agent decides when to invoke the skill and how to interpret the result
Skills are registry-driven. They are not discovered by folder scanning alone. clawperator skills list, clawperator skills for-app, clawperator skills search, clawperator skills get, clawperator skills validate, and clawperator skills run all read the registry through loadRegistry() in apps/node/src/adapters/skills-repo/localSkillsRegistry.ts.
clawperator skills and skills-registry.json cover runtime skills only.
Authoring skills are a separate category of AI agent programs that live in
.agents/skills/ in source form and install separately into
~/.clawperator/agent-skills/ plus the Claude Code, Codex, and generic
agents (~/.agents/skills/) discovery directories.
Installer-facing discovery is deliberately split:
~/.clawperator/AGENTS.mdis the installer-written local guide for runtime skills- if
~/.agents/AGENTS.mdalready exists, the installer appends one bounded Clawperator bridge there that points back to~/.clawperator/AGENTS.mdand theclawperator skillsdiscovery commands - the installer does not mirror runtime skills into shared agent skill directories such as
~/.agents/skills/,~/.claude/skills/, or~/.codex/skills/
Skill Categories
Current authoring practice recognizes two categories of skills:
-replayskills:- recording-derived or replay-oriented wrappers
- optimized for deterministic path execution on a known UI flow
- may rely on tighter device or layout assumptions
-orchestratedskills:- agent-controlled skills intended to better match the Clawperator brain/hand model
- may declare an
agentblock inskill.json - run through their
scripts/run.jsharness, which spawns the configured agent CLI - can emit structured
SkillResultframes with checkpoints and terminal verification thatrunSkillparses and returns
Important current caveats:
- the
-replay/-orchestratedsuffix split is still primarily a naming and authoring convention, not a dedicated registry enum - runtime behavior for orchestrated skills is currently driven by the presence of
skill.json.agent, not by suffix inspection alone - the currently supported orchestrated runtime path uses
codexas the agent CLI - some orchestrated harnesses currently run codex with
danger-full-accessso the runtime agent can reach live adb targets, but that is a harness-specific choice rather than a Node runtime guarantee - some legacy skills predate the suffix convention and may still have unsuffixed ids
- unsuffixed legacy ids should not be read as proof that a skill is already orchestrated
Skill Structure
The registry contract for one skill is:
{
"id": "com.android.settings.capture-overview",
"applicationId": "com.android.settings",
"intent": "capture-overview",
"summary": "Capture a Settings overview snapshot",
"path": "skills/com.android.settings.capture-overview",
"skillFile": "skills/com.android.settings.capture-overview/SKILL.md",
"scripts": [
"skills/com.android.settings.capture-overview/scripts/run.js",
"skills/com.android.settings.capture-overview/scripts/run.sh"
],
"artifacts": [
"skills/com.android.settings.capture-overview/artifacts/overview.recipe.json"
]
}
Meaning of the fields:
| Field | Meaning |
|---|---|
id |
canonical registry id |
applicationId |
app package the skill is primarily associated with |
intent |
short intent name derived from the id |
summary |
one-line description |
path |
skill directory relative to the skills repo root |
skillFile |
SKILL.md path |
scripts |
runnable script paths |
artifacts |
deterministic recipe payload files |
Orchestrated skills may also include an agent block in skill.json:
{
"agent": {
"cli": "codex",
"timeoutMs": 300000
}
}
Current behavior:
runSkill()detects agent-driven orchestrated skills fromskill.json.agentrunSkill()validates agent CLI availability before spawn and returnsSKILL_AGENT_CLI_UNAVAILABLEwhen it is missingrunSkill()executes the skill'sscripts/run.jsharness- the harness is responsible for spawning the configured agent CLI on
SKILL.md - framed
SkillResultoutput must omitsource;runSkill()injects trusted source metadata fromskill.json.agent
Orchestrated Runtime Contract
An orchestrated skill is an agent-driven runtime shape with these durable rules:
skill.json.agentis the trusted runtime metadata. It names the agent CLI and timeout policy thatrunSkill()enforces.- registry parity validation does not police
skill.json.agent. The registry covers distributable skill identity and file layout, whileskill.json.agentremains the trusted runtime execution config thatrunSkill()reads directly. SKILL.mdis the skill authority. It contains the app-specific runtime program, navigation policy, checkpoints, and terminal verification expectations.scripts/run.jsis a thin harness. It reads the injected Clawperator env vars, spawns the configured agent CLI onSKILL.md, and forwards stdout and stderr.- the harness must not absorb the real skill logic. If app-specific decision policy, navigation authority, or terminal verification rules migrate into the harness, the skill has left this contract.
runSkill()remains the Clawperator-owned boundary. It validates the skill, injects runtime env vars, executes the harness, parses the framed result, and injects trustedsourcemetadata.- orchestrated output is contract-bound. The runtime agent must emit exactly one terminal
[Clawperator-Skill-Result]frame with a validSkillResultobject. - replay skills remain first-class. Orchestrated skills are an additional runtime shape, not a replacement for replay-driven skills.
For the practical authoring rules that keep orchestrated skills debuggable and truthful in real device runs, see Authoring.
Current implementation notes:
- the currently supported orchestrated runtime path uses
codexas the agent CLI - some orchestrated harnesses currently run codex with
danger-full-accessso the runtime agent can reach live adb targets, but that is a harness-specific choice rather than a Node runtime guarantee
Registry
The registry file is a JSON object with:
{
"schemaVersion": "optional string",
"generatedAt": "optional string",
"skills": [
{
"id": "com.android.settings.capture-overview",
"applicationId": "com.android.settings",
"intent": "capture-overview",
"summary": "Capture a Settings overview snapshot",
"path": "skills/com.android.settings.capture-overview",
"skillFile": "skills/com.android.settings.capture-overview/SKILL.md",
"scripts": [
"skills/com.android.settings.capture-overview/scripts/run.js"
],
"artifacts": []
}
]
}
Registry resolution precedence is:
- explicit
registryPathargument, when a caller supplied one CLAWPERATOR_SKILLS_REGISTRY, when it is set and non-blank- default path
skills/skills-registry.jsonrelative to the current working directory
Current failure and fallback rules:
- if an explicit
registryPathargument is passed and that path cannot be read,loadRegistry()fails immediately and does not fall back - if
CLAWPERATOR_SKILLS_REGISTRYis set but blank,loadRegistry()fails immediately and does not fall back - if
CLAWPERATOR_SKILLS_REGISTRYis set to a non-blank path and that read fails,loadRegistry()fails immediately and does not fall back - if neither an explicit path nor env var is active and the default-path read fails,
loadRegistry()next tries: ../../skills/skills-registry.jsonrelative to the current working directory when running fromapps/node~/.clawperator/skills/skills/skills-registry.json
The install and sync flow writes the canonical long-lived registry under:
~/.clawperator/skills/skills/skills-registry.json
That path is assembled from these literals in apps/node/src/domain/skills/skillsConfig.ts:
DEFAULT_SKILLS_DIR = ~/.clawperator/skillsDEFAULT_SKILLS_REGISTRY_SUBPATH = skills/skills-registry.jsonSKILLS_REPO_URL = https://github.com/clawperator/clawperator-skills
Registry Verification
Use skills list --json to confirm that the registry path in your current shell is readable:
- after
install.sh, this works in a fresh non-login shell becauseloadRegistry()falls back to~/.clawperator/skills/skills/skills-registry.jsonwhen no explicit registry path or env var is active
clawperator skills list --json
Success means the registry was loaded and the skills array was parsed:
{
"skills": [
{
"id": "com.android.settings.capture-overview",
"applicationId": "com.android.settings",
"intent": "capture-overview",
"summary": "Capture a Settings overview snapshot",
"path": "skills/com.android.settings.capture-overview",
"skillFile": "skills/com.android.settings.capture-overview/SKILL.md",
"scripts": [
"skills/com.android.settings.capture-overview/scripts/run.js"
],
"artifacts": []
}
],
"count": 1
}
If the registry cannot be read, every discovery command fails with REGISTRY_READ_FAILED:
{
"code": "REGISTRY_READ_FAILED",
"message": "Registry not found at configured path: /tmp/missing-registry.json. Update CLAWPERATOR_SKILLS_REGISTRY or run clawperator skills install."
}
Recovery depends on how the path was chosen:
- when
CLAWPERATOR_SKILLS_REGISTRYpoints at a missing file, update the env var or runclawperator skills install - when no env var is set and neither the current working directory nor
~/.clawperator/skills/skills/skills-registry.jsoncontains the registry, verify~/.clawperator/skills/skills/skills-registry.json, runclawperator skills list --json, then runclawperator skills installor setCLAWPERATOR_SKILLS_REGISTRY - when the registry file exists but does not contain a
skillsarray, fix the JSON becauseloadRegistry()rejects that shape withInvalid registry: skills array required
Wrapper failure fields like stdout and stderr are optional. runSkill.ts includes them only when the child process actually emitted non-empty data on those streams.
Discovery
If you are starting from a fresh install, the shortest discovery flow is:
clawperator skills for-app <package_id> --json
clawperator skills search --keyword <text> --json
clawperator skills get <skill_id> --json
clawperator skills run <skill_id> --json
Start with skills for-app when you know the Android package id. Use
skills search --keyword when you only have app names or user-language intent
terms.
Current discovery commands:
clawperator skills list
clawperator skills for-app <package_id>
clawperator skills search --app <package_id>
clawperator skills search --intent <intent>
clawperator skills search --keyword <text>
clawperator skills get <skill_id>
skills list
cmdSkillsList() returns the raw registry entries plus a computed count:
{
"skills": [
{
"id": "com.android.settings.capture-overview",
"applicationId": "com.android.settings",
"intent": "capture-overview",
"summary": "Capture a Settings overview snapshot",
"path": "skills/com.android.settings.capture-overview",
"skillFile": "skills/com.android.settings.capture-overview/SKILL.md",
"scripts": [
"skills/com.android.settings.capture-overview/scripts/run.js"
],
"artifacts": []
}
],
"count": 1
}
skills search
Search filters are exact for --app and --intent.
--keyword matching is case-insensitive and deterministic. Current ranking order is:
- exact
keywords[]entry match - tokenized
keywords[]match - exact
idorapplicationIdmatch - tokenized
idorapplicationIdmatch - substring in
keywords[] - substring in
idorapplicationId - substring in
summary
Equal-ranked results keep their original registry order.
skills search success uses the same response shape as skills list:
{
"skills": [
{
"id": "com.android.settings.capture-overview",
"applicationId": "com.android.settings",
"intent": "capture-overview",
"summary": "Capture a Settings overview snapshot",
"path": "skills/com.android.settings.capture-overview",
"skillFile": "skills/com.android.settings.capture-overview/SKILL.md",
"scripts": [
"skills/com.android.settings.capture-overview/scripts/run.js"
],
"artifacts": []
}
],
"count": 1
}
skills for-app
cmdSkillsForApp() is a thin discovery alias over skills search --app <package_id>.
Use skills for-app when you already know the Android package id and want the shortest answer to "what can this host do for this app?" Use skills search --keyword when you only have user-language terms such as app names or intents.
Example:
clawperator skills for-app com.android.settings --json
It returns the same response shape as skills search --app ... --json.
skills get
cmdSkillsGet() wraps a single registry entry under skill:
clawperator skills get com.android.settings.capture-overview --json
{
"skill": {
"id": "com.android.settings.capture-overview",
"applicationId": "com.android.settings",
"intent": "capture-overview",
"summary": "Capture a Settings overview snapshot",
"path": "skills/com.android.settings.capture-overview",
"skillFile": "skills/com.android.settings.capture-overview/SKILL.md",
"scripts": [
"skills/com.android.settings.capture-overview/scripts/run.js"
],
"artifacts": []
}
}
Discovery Verification
Use these commands to verify the three core discovery paths:
clawperator skills list --json
clawperator skills for-app com.android.settings --json
clawperator skills search --app com.android.settings --json
clawperator skills get com.android.settings.capture-overview --json
Check these exact fields:
countmatches the number of returned entries forlistandsearch- every result entry includes
id,applicationId,intent,path,skillFile,scripts, andartifacts skills getreturns a top-levelskillobject, not askillsarray
Discovery Error Cases
Top-level usage and lookup failures are exact:
{
"code": "USAGE",
"message": "skills get <skill_id>"
}
{
"code": "USAGE",
"message": "skills search requires --app <package_id>, --intent <intent>, or --keyword <text>",
"example": "clawperator skills search --keyword solax"
}
{
"code": "SKILL_NOT_FOUND",
"message": "Skill not found: com.android.settings.capture-overview"
}
SKILL_NOT_FOUND comes from getSkill() in apps/node/src/domain/skills/getSkill.ts. REGISTRY_READ_FAILED comes from the shared registry loader and can appear on list, search, and get.
Execution
Current execution command:
clawperator skills run <skill_id> [--device <id>] [--operator-package <pkg>] [--timeout <ms>] [--timeout-ms <ms>] [--expect-contains <text>] [--skip-validate] [--json] [skill_args...]
What the wrapper does:
- resolves
CLAWPERATOR_BINfor the child process - resolves
CLAWPERATOR_OPERATOR_PACKAGEfor the child process - validates the skill with
validateSkill(skillId, undefined, { dryRun: true })unless--skip-validateis passed - loads the registry entry
- chooses a script, preferring
.js, then.sh, then the first listed script - spawns the script
- captures raw stdout and stderr
- enforces a timeout
Argument passing rules:
- if
--devicewas provided, the wrapper prepends that device id as the first script argument - unknown trailing tokens such as
--limit 40are forwarded to the script unchanged - use
--when you need to force literal passthrough for tokens that would otherwise be parsed as wrapper flags CLAWPERATOR_BINandCLAWPERATOR_OPERATOR_PACKAGEare injected into the script environment- in JSON mode, the wrapper suppresses the pretty banner so stdout stays parseable JSON
Default execution values are exact:
DEFAULT_TIMEOUT_MS = 120000DEFAULT_OPERATOR_PACKAGE = com.clawperator.operatorCLAWPERATOR_BINresolution order is:- non-empty
CLAWPERATOR_BIN - branch-local sibling build at
apps/node/dist/cli/index.js - global
clawperatorbinary
skills run Verification
Use a JSON run first so you can verify the wrapper envelope separately from the skill's own stdout contract:
clawperator skills run com.android.settings.capture-overview --timeout 3210 --json
Success response:
{
"skillId": "com.android.settings.capture-overview",
"output": "RESULT|status=success|snapshot=/tmp/settings.xml\n",
"exitCode": 0,
"durationMs": 15842,
"timeoutMs": 3210
}
Check these exact fields:
skillIdmatches the requested registry idoutputis the raw stdout stream captured from the skill scriptskillResultis either a parsed structured result ornullfor a legacy skillexitCodeis0on successdurationMsis the measured wrapper runtimetimeoutMsis present only when a timeout override was passed on the CLI
To verify wrapper-side output assertions, run:
clawperator skills run com.android.settings.capture-overview --expect-contains RESULT --json
If the expected text is present, the success payload echoes the assertion:
{
"skillId": "com.android.settings.capture-overview",
"output": "RESULT|status=success|snapshot=/tmp/settings.xml\n",
"exitCode": 0,
"durationMs": 15842,
"expectedSubstring": "RESULT"
}
skills run Success Shape
{
"status": "success",
"skillId": "com.android.settings.capture-overview",
"output": "RESULT|status=success|snapshot=/tmp/settings.xml\n",
"skillResult": null,
"exitCode": 0,
"durationMs": 15842
}
Important:
outputis raw stdout from the script- the wrapper parses a trailing framed
SkillResultwhen present and returns it asskillResult - the top-level wrapper
statusissuccess,failed, orindeterminate - when a declared verification contract is not proved, the wrapper returns
status: "indeterminate"without rewriting the emittedskillResult - progress lines written by the skill to stdout remain inside
outputin JSON mode - pretty mode writes a banner before streaming live skill output, so use
--jsonwhen another agent needs machine-readable output - in JSON mode, the wrapper returns one JSON object and does not stream live child stdout separately
timeoutMsis present only when the caller passed--timeoutor--timeout-msexpectedSubstringis present only when the caller passed--expect-contains
skills run Indeterminate Shape
{
"status": "indeterminate",
"code": "SKILL_VERIFICATION_INDETERMINATE",
"message": "Declared verification was not proved.",
"skillId": "com.android.settings.capture-overview",
"output": "[Clawperator-Skill-Result]\n{\"status\":\"success\"}\n",
"skillResult": {
"status": "success"
},
"exitCode": 0,
"durationMs": 15842
}
skills run Failure Shape
{
"status": "failed",
"code": "SKILL_EXECUTION_FAILED",
"message": "Skill com.android.settings.capture-overview exited with code 2",
"skillId": "com.android.settings.capture-overview",
"exitCode": 2,
"stdout": "RESULT|status=partial|snapshot=/tmp/settings.xml\n",
"stderr": "Timed out waiting for expected node\n",
"skillResult": null
}
Other wrapper failures are distinct and worth handling separately:
{
"code": "SKILL_EXECUTION_TIMEOUT",
"message": "Skill com.android.settings.capture-overview timed out after 150ms",
"skillId": "com.android.settings.capture-overview",
"stdout": "{\"stage\":\"before-timeout\"}\n"
}
{
"code": "SKILL_OUTPUT_ASSERTION_FAILED",
"message": "Skill com.android.settings.capture-overview output did not include expected text",
"skillId": "com.android.settings.capture-overview",
"output": "RESULT|status=success|snapshot=/tmp/settings.xml\n",
"skillResult": null,
"expectedSubstring": "missing-value"
}
{
"code": "SKILL_RESULT_PARSE_FAILED",
"message": "SkillResult frame contained invalid JSON: ...",
"skillId": "com.android.settings.capture-overview",
"stdout": "[Clawperator-Skill-Result]\n{not-json\n",
"skillResult": null
}
{
"code": "SKILL_SCRIPT_NOT_FOUND",
"message": "Script not found: /abs/path/to/skills/com.android.settings.capture-overview/scripts/run.js",
"skillId": "com.android.settings.capture-overview"
}
skills run Error Cases
There are three layers of failure to expect:
- CLI usage and option parsing
- validation gate failure before the skill starts
- wrapper execution failure after the child process is spawned
Exact CLI parsing failures include:
{
"code": "USAGE",
"message": "--timeout-ms requires a value"
}
{
"code": "USAGE",
"message": "--expect-contains requires a value"
}
{
"code": "EXECUTION_VALIDATION_FAILED",
"message": "timeoutMs must be a finite number"
}
The pre-run validation gate can stop the command before script execution. cmdSkillsRun() calls validateSkill(..., { dryRun: true }) unless --skip-validate is present. That gate fails with SKILL_VALIDATION_FAILED when required files or artifact payloads are wrong:
{
"code": "SKILL_VALIDATION_FAILED",
"message": "Skill com.android.settings.capture-overview is missing required files",
"details": {
"skillJsonPath": "/abs/path/to/skills/com.android.settings.capture-overview/skill.json",
"missingFiles": [
"/abs/path/to/skills/com.android.settings.capture-overview/scripts/run.js"
]
}
}
For script-only skills, dry-run payload validation is skipped on purpose. The success payload from skills validate --dry-run includes:
{
"valid": true,
"dryRun": {
"payloadValidation": "skipped",
"reason": "skill has no pre-compiled artifacts; payload is generated at runtime by the skill script"
}
}
Recovery depends on the error code:
REGISTRY_READ_FAILED: fixCLAWPERATOR_SKILLS_REGISTRY, run from the correct working directory, or reinstall the skills repoSKILL_NOT_FOUND: confirm the exactidwithskills list --jsonorskills search --jsonSKILL_VALIDATION_FAILED: repair missing files, mismatchedskill.jsonmetadata, or invalid artifact payloads before rerunningSKILL_SCRIPT_NOT_FOUND: fix the registry entry or restore the script on diskSKILL_EXECUTION_FAILED: inspectstdout,stderr, and the skill script's exit codeSKILL_EXECUTION_TIMEOUT: raise--timeoutonly after confirming the skill is still making progressSKILL_OUTPUT_ASSERTION_FAILED: verify the expected substring against the rawoutputSKILL_RESULT_PARSE_FAILED: fix malformed framed output or trusted-provenance metadata before rerunning
Serve API Context
The local HTTP server exposes:
GET /skillsGET /skills/:skillIdPOST /skills/:skillId/run
The serve wrapper uses the same underlying registry and runSkill() runtime, with route-local request validation layered on top.
Important boundary:
- the serve route calls
runSkill()directly - it does not go through the full CLI
cmdSkillsRun()path, so it does not include the CLI pre-run validation gate or the CLI banner behavior
Error Codes
The skills contract defines its own stable codes in apps/node/src/contracts/skills.ts.
Common ones:
| Code | Meaning |
|---|---|
REGISTRY_READ_FAILED |
registry file missing, unreadable, or invalid |
SKILL_NOT_FOUND |
requested id is not in the registry |
SKILL_VALIDATION_FAILED |
pre-run validation found missing files, mismatched metadata, or invalid artifacts |
SKILL_SCRIPT_NOT_FOUND |
registry exists but the chosen script file does not |
SKILL_EXECUTION_FAILED |
subprocess exited non-zero or failed to spawn |
SKILL_EXECUTION_TIMEOUT |
wrapper timeout elapsed |
SKILL_AGENT_CLI_UNAVAILABLE |
orchestrated skill declared agent.cli but the configured agent CLI could not be resolved |
SKILL_OUTPUT_ASSERTION_FAILED |
--expect-contains was set and the output did not contain the text |
SKILL_RESULT_PARSE_FAILED |
a framed SkillResult was malformed or could not be trusted |
Practical Model
- use
skills list,skills search, andskills getto discover what is available - use
skills validate --dry-runwhen you want to confirm registry integrity before a live run - use
skills runwhen you want the wrapper's validation gate, timeout, env injection, and JSON envelope - use skill output as deterministic wrapper output, not as autonomous reasoning