Claude Code has a sophisticated skill and plugin system that allows extending its capabilities through:
- Skills: Markdown-based instructions that define reusable workflows (stored in
SKILL.mdfiles) - Plugins: Full-featured extensions that can provide skills, hooks, MCP servers, and LSP servers
- Bundled Skills: Built-in skills that ship with Claude Code
- Dynamic Skills: Skills discovered from
.claude/skills/directories during file operations
| Component | File Path | Purpose |
|---|---|---|
SkillLoader |
src/skills/loadSkillsDir.ts |
Loads skills from disk directories |
BundledSkillRegistry |
src/skills/bundledSkills.ts |
Registers built-in skills |
SkillTool |
src/tools/SkillTool/SkillTool.ts |
Tool for model to invoke skills |
Command Registry |
src/commands.ts |
Aggregates all command sources |
PluginLoader |
src/utils/plugins/loadPluginCommands.ts |
Loads plugin-provided skills |
Skills are loaded from multiple sources in this order:
- Bundled Skills (
src/skills/bundled/) - Built into the CLI - Built-in Plugin Skills (
src/plugins/bundled/) - User-toggleable built-in features - Skill Directory Skills (
~/.claude/skills/,.claude/skills/) - User and project skills - Dynamic Skills (discovered during file operations) - Nested project skills
- Workflow Commands (if WORKFLOW_SCRIPTS enabled) - Workflow-backed skills
- Plugin Commands - From installed plugins
- Built-in Commands - Core CLI commands
Entry Point: src/commands.ts:449-469 (loadAllCommands)
const loadAllCommands = memoize(async (cwd: string): Promise<Command[]> => {
const [
{ skillDirCommands, pluginSkills, bundledSkills, builtinPluginSkills },
pluginCommands,
workflowCommands,
] = await Promise.all([
getSkills(cwd),
getPluginCommands(),
getWorkflowCommands ? getWorkflowCommands(cwd) : Promise.resolve([]),
])
return [
...bundledSkills,
...builtinPluginSkills,
...skillDirCommands,
...workflowCommands,
...pluginCommands,
...pluginSkills,
...COMMANDS(), // Built-in commands
]
})File: src/skills/loadSkillsDir.ts
Skills are loaded from these locations (lines 638-714):
| Source | Path | Condition |
|---|---|---|
| Managed (Policy) | managed/.claude/skills/ |
Unless CLAUDE_CODE_DISABLE_POLICY_SKILLS |
| User | ~/.claude/skills/ |
If userSettings enabled and not plugin-only |
| Project | .claude/skills/ (up to home) |
If projectSettings enabled and not plugin-only |
| Additional | --add-dir paths |
Always if provided |
| Legacy Commands | .claude/commands/ |
If not skillsLocked |
File: src/skills/loadSkillsDir.ts:861-915
When files are accessed, Claude Code discovers nested skills:
export async function discoverSkillDirsForPaths(
filePaths: string[],
cwd: string,
): Promise<string[]> {
// Walks up from file paths to find .claude/skills/ directories
// below cwd (excluding cwd itself, which is loaded at startup)
}Skills use a directory-based structure:
.claude/skills/
├── skill-name/
│ └── SKILL.md # Required - skill definition
├── another-skill/
│ └── SKILL.md
└── ...
From src/skills/loadSkillsDir.ts:405-480:
- Only directory format is supported in
/skills/directories:skill-name/SKILL.md - Single
.mdfiles are NOT supported in/skills/directories - Legacy
/commands/directories support both formats
Skills can specify paths frontmatter to only activate when matching files are touched:
---
name: my-skill
paths: src/**/*.tsx # Only activates when .tsx files are accessed
---Implementation at src/skills/loadSkillsDir.ts:997-1058 (activateConditionalSkillsForPaths)
File: src/skills/bundled/index.ts
All bundled skills are initialized at startup:
export function initBundledSkills(): void {
registerUpdateConfigSkill()
registerKeybindingsSkill()
registerVerifySkill()
registerDebugSkill()
registerLoremIpsumSkill()
registerSkillifySkill()
registerRememberSkill()
registerSimplifySkill()
registerBatchSkill()
registerStuckSkill()
// Feature-gated skills...
if (feature('RUN_SKILL_GENERATOR')) {
const { registerRunSkillGeneratorSkill } = require('./runSkillGenerator.js')
registerRunSkillGeneratorSkill()
}
}File: src/skills/bundledSkills.ts:15-41
export type BundledSkillDefinition = {
name: string
description: string
aliases?: string[]
whenToUse?: string
argumentHint?: string
allowedTools?: string[]
model?: string
disableModelInvocation?: boolean
userInvocable?: boolean
isEnabled?: () => boolean
hooks?: HooksSettings
context?: 'inline' | 'fork'
agent?: string
files?: Record<string, string> // Additional reference files to extract
getPromptForCommand: (args: string, context: ToolUseContext) => Promise<ContentBlockParam[]>
}| Skill | File | Description | Feature Flag |
|---|---|---|---|
update-config |
updateConfig.ts |
Update Claude Code configuration | - |
keybindings |
keybindings.ts |
Manage keyboard shortcuts | - |
verify |
verify.ts |
Verify code changes by running app | - |
debug |
debug.ts |
Debug assistant | - |
lorem-ipsum |
loremIpsum.ts |
Generate placeholder text | - |
skillify |
skillify.ts |
Capture session as reusable skill | - |
remember |
remember.ts |
Review and organize auto-memory | - |
simplify |
simplify.ts |
Review code for reuse/quality | - |
batch |
batch.ts |
Batch process files | - |
stuck |
stuck.ts |
Help when stuck | - |
dream |
dream.ts |
Kairos dream mode | KAIROS |
hunter |
hunter.ts |
Review artifacts | REVIEW_ARTIFACT |
loop |
loop.ts |
Agent triggers | AGENT_TRIGGERS |
schedule-remote-agents |
scheduleRemoteAgents.ts |
Remote agent scheduling | AGENT_TRIGGERS_REMOTE |
claude-api |
claudeApi.ts |
Build Claude apps | BUILDING_CLAUDE_APPS |
run-skill-generator |
runSkillGenerator.js |
Generate skills from sessions | RUN_SKILL_GENERATOR |
File: src/skills/bundled/index.ts:73-78
if (feature('RUN_SKILL_GENERATOR')) {
const { registerRunSkillGeneratorSkill } = require('./runSkillGenerator.js')
registerRunSkillGeneratorSkill()
}File: src/skills/bundled/skillify.ts
The /skillify command captures the current session as a reusable skill:
- Analyzes session memory and user messages
- Interviews the user about the process (4 rounds of questions)
- Generates a
SKILL.mdwith proper frontmatter - Saves to either:
.claude/skills/<name>/SKILL.md(project-specific)~/.claude/skills/<name>/SKILL.md(personal)
MCP (Model Context Protocol) servers can be converted to skills automatically.
| File | Purpose |
|---|---|
src/services/mcp/client.ts |
MCP client implementation |
src/services/mcp/MCPConnectionManager.tsx |
Connection management |
src/skills/mcpSkillBuilders.ts |
Skill builder registry |
File: src/skills/mcpSkillBuilders.ts:26-44
export type MCPSkillBuilders = {
createSkillCommand: typeof createSkillCommand
parseSkillFrontmatterFields: typeof parseSkillFrontmatterFields
}
// Registration happens at loadSkillsDir.ts module init
registerMCPSkillBuilders({
createSkillCommand,
parseSkillFrontmatterFields,
})MCP skills are loaded from:
- Plugin-declared MCP servers with
mcpServersin manifest - User-configured MCP servers
- MCPB (MCP Bundle) files
File: src/utils/hooks/skillImprovement.ts
Automatically detects user preferences and corrections during skill execution, then suggests improvements to the skill definition.
- Hook Registration (lines 175-181):
export function initSkillImprovement(): void {
if (feature('SKILL_IMPROVEMENT') &&
getFeatureValue_CACHED_MAY_BE_STALE('tengu_copper_panda', false)) {
registerPostSamplingHook(createSkillImprovementHook())
}
}-
Detection (every 5 user messages):
- Analyzes recent conversation for preferences/corrections
- Generates
SkillUpdate[]with section, change, and reason
-
Application (lines 188-267):
- LLM rewrites the SKILL.md with improvements
- Preserves frontmatter exactly
- File written back to disk
File: src/commands.ts:86-90
const workflowsCmd = feature('WORKFLOW_SCRIPTS')
? (require('./commands/workflows/index.js') as typeof import('./commands/workflows/index.js')).default
: nullFile: src/tools.ts:129-132
const WorkflowTool = feature('WORKFLOW_SCRIPTS')
? (() => {
require('./tools/WorkflowTool/bundled/index.js').initBundledWorkflows()
return require('./tools/WorkflowTool/WorkflowTool.js').WorkflowTool
})()
: nullFile: src/commands.ts:401-405
const getWorkflowCommands = feature('WORKFLOW_SCRIPTS')
? (require('./tools/WorkflowTool/createWorkflowCommand.js') as typeof import('./tools/WorkflowTool/createWorkflowCommand.js')).getWorkflowCommands
: nullWorkflows appear in command listings with kind: 'workflow' (line 233 in src/types/command.ts), which affects how they're displayed in the UI (badged as "workflow" in autocomplete).
File: src/tools/SkillTool/SkillTool.ts:331-869
The Skill tool allows the model to invoke skills:
export const SkillTool: Tool<InputSchema, Output, Progress> = buildTool({
name: SKILL_TOOL_NAME, // "Skill"
searchHint: 'invoke a slash-command skill',
maxResultSizeChars: 100_000,
// ...
})Input (lines 291-298):
z.object({
skill: z.string().describe('The skill name'),
args: z.string().optional().describe('Optional arguments'),
})Output (lines 301-326):
| Mode | Output |
|---|---|
| Inline | { success, commandName, allowedTools?, model?, status: 'inline' } |
| Forked | { success, commandName, status: 'forked', agentId, result } |
- Inline (default): Skill content expands into the current conversation
- Forked: Skill runs as a sub-agent with separate context and token budget
Forked execution (lines 122-289):
async function executeForkedSkill(
command: Command & { type: 'prompt' },
commandName: string,
args: string | undefined,
context: ToolUseContext,
// ...
): Promise<ToolResult<Output>>For EXPERIMENTAL_SKILL_SEARCH feature (lines 604-613, 969-1107):
- Loads remote canonical skills via
_canonical_<slug>prefix - Caches skill content from AKI/GCS
- Injects SKILL.md as user message
Based on: src/skills/loadSkillsDir.ts:185-265 and src/utils/plugins/loadPluginCommands.ts
---
name: skill-name # Display name (optional)
description: One-line description # Required or auto-extracted
allowed-tools: # Tool permission patterns
- Read
- Write
- Bash(gh:*)
when_to_use: | # Auto-invocation trigger description
Use when the user wants to X.
Examples: "trigger phrase"
argument-hint: "[file] [options]" # Argument placeholder display
arguments: # Argument names for substitution
- file
- options
model: sonnet # Model override (optional)
context: fork # 'fork' for sub-agent, omit for inline
agent: general-purpose # Agent type for forked execution
user-invocable: true # Can users type /skill-name?
disable-model-invocation: false # Can model invoke via Skill tool?
effort: low # Effort level: low/medium/high
paths: # Conditional activation patterns
- src/**/*.tsx
hooks: # Post-sampling hooks
postInlineEdit: ...
shell: # Shell command configuration
timeout: 30000
cwd: /path/to/dir
---Standard skill content sections:
# Skill Title
Description of what this skill does.
## Inputs
- `$arg_name`: Description of this input
## Goal
Clearly stated goal with success criteria.
## Steps
### 1. Step Name
What to do in this step.
**Success criteria**: Specific criteria for completion.
**Execution**: Direct | Task agent | Teammate | [human]
**Artifacts**: Data produced for later steps.
**Human checkpoint**: When to ask user before proceeding.
**Rules**: Hard constraints for this step.File: src/skills/loadSkillsDir.ts:344-398
Available substitutions:
| Variable | Description |
|---|---|
$ARGUMENTS |
Full argument string |
$arg_name |
Individual argument values |
${CLAUDE_SKILL_DIR} |
Skill's directory path |
${CLAUDE_SESSION_ID} |
Current session ID |
!command |
Shell command execution (inline backticks) |
File: src/tools/SkillTool/SkillTool.ts:432-578
- Check deny rules first (can block specific skills)
- Auto-allow canonical remote skills (ant-only)
- Check allow rules
- Auto-allow skills with only "safe properties"
- Default: Ask user for permission
File: src/tools/SkillTool/SkillTool.ts:875-906
Properties that don't require permission:
const SAFE_SKILL_PROPERTIES = new Set([
'type', 'progressMessage', 'contentLength', 'argNames', 'model', 'effort',
'source', 'pluginInfo', 'skillRoot', 'context', 'agent',
'getPromptForCommand', 'name', 'description', 'aliases',
])File: src/skills/loadSkillsDir.ts:371-396
- Skills with
context: 'fork'run in isolated sub-agents - Shell commands (
!command) execute with skill-specific permissions - MCP skills never execute inline shell commands (security boundary)
Files:
src/plugins/builtinPlugins.ts- Built-in plugin registrysrc/plugins/bundled/index.ts- Built-in plugin initializationsrc/utils/plugins/schemas.ts- Manifest schemas
File: src/utils/plugins/schemas.ts:884-898
export const PluginManifestSchema = z.object({
// Metadata
name: z.string()
version: z.string().optional()
description: z.string().optional()
author: PluginAuthorSchema.optional()
homepage: z.string().optional()
repository: z.string().optional()
// Components
hooks: HooksConfigSchema.optional()
commands: z.union([CommandPathsSchema, z.record(CommandMetadataSchema)]).optional()
agents: AgentPathsSchema.optional()
skills: SkillPathsSchema.optional()
outputStyles: OutputStylePathsSchema.optional()
// MCP/LSP
mcpServers: McpServerConfigSchema.optional()
lspServers: LspServerConfigSchema.optional()
channels: ChannelConfigSchema.optional()
// User config
userConfig: z.record(UserConfigOptionSchema).optional()
settings: z.record(z.unknown()).optional()
})File: src/utils/plugins/schemas.ts:906-1044
Plugins can be sourced from:
| Source | Format | Description |
|---|---|---|
url |
URL | Direct URL to marketplace.json |
github |
owner/repo |
GitHub repository |
git |
URL | Git repository URL |
npm |
Package name | NPM package |
pip |
Package name | Python package |
file/directory |
Path | Local filesystem |
hostPattern/pathPattern |
Pattern | Pattern-based sources |
settings |
Object | Inline marketplace in settings.json |
File: src/utils/plugins/loadPluginCommands.ts:414-677
Plugin commands are loaded with:
- Namespace prefix:
plugin-name:command-name - Variable substitution for
${CLAUDE_PLUGIN_ROOT},${CLAUDE_PLUGIN_DATA} - User config substitution for
${user_config.KEY} - Shell command execution with proper context
File: src/utils/plugins/loadPluginCommands.ts:687-838
Plugin skills are loaded from:
- Default
skills/directory within plugin - Additional
skillsPathsfrom manifest
Skills are namespaced: plugin-name:skill-name
| Component | Path |
|---|---|
| Skill Loader | src/skills/loadSkillsDir.ts |
| Bundled Skills Registry | src/skills/bundledSkills.ts |
| Bundled Skills Index | src/skills/bundled/index.ts |
| Skill Tool | src/tools/SkillTool/SkillTool.ts |
| Skill Tool Prompt | src/tools/SkillTool/prompt.ts |
| Commands Registry | src/commands.ts |
| Command Types | src/types/command.ts |
| Plugin Types | src/types/plugin.ts |
| Plugin Manifest Schema | src/utils/plugins/schemas.ts |
| Plugin Loader | src/utils/plugins/loadPluginCommands.ts |
| Built-in Plugins | src/plugins/builtinPlugins.ts |
| Skill Improvement | src/utils/hooks/skillImprovement.ts |
| MCP Skill Builders | src/skills/mcpSkillBuilders.ts |
| Plugin Management UI | src/commands/plugin/ManagePlugins.tsx |