cc-haha/docs/en/skills/01-usage-guide.md
程序员阿江(Relakkes) 9ef170bb42 feat(skills): discover the cross-client .agents/skills convention #1093
Skills installed by Codex, Cursor, Gemini CLI, or opencode live under
`.agents/skills/` -- the convention documented at agentskills.io -- so users
had to keep a duplicate copy in `.claude/skills/` before cc-haha could see
them. The spec only constrains SKILL.md itself, which we already parse, so
this is purely a directory-discovery gap.

Scan both conventions at every scope: user (`~/.agents/skills`), project
(every level up to the repo root), `--add-dir` roots, on-demand monorepo
discovery, and the hot-reload watcher. skillRoots.ts is the single source of
truth for those paths; the REST API now reports which convention a skill came
from and the desktop list badges the `.agents` ones.

`.claude` is always ordered first, so first-wins dedup keeps a user's own
skills authoritative rather than letting an externally installed same-named
skill shadow them. Name collisions are collapsed per scope, which covers
copy-based sync -- symlink-based sync was already handled by the existing
realpath dedup. Collisions across different scopes, such as a user skill and
a project skill sharing a name, keep their existing behavior, and the shadowed
file's identity is recorded so overlapping roots (CLAUDE_CONFIG_DIR pointing
inside the project) cannot reintroduce it under the other scope.

getProjectDirsUpToHome is untouched -- it is shared by commands, agents,
output-styles, and workflows. Only its directory walk is extracted so the
skill resolver reuses the same submodule/worktree stop boundary. Writes stay
where they were: market installs and /skillify still target
`~/.claude/skills`.

`.agents` is anchored at $HOME rather than CLAUDE_CONFIG_DIR, since it is a
space shared with other tools that read $HOME. Note that Bun snapshots
os.homedir() at startup while Node re-reads $HOME per call, so the resolver
reads the env var explicitly.

Opt out with `disableAgentSkillsDirectory` in settings.json or
CLAUDE_CODE_DISABLE_AGENT_SKILLS_DIR=1; no `.agents` root is produced at all
then, so there is not even a failed stat.

(cherry picked from commit ba9338f85d6beaba237753e42e823c87b59633d4)
2026-07-25 02:50:21 +08:00

421 lines
14 KiB
Markdown

# Claude Code Skills System -- Usage Guide
> Skills are the extensible capability engine of Claude Code, allowing you to define custom automated workflows using Markdown files.
<p align="center">
<a href="#1-what-are-skills">What Are Skills</a> · <a href="#2-six-skill-sources">Six Sources</a> · <a href="#3-skill-definition-format">Definition Format</a> · <a href="#4-invocation-methods">Invocation</a> · <a href="#5-execution-context">Execution Context</a> · <a href="#6-conditional-activation">Conditional Activation</a> · <a href="#7-permission-control">Permissions</a> · <a href="#8-quick-reference">Quick Reference</a>
</p>
![Skills System Overview](./images/01-skills-overview.png)
---
## 1. What Are Skills?
Skills are Claude Code's **extensible capability plugin system**. Each Skill is a Markdown file (with YAML frontmatter) that defines a specialized prompt and behavioral configuration, enabling Claude to execute professional workflows in specific scenarios.
Core capabilities:
| Capability | Description |
|------------|-------------|
| Specialized Workflows | Define standard processes for code review, TDD, debugging, etc. |
| Tool Permission Control | Restrict a Skill to only use specified tools |
| Model Switching | Assign different models to different Skills |
| Execution Isolation | Fork mode runs in an isolated sub-agent |
| Conditional Activation | Activate only when specific files are being operated on |
| Hook Injection | Automatically register lifecycle hooks when a Skill is invoked |
---
## 2. Six Skill Sources
![Skill Source Types](./images/02-skill-sources.png)
Claude Code loads Skills from 6 different sources, ordered by priority from highest to lowest:
### 1. Bundled (Built-in Skills)
Compiled into the CLI binary and available to all users. Defined in TypeScript and registered via `registerBundledSkill()`.
**Current Built-in Skills:**
| Skill | Description | Special Conditions |
|-------|-------------|--------------------|
| `/verify` | Verify code changes | -- |
| `/debug` | Debugging assistant | -- |
| `/simplify` | Code simplification review | -- |
| `/remember` | Memory management | Requires auto-memory enabled |
| `/batch` | Batch processing | -- |
| `/stuck` | Help when stuck | -- |
| `/skillify` | Create a new Skill | -- |
| `/keybindings` | Custom keyboard shortcuts | -- |
| `/loop` | Timed loop tasks | AGENT_TRIGGERS feature gate |
| `/schedule` | Remote agent scheduling | AGENT_TRIGGERS_REMOTE feature gate |
| `/claude-api` | Claude API integration | BUILDING_CLAUDE_APPS feature gate |
| `/dream` | Automatic memory organization | KAIROS feature gate |
### 2. Managed (Policy-Managed Skills)
Controlled by organizational policies, stored in `<managed-path>/.claude/skills/`. Suitable for enterprise deployments.
### 3. User (User Skills)
Defined by individual users, stored in `~/.claude/skills/`. The cross-client open-standard
directory `~/.agents/skills/` is read as well.
```
~/.claude/skills/
├── my-review/
│ └── SKILL.md ← Main Skill file
├── deploy-check/
│ └── SKILL.md
└── ...
~/.agents/skills/ ← Open-standard directory, shared with Codex / Cursor / Gemini CLI
└── pdf-processing/
└── SKILL.md
```
### 4. Project (Project Skills)
Defined at the project level, stored in `.claude/skills/` or `.agents/skills/`. Can be
committed to version control.
```
your-project/
├── .claude/
│ └── skills/
│ ├── lint-fix/
│ │ └── SKILL.md
│ └── test-runner/
│ └── SKILL.md
└── .agents/
└── skills/ ← Shared with the team; other agent tools discover it too
└── deploy-app/
└── SKILL.md
```
### About `.agents/skills/` (the Agent Skills open standard)
`.agents/skills/` is the cross-client directory recommended by the
[agentskills.io](https://agentskills.io) specification, and is scanned by OpenAI Codex,
Cursor, Gemini CLI, and opencode among others. Skills placed there no longer need to be
copied into each tool's private directory.
- Both directories are active at once, and the `SKILL.md` format is identical — nothing to rewrite.
- If the same skill name appears in both at one level, `.claude/` wins and the `.agents/` entry
is skipped (a warn-level log records it). Names colliding across different levels — user vs.
project, say — still both survive, exactly as before.
- Sharing one skill through a symlink is recognized as a single skill, not loaded twice.
- To turn it off, set `"disableAgentSkillsDirectory": true` in `settings.json`, or set the
`CLAUDE_CODE_DISABLE_AGENT_SKILLS_DIR=1` environment variable. `.claude/skills/` is unaffected.
- Writes — Skills Market installs, `/skillify` — still go to `~/.claude/skills/`.
### 5. Plugin (Plugin Skills)
Provided by installed plugins. Plugins declare Skills directories via `skillsPath` / `skillsPaths` in their manifest.
Naming format: `{pluginName}:{skillName}`
```
Examples: superpowers:code-reviewer
superpowers:brainstorming
```
### 6. MCP (MCP Server Skills)
Provided by connected MCP servers, naming format: `mcp__server-name__prompt-name`.
**Security restriction**: MCP Skills are from remote untrusted sources and are **prohibited from executing** `!`...`` inline shell commands.
---
## 3. Skill Definition Format
### Directory Structure
Each Skill is a directory containing a `SKILL.md` file:
```
skill-name/
└── SKILL.md ← Filename must be SKILL.md (case-insensitive)
```
### Complete Frontmatter Fields
```yaml
---
name: My Skill # Display name (optional, defaults to directory name)
description: What this skill does # Description (required; auto-extracted from content if missing)
when_to_use: When to use this skill # Usage scenario description (optional)
version: 1.0.0 # Version number (optional)
# ── Invocation Control ──
user-invocable: true # Whether user can invoke via /skill-name (default: true)
disable-model-invocation: false # Prevent model from invoking via Skill tool (optional)
argument-hint: "<file path>" # Argument hint (optional)
# ── Execution Configuration ──
context: inline # Execution context: inline (default) or fork (sub-agent)
agent: general-purpose # Agent type when forked (optional)
model: sonnet # Model override: haiku / sonnet / opus / inherit (optional)
effort: high # Thinking effort: low / medium / high / max (optional)
allowed-tools: "Bash, Read" # Allowed tools (comma-separated or YAML list)
shell: bash # Shell type: bash (default) or powershell
# ── Conditional Activation ──
paths: "src/**/*.ts, test/**/*.ts" # Glob patterns; activate only when matching files are operated on
# ── Lifecycle Hooks ──
hooks:
PreToolUse:
- matcher: "Bash"
hooks:
- command: "echo 'Before bash'"
once: true # Execute only once
---
# Skill Body Content
This is the Markdown-formatted prompt that Claude sees when this Skill is invoked.
Supported special syntax:
- `${CLAUDE_SKILL_DIR}` — Expands to the Skill's directory
- `${CLAUDE_SESSION_ID}` — Expands to the current session ID
- `$ARGUMENTS` / `${ARG1}` — Argument substitution
- !`shell command` — Inline shell command execution
```
### Frontmatter Field Reference
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `name` | string | Directory name | Display name override |
| `description` | string | Auto-extracted | Brief Skill description |
| `when_to_use` | string | -- | Usage scenario description |
| `user-invocable` | boolean | true | Whether user can invoke via /name |
| `disable-model-invocation` | boolean | false | Prevent model invocation |
| `context` | `inline` \| `fork` | inline | Execution context |
| `agent` | string | general-purpose | Agent type when forked |
| `model` | string | Inherited | Model override (haiku/sonnet/opus) |
| `effort` | string \| int | -- | Thinking effort level |
| `allowed-tools` | string \| list | All | Allowed tools whitelist |
| `paths` | string \| list | -- | Conditional activation glob patterns |
| `shell` | `bash` \| `powershell` | bash | Shell command type |
| `hooks` | object | -- | Lifecycle hook configuration |
| `argument-hint` | string | -- | Argument hint text |
| `version` | string | -- | Version number |
---
## 4. Invocation Methods
![Skill Invocation Flow](./images/03-skill-invocation.png)
### Method 1: User Slash Commands
Type `/skill-name` directly in the terminal:
```
> /commit
> /review-pr 123
> /verify
```
**Prerequisite**: The Skill's `user-invocable` must be `true`.
### Method 2: Automatic Model Invocation
When Claude identifies a suitable Skill during conversation, it automatically invokes it via SkillTool:
```
User: Please review this code for me
Claude: [Invokes superpowers:code-reviewer via SkillTool]
```
**Prerequisite**: The Skill's `disable-model-invocation` must not be `true`.
### Method 3: Nested Invocation
One Skill can trigger another during execution:
```
/verify → internally invokes → /simplify
```
Tracked in telemetry via `invocation_trigger: 'nested-skill'`.
### Invocation Priority
When Skills with the same name exist in multiple sources, they are resolved in the following order (first match wins):
```
1. Bundled (built-in) ← Highest priority
2. Built-in Plugin
3. Skill Dirs (user/project directories)
4. Workflow Commands
5. Plugin Commands
6. Plugin Skills
7. Built-in Commands ← Lowest priority
```
---
## 5. Execution Context
### Inline Mode (Default)
Skill content is **expanded into the current conversation**. Claude directly sees the prompt and executes within the same context.
```yaml
context: inline # Default value, can be omitted
```
**Characteristics:**
- Shares the parent conversation's token budget
- Can access conversation history context
- `allowedTools` restricts available tools for the current turn
- `model` overrides the model used for the current turn
### Fork Mode (Sub-Agent)
The Skill runs in an **isolated sub-agent** with its own independent token budget and context.
```yaml
context: fork
agent: general-purpose # Optional, specifies agent type
```
**Characteristics:**
- Independent token budget; does not consume the parent conversation's quota
- Isolated conversation context
- Can specify a different agent type (e.g., `Bash`, `general-purpose`)
- Results are extracted and returned to the parent conversation upon completion
- Supports progress reporting (`onProgress` callback)
### Comparison of Both Modes
| Feature | Inline | Fork |
|---------|--------|------|
| Token Budget | Shared with parent | Independent budget |
| Context Access | Full conversation history | Skill prompt only |
| Result Return | Directly in conversation | Text extracted into tool_result |
| Use Cases | Brief guidance, extended context | Long tasks, independent computation |
| Tool Restrictions | contextModifier modification | modifiedGetAppState |
---
## 6. Conditional Activation
Skills can use the `paths` frontmatter to implement **on-demand activation**, becoming visible to the model only when matching files are operated on.
### Configuration
```yaml
---
name: TypeScript Fix
description: Fix TypeScript type errors
paths: "src/**/*.ts, test/**/*.ts"
---
```
### How It Works
```
1. All Skills are loaded at startup
2. Skills with paths are stored in the conditionalSkills Map (not exposed to the model)
3. When the user operates on a file (Read/Write/Edit)
4. activateConditionalSkillsForPaths() matches using the ignore library
5. On match → moved to the dynamicSkills Map → visible to model
6. Once activated, remains active for the entire session
```
### Dynamic Discovery
In addition to conditional activation, Skills also support **runtime discovery**:
```
1. User operates on a file in a deeply nested directory
2. discoverSkillDirsForPaths() traverses upward from the file path
3. Looks for .claude/skills/ and .agents/skills/ directories (not beyond cwd)
4. Skips directories ignored by .gitignore
5. New directory found → addSkillDirectories() → load and register
```
---
## 7. Permission Control
### Auto-Allow
If a Skill contains only "safe properties" (no `allowedTools`, no `hooks`, no `fork`), it is **automatically approved** for execution without user confirmation.
### Manual Confirmation
Skills with tool restrictions, hooks, or fork execution will prompt the user on first invocation:
```
Execute skill: my-custom-skill
Allow? (y)es / (n)o / (a)lways allow / (d)eny
```
### Permission Rules
| Rule Type | Format | Description |
|-----------|--------|-------------|
| Exact Allow | `Skill:commit` | Allow execution of the commit Skill |
| Prefix Allow | `Skill:review:*` | Allow all Skills with the review: prefix |
| Exact Deny | `Skill:dangerous` set to deny | Deny execution |
| Prefix Deny | `Skill:untrusted:*` set to deny | Deny all Skills with the untrusted: prefix |
**Processing order:** Deny rules → Allow rules → Safe property check → Ask user
---
## 8. Quick Reference
### Creating a Skill
```bash
# 1. Create directory
mkdir -p ~/.claude/skills/my-skill
# 2. Create SKILL.md
cat > ~/.claude/skills/my-skill/SKILL.md << 'EOF'
---
name: My Skill
description: An example Skill
user-invocable: true
---
# Skill Content
Hello, this is my custom Skill.
EOF
```
### Common Operations
| Operation | Method |
|-----------|--------|
| Create a Skill | `~/.claude/skills/<name>/SKILL.md` |
| Project-level Skill | `.claude/skills/<name>/SKILL.md` |
| Cross-tool shared Skill | `~/.agents/skills/<name>/SKILL.md` (visible to Codex / Cursor / Gemini CLI too) |
| Invoke a Skill | Type `/skill-name` in terminal |
| View available Skills | Type `/skills` in terminal |
| Create Skill with AI | `/skillify` |
| Restrict tools | Add `allowed-tools` in frontmatter |
| Fork execution | Add `context: fork` in frontmatter |
| Conditional activation | Add `paths: "src/**"` in frontmatter |
### Skill Availability Matrix
| Source | User Invocable | Model Invocable | Supports Fork | Supports Hooks |
|--------|---------------|-----------------|---------------|----------------|
| Bundled | Per definition | Per definition | Yes | Yes |
| Managed | Yes | Yes | Yes | Yes |
| User | Yes (default) | Yes | Yes | Yes |
| Project | Yes (default) | Yes | Yes | Yes |
| Plugin | Per config | Per config | Yes | Yes |
| MCP | Per config | Per config | Yes | No (security restriction) |