Skills are markdown instruction files that teach the agent how and when to use
tools. Each skill lives in a directory containing a SKILL.md file with YAML
frontmatter and a markdown body. Apus loads bundled skills plus any local
overrides, and filters them at load time based on environment, config, and
binary presence.
Creating skills
Build and test a custom skill from scratch.
Skill Workshop
Skills your agent learns on its own, with change history and undo.
Skills config
Full `skills.*` config schema and agent allowlists.
ClawHub
Browse and install community skills.
Loading order
File-backed skills load from these sources, highest precedence first. When the same skill name appears in multiple places, the highest source wins. Personal library skills are selected by identity and revision rather than discovered by scanning every user's files.
| Priority | Source | Path |
|---|---|---|
| 1 — highest | Workspace skills | <workspace>/skills |
| 2 | Project agent skills | <workspace>/.agents/skills |
| 3 | Personal agent skills | ~/.agents/skills (default state only) |
| 4 | Managed / local skills | <state-dir>/skills |
| 5 | Workshop skills | <state-dir>/agents/<agentId>/agent/workshop-skills |
| 6 | Bundled skills | shipped with the install |
| 6 | Custodian skills | shipped; configured Custodian agent only |
| 7 — lowest | Extra directories | skills.load.extraDirs + plugin skills |
When a session uses a different execution workspace, Apus also loads that
workspace's skills/ and .agents/skills/ directories. These skills follow the
entire agent catalog in precedence and prompt order; within the execution
workspace, skills/ wins over .agents/skills/. Both directories participate in
snapshot refresh and sandbox synchronization. Sandboxed runs read the
materialized copies, not the original host paths.
Managed worktree sessions keep their recorded canonical workspace as the skill
source. That source is read and watched on the Gateway, even when a File Transfer
plugin serves the agent workspace from a paired node. The node reads its configured
agent skill roots; it does not receive Gateway source paths. Selected skill files
and supporting resources are delivered from their owning host. Model-facing
workspace-hosted entries use workspace-skill:// read locations, so an identical
Gateway path cannot redirect the read to the node.
The configured agent workspace remains the primary skill source even when
the session executes in a worktree; only selecting that worktree as the agent's
workspace gives its skills primary precedence. A selected nested workspace stays
nested: discovery does not walk up to its parent repository. Installing Apus
from a repository does not make that
repository's .agents/skills/ a global bundled skill source.
Each discovery pass reports one summary per winning/losing discovery root and
source kind, with the skill count and up to three example names. Workspace or
project skills overriding bundled skills, and managed-worktree skills overriding
project checkout skills, are warnings; other collisions are informational.
Worktree provenance uses the configured worktreeRoot (the state directory's
worktrees/ by default), without checking Git. Identical content stays silent.
Unchanged root-pair summaries are not repeated on refresh; changes to content,
declared metadata, or collision membership update the summary. Precedence is unchanged.
Skill roots support grouped layouts. Apus discovers a skill whenever
SKILL.md appears anywhere under a configured root (up to 6 levels deep):
<workspace>/skills/research/SKILL.md ✓ found as "research"
<workspace>/skills/personal/research/SKILL.md ✓ also found as "research"
During grouped discovery, finding SKILL.md ends traversal below that directory.
Invalid skill files are reported and skipped; valid siblings can still load.
The folder path is for organization only. The skill's name and slash command
come from the name frontmatter field (or the directory name when name is
missing). Agent allowlists (below) also match on this name.
The release-versioned Custodian skill library shares the bundled precedence tier but is absent for every agent except the configured system/Custodian agent.
Codex CLI's native $CODEX_HOME/skills directory is not an Apus
skill root. Use apus migrate plan codex to inventory those skills, then
apus migrate codex to copy them into your Apus workspace.
Node-hosted skills
A connected headless node can publish skills installed in its active Apus
skills directory (~/.apus/skills by default; profile environment overrides
apply). They appear in the normal agent skill list while the node is connected
and disappear when it disconnects. A local or Gateway skill keeps its name on
collision; the node skill receives a deterministic node-prefixed name.
Node-hosted v1 requires the directory name to match the skill's name
frontmatter field. The published name, description, and instructions come from
the same captured file content.
The skill entry includes the node locator. Its files, relative references, and
binaries live on the node, so load and execute it with
exec host=node node=<node-id>. Restart the node host after changing its skill
files. See Nodes for pairing and off-switches.
Per-agent vs shared skills
In multi-agent setups, each agent has its own workspace. Use the path that matches your desired visibility:
| Scope | Path | Visible to |
|---|---|---|
| Per-agent | <workspace>/skills |
Only that agent |
| Project-agent | <workspace>/.agents/skills |
Only that workspace's agent |
| Personal-agent | ~/.agents/skills |
Agents using the default state |
| Shared managed | <state-dir>/skills |
All agents using that state |
| Workshop | <state-dir>/agents/<agentId>/agent/workshop-skills |
Only that agent |
| Extra dirs | skills.load.extraDirs |
All agents using that config |
Workshop skills learned by one agent are not shared with another agent. Publish a skill to the managed library when it must be available to multiple agents.
When APUS_STATE_DIR points somewhere other than the default
~/.apus, session skill indexes exclude home-scoped personal or
compatibility skill roots such as ~/.agents/skills. Workspace, project,
bundled, extra, and state-owned managed skills continue to load normally.
Personal skills on a shared Gateway
On a shared Gateway, identified operators can keep a personal skill library
without receiving permission to change everybody's workspace skills or Gateway
configuration. Open Plugins → Skills to create a skill, import a SKILL.md
or ZIP bundle, or add a skill from ClawHub. The editor keeps supporting scripts,
references, and assets with the instructions.
The ordinary single-admin setup stays unchanged: workspace authoring still
uses <workspace>/skills, and existing file-backed skills are not moved into
the library. A shared token does not identify a person. Personal library
operations require an authenticated Gateway profile.
The team-specific interface and agent guidance use distinct canonical Gateway
profiles, not channel senders, contacts, accounts, agents, devices, or browser
connections. Linked and merged login identities count as one profile.
Ownership and sharing
New managed skills belong to the authenticated creator. Share with team makes a skill available to other operators without giving them edit access. An administrator can transfer to team, changing management ownership while retaining the original author. Sharing and transfer do not move files or change the skill's stable ID. Profile merges retain existing revision paths.
Use the skill picker or returned command identity when invoking a managed skill. Different owners can use the same friendly name without one skill silently replacing another.
A shared Gateway remains one trust domain. Skills attached to a shared session are inputs to that session, not secret storage. Library ownership governs management and discovery; it does not grant new tools, credentials, host installation rights, or isolation from the Gateway operator. Keep credentials out of skill content.
Revisions and session selection
Saving publishes a complete immutable revision: SKILL.md and every supporting
file. The revision hash includes portable file paths, exact content, sizes, and
executable flags. Editing only a helper script still changes the revision;
sharing, ownership changes, ZIP timestamps, and archive entry order do not.
Identical saves are no-ops. A stale edit fails with a conflict instead of
overwriting a newer revision.
A session retains its selected skill IDs and revisions. Another person joining or taking ownership of the session does not replace that selection. Published changes are available to new sessions; explicitly attach or refresh a skill to use it on the next turn of an existing session. Rollback selects a retained revision. Removing a skill from the library excludes it from new selections without deleting a revision already selected by a session. Disabling a skill removes it from new-session defaults; explicit attachment remains available.
A new session selects up to 64 enabled library skills, with personal skills first and stable ID ordering within each group. If the library exceeds that limit, the Skills page explains how to detach a selected skill and attach another. Enablement does not bypass agent allowlists, required binaries, operating-system restrictions, or other prerequisites.
A managed bundle is limited to 256 files, 1 MiB per file, and 8 MiB total. Worker resource delivery also has an 8 MiB aggregate limit; narrow the session selection if its complete bundles exceed that limit. Published revisions are retained, including revisions still selected by older sessions.
ZIP imports allow up to 16 unfinished uploads per canonical profile and 32 across the Gateway. Linked or merged identities share the profile limit. Completed imports do not count toward either limit, and uploads expire one hour after they begin. If a profile merge or upgrade leaves more uploads than the limit allows, existing unexpired uploads can still be completed. Finish existing uploads or wait for them to expire before starting more.
The Gateway stores library records and revision metadata in
state/apus.sqlite, and immutable bundles under
skill-library/<skill-id>/revisions/<revision-hash>/ inside its state directory.
Do not edit those managed directories directly. Use the editor, the
Skills library CLI, or the agent's
authorized authoring tool. Runtime copies are separate from project files and
must not be committed with a project.
Agent allowlists
Skill location (precedence) and skill visibility (which agent can use it) are separate controls. Use allowlists to restrict which skills an agent sees, regardless of where they are loaded from.
{
agents: {
ownership: "explicit",
defaults: {
skills: ["github", "weather"], // shared baseline
heartbeat: { agentId: "writer" },
systemAgent: { agentId: "writer" },
authInheritance: { agentId: "writer" },
},
entries: {
writer: { workspace: "~/.apus/workspace" }, // inherits github, weather
docs: { skills: ["docs-search"] }, // replaces defaults entirely
"locked-down": { skills: [] }, // no skills
},
},
talk: { agentId: "writer" },
}
Allowlist rules
- Omit `agents.defaults.skills` to leave all skills unrestricted by default.
- Omit `agents.entries.*.skills` to inherit `agents.defaults.skills`.
- Set `agents.entries.*.skills: []` to expose no skills for that agent.
- A non-empty `agents.entries.*.skills` list is the **final** set — it does not
merge with defaults.
- The effective allowlist applies across prompt building, slash-command
discovery, sandbox sync, and skill snapshots.
- This is not a host shell authorization boundary. If the same agent can
use `exec`, constrain that shell separately with sandboxing, OS-user
isolation, exec deny/allowlists, and per-resource credentials.
Plugins and skills
Plugins can ship their own skills by listing skills directories in
apus.plugin.json (paths relative to the plugin root). Plugin skills load
when the plugin is enabled — for example, the browser plugin ships a
browser-automation skill for multi-step browser control.
Plugin skill directories merge at the same low-precedence level as
skills.load.extraDirs, so a same-named bundled, managed, agent, or workspace
skill overrides them. Gate a plugin skill's own eligibility via
metadata.apus.requires in its frontmatter, same as any other skill.
For multi-account channel plugins, gate general messaging skills on the channel
subtree (for example, channels.discord), not a root token field: credentials
may live under a named account. This is a coarse skill-visibility check. The
plugin still owns credential resolution, account enablement, action availability,
and authorization; an eligible skill does not grant tool access.
See Plugins and Tools for the full plugin system.
Reference a skill in a prompt
Type $ in the Control UI composer to search the skills available to the
current agent. Selecting a result inserts its stable command name, for example
$release_notes, without replacing the rest of your message. A prompt can
reference more than one skill:
Use $github and $release_notes to summarize this change for the release.
Apus resolves explicit references from authorized senders on every channel
and on generic Gateway, CLI, and webhook agent turns. It matches the current
agent's eligible, user-invocable skills and tells the model to read each
referenced SKILL.md before acting. A single message can reference up to eight
distinct skills; Apus returns a visible error instead of ignoring extra or
allowlist-hidden references. The $ form is composable prompt text. On channel
messages, /release_notes ... remains the standalone command form and may use
direct tool dispatch when the skill declares command-dispatch: tool; generic
agent turns expand the same leading skill command as model instructions without
running the channel command dispatcher. Common uppercase shell variables such
as $HOME, $PATH, and $EDITOR remain ordinary text; use lowercase $home,
$path, or $editor to reference skills with those names. Escape a reference
as \$name when it should stay literal.
Skills with disable-model-invocation: true stay out of the $ picker and the
model's normal prompt, so the model cannot select them on its own. An authorized
explicit $skill-name reference still invokes them; the flag only hides the
skill from model-initiated selection.
Skill Workshop
Skill Workshop lets an agent save and update its own
skills in <state-dir>/agents/<agentId>/agent/workshop-skills. Every change
applies immediately, is announced, and saves the previous version so it can be
undone. Learned skills are always visible to their agent; archive one to hide
it. Operators edit skills outside that directory through their owning tools or
files.
apus skills workshop list
apus skills workshop changes
apus skills workshop archive <name>
apus skills workshop restore <name>
See Skill Workshop for how agents learn skills and
apus skills workshop for the CLI reference.
Installing from ClawHub
ClawHub is the public skills registry. Use
apus skills commands for install and update, or the clawhub CLI for
publish and sync.
| Action | Command |
|---|---|
| Install a skill into the workspace | apus skills install @owner/<slug> |
| Install an external skills.sh ref | apus skills install skills-sh:owner/repo/slug |
| Install from a Git repository | apus skills install git:owner/repo@ref |
| Install a local skill directory | apus skills install ./path/to/skill --as my-tool |
| Install for all local agents | apus skills install @owner/<slug> --global |
| Update all workspace skills | apus skills update --all |
| Update a shared managed skill | apus skills update @owner/<slug> --global |
| Update all shared managed skills | apus skills update --all --global |
| Verify a skill's trust envelope | apus skills verify @owner/<slug> |
| Print the generated Skill Card | apus skills verify @owner/<slug> --card |
| Publish / sync via ClawHub CLI | clawhub sync --all |
Install details
`apus skills install` installs into the active workspace `skills/`
directory by default. Add `--global` to install into the shared
`~/.apus/skills` directory, visible to all local agents unless agent
allowlists narrow it.
Skill Workshop does not install into either location. Generated skills live
in the selected agent's `<state-dir>/agents/<agentId>/agent/workshop-skills`.
Git and local installs expect `SKILL.md` at the source root. The slug comes
from `SKILL.md` frontmatter `name` when valid, then falls back to the
directory or repository name. Use `--as <slug>` to override.
`apus skills update` tracks ClawHub installs only — reinstall Git or
local sources to refresh them.
ClawHub tracking uses `.clawhub/lock.json` in the workspace and
`.clawhub/origin.json` in each installed skill. The pre-July 2026
`.clawdhub` directory is no longer read. For older installs, rename those
metadata directories to `.clawhub` before updating or verifying skills;
preserve and reconcile any existing `.clawhub` metadata instead of overwriting it.
Verification and security scanning
`apus skills verify @owner/<slug>` asks ClawHub for the skill's
`clawhub.skill.verify.v1` trust envelope. Installed ClawHub skills verify
against the version and registry recorded in `.clawhub/origin.json`.
Bare slugs remain accepted for existing installed or unambiguous skills, but
owner-qualified refs avoid publisher ambiguity.
ClawHub skill pages expose the latest security scan state before install,
with detail pages for VirusTotal, ClawScan, and static analysis. The
command exits non-zero when ClawHub marks verification as failed. Publishers
recover false positives through the ClawHub dashboard or
`clawhub skill rescan @owner/<slug>`.
Private archive installs
Gateway clients that need non-ClawHub delivery can stage a zip skill archive
with `skills.upload.begin`, `skills.upload.chunk`, and `skills.upload.commit`,
then install with `skills.install({ source: "upload", ... })`. This path is
off by default and requires `skills.install.allowUploadedArchives: true` in
`apus.json`. Normal ClawHub installs never need that setting.
Security
Treat third-party skills as untrusted code. Read them before enabling. Prefer sandboxed runs for untrusted inputs and risky tools. See Sandboxing for agent-side controls.
Path containment
Workspace, project-agent, and extra-dir skill discovery only accepts skill
roots whose resolved realpath stays inside the configured root, unless
`skills.load.allowSymlinkTargets` explicitly trusts a target root.
Skill Workshop rejects symlinked skills that resolve outside
`<state-dir>/agents/<agentId>/agent/workshop-skills`.
Managed `~/.apus/skills` and personal `~/.agents/skills` may contain
symlinked skill folders, but every `SKILL.md` realpath must still stay
inside its resolved skill directory.
Optional `skill-card.md` files must be regular files inside that directory
and no larger than 256 KiB, including when they grow during a read.
Operator install policy
Configure `security.installPolicy` to run a trusted local policy command
before skill installs continue. The policy receives metadata and the staged
source path, applies to ClawHub, uploaded, Git, local, update, and
dependency-installer paths, and fails closed when the command cannot return
a valid decision.
Secret injection scope
`skills.entries.*.env` and `skills.entries.*.apiKey` inject secrets into the
**host** process for that agent turn only — not into the sandbox. Keep
secrets out of prompts and logs.
For the broader threat model and security checklists, see Security.
SKILL.md format
Every skill needs at minimum a name and description in the frontmatter:
---
name: image-lab
description: Generate or edit images via a provider-backed image workflow
---
When the user asks to generate an image, use the `image_generate` tool...
Apus follows the AgentSkills spec. Frontmatter
is parsed as YAML first; if that fails, it falls back to a single-line-only
parser. Nested metadata blocks (including multi-line YAML mappings) are
flattened to a JSON string and re-parsed as JSON5, so the block form shown
under Gating works. Use {baseDir} in the body to reference the
skill folder path.
Optional frontmatter keys
homepage string
URL shown as "Website" in the macOS Skills UI. Also supported via
metadata.apus.homepage.
user-invocable boolean
When true, the skill is exposed as a user-invocable slash command.
disable-model-invocation boolean
When true, Apus keeps the skill's instructions out of the agent's normal
prompt. The skill is still available as a slash command when user-invocable
is also true.
command-dispatch "tool"
When set to tool, the slash command bypasses the model and dispatches
directly to a registered tool.
command-tool string
Tool name to invoke when command-dispatch: tool is set.
command-arg-mode "raw"
For tool dispatch, forwards the raw args string to the tool with no
core parsing. The tool receives
{ command: "<raw args>", commandName: "<slash command>", skillName: "<skill name>" }.
Gating
Apus filters skills at load time using metadata.apus (JSON5 object
embedded in the frontmatter, see the parsing note above). A skill with no
metadata.apus block is always eligible unless explicitly disabled.
Skill inventory, skill readiness, and skill visibility are related but different:
- Inventory answers whether Apus discovered the skill in a configured root or bundled source.
- Readiness answers whether the current runtime can satisfy the skill's declared requirements, such as binaries, environment variables, config paths, operating system constraints, or reachable node-hosted capabilities.
- Visibility answers whether a ready, eligible skill is exposed to the selected agent after agent allowlists, invocation flags, and session snapshot rules are applied.
A skill can be present in inventory but still not ready or visible. Use
apus skills check when debugging a skill that
appears in configuration but does not show up for an agent, or when its required
tool, credential, or host capability is missing.
---
name: image-lab
description: Generate or edit images via a provider-backed image workflow
metadata:
{
"apus":
{
"requires": { "bins": ["uv"], "env": ["GEMINI_API_KEY"], "config": ["browser.enabled"] },
"primaryEnv": "GEMINI_API_KEY",
},
}
---
always boolean
When true, include the skill whenever its os requirement is compatible,
bypassing requires.bins, requires.anyBins, requires.env, and
requires.config.
emoji string
Optional emoji shown in the macOS Skills UI.
homepage string
Optional URL shown as "Website" in the macOS Skills UI.
os ("darwin" | "macos" | "linux" | "win32")[]
Hard platform filter. When set, the skill is only eligible when the local
host or a connected remote runtime matches a listed OS. always does not
override this filter. macos is accepted as an alias for darwin.
requires.bins string[]
Each binary must exist on PATH.
requires.anyBins string[]
At least one binary must exist on PATH.
Fresh dependency checks detect binaries installed into directories already on
PATH. This does not refresh an existing session's skill snapshot; see
Snapshots and refresh.
requires.env string[]
Each env var must exist in the process or be provided via config.
requires.config string[]
Each apus.json path must be truthy.
primaryEnv string
Env var name associated with skills.entries.<name>.apiKey.
install object[]
Optional installer specs used by the macOS Skills UI (brew / node / go / uv / download).
The pre-July 2026 metadata.clawdbot format is no longer read. To update an
older skill, edit its SKILL.md frontmatter and rename that block to
metadata.apus, preserving its requirements and installer fields. If
both blocks exist, keep the current block and merge only the legacy fields
you still want. Apus does not rewrite the file; the old block's dependency
gates and installer hints are ignored until you update it.
Installer specs
Installer specs tell the macOS Skills UI how to install a dependency:
---
name: gemini
description: Use Gemini CLI for coding assistance and Google search lookups.
metadata:
{
"apus":
{
"emoji": "♊️",
"requires": { "bins": ["gemini"] },
"install":
[
{
"id": "brew",
"kind": "brew",
"formula": "gemini-cli",
"bins": ["gemini"],
"label": "Install Gemini CLI (brew)",
},
],
},
}
---
Installer selection rules
- When multiple installers are listed, the gateway picks one preferred
option (brew when available, otherwise node).
- If all installers are `download`, Apus lists each entry so you can
see all available artifacts.
- Specs can include `os: ["darwin"|"macos"|"linux"|"win32"]` to filter by platform.
- Node installs honor `skills.install.nodeManager` in `apus.json`
(default: npm; options: npm / pnpm / yarn / bun). This only affects skill
installs; the Gateway runtime should still be Node.
- Gateway installer preference: Homebrew → uv → configured node manager →
go → download.
Per-installer details
- **Homebrew:** Apus does not auto-install Homebrew or translate brew
formulas into system package commands. In Linux containers without
`brew`, brew-only installers are hidden; use a custom image or install
the dependency manually.
- **Go:** Apus requires Go 1.21 or newer for automatic skill installs.
If `go` is missing and Homebrew is available, Apus installs Go via
Homebrew first; on Linux without Homebrew it can instead use `apt-get`
as root or through passwordless `sudo` when the refreshed `golang-go`
candidate meets the minimum version. The actual `go install` for the
dependency always targets a dedicated Apus-managed bin directory
(Homebrew's `bin` on a fresh install, else `~/.local/bin`) rather than
your configured `GOBIN` — your own `GOBIN`, `GOPATH`, and `GOTOOLCHAIN`
env vars are read but never overwritten.
- **Download:** `url` (required), `sha256` (optional 64-character hexadecimal
digest, verified after download and before the file is installed or extracted),
`archive` (`tar.gz` | `tar.bz2` | `zip`), `extract` (default: auto when
the recipe or URL identifies an archive), `stripComponents`, `targetDir` (default:
`~/.apus/tools/<skillKey>`). Existing specs without `sha256` keep the
previous download behavior. Response bodies are capped at 256 MiB; larger
transfers are aborted while streaming, and partial staging data is removed.
Archive extraction does not require a system `tar` command.
With `extract: true`, a valid `Content-Disposition` filename can identify
an archive served from an extensionless URL. Invalid or empty response
filenames fall back to the URL; response headers do not enable extraction
for recipes that omit `extract`.
Sandboxing notes
`requires.bins` is checked on the **host** at skill load time. If an agent
runs in a sandbox, the binary must also exist **inside the container**.
Install it via `agents.defaults.sandbox.docker.setupCommand` or a custom
image. `setupCommand` runs once after container creation and requires
network egress, a writable root FS, and a root user in the sandbox.
Config overrides
Toggle and configure bundled or managed skills under skills.entries in
~/.apus/apus.json:
{
skills: {
entries: {
"image-lab": {
enabled: true,
apiKey: { source: "env", provider: "default", id: "GEMINI_API_KEY" },
env: { GEMINI_API_KEY: "GEMINI_KEY_HERE" },
config: {
endpoint: "https://example.invalid",
model: "nano-pro",
},
},
peekaboo: { enabled: true },
sag: { enabled: false },
},
},
}
enabled boolean
false disables the skill even when bundled or installed. The coding-agent
bundled skill is opt-in — set skills.entries.coding-agent.enabled: true
and ensure one of claude, codex, opencode, or another supported CLI
is installed and authenticated.
apiKey string | { source, provider, id }
Convenience field for skills that declare metadata.apus.primaryEnv.
Supports a plaintext string or a SecretRef object.